Initial Contribution

Signed-off-by: Vinay Vishal <vinay.vishal@oracle.com>
diff --git a/appserver/web/web-core/exclude.xml b/appserver/web/web-core/exclude.xml
new file mode 100644
index 0000000..a391572
--- /dev/null
+++ b/appserver/web/web-core/exclude.xml
@@ -0,0 +1,64 @@
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<FindBugsFilter>
+    <!--
+         Shing Wai Chan claims that whichever thread is woken up will do
+         the requested work, so there's no need to wake up more than one
+         thread.
+     -->
+    <Match>
+        <Class name="org.apache.catalina.core.StandardWrapper"/>
+        <Method name="deallocate"/>
+        <Bug pattern="NO_NOTIFY_NOT_NOTIFYALL"/>
+    </Match>
+
+    <!--
+        A "." at the beginning of a package name is never legal and the
+        code handles that case.
+    -->
+    <Match>
+        <Class name="org.apache.tomcat.util.modeler.Registry"/>
+        <Method name="findDescriptor"/>
+        <Bug pattern="RV_CHECK_FOR_POSITIVE_INDEXOF"/>
+    </Match>
+
+    <!--
+        These methods correspond to HttpServletResponse methods.
+    -->
+    <Match>
+        <Class name="org.apache.catalina.connector.Response"/>
+        <Or>
+            <Method name="encodeRedirectURL"/>
+            <Method name="encodeURL"/>
+        </Or>
+        <Bug pattern="NM_CONFUSING"/>
+    </Match>
+
+    <!--
+        These private Comparator classes are never used with TreeSets
+        or TreeMaps and so don't need to be Serializable.
+    -->
+    <Match>
+        <Or>
+            <Class name="org.apache.catalina.servlets.DefaultServlet$LastModifiedComparator"/>
+            <Class name="org.apache.catalina.servlets.DefaultServlet$SizeComparator"/>
+        </Or>
+        <Bug pattern="SE_COMPARATOR_SHOULD_BE_SERIALIZABLE"/>
+    </Match>
+</FindBugsFilter>
diff --git a/appserver/web/web-core/osgi.bundle b/appserver/web/web-core/osgi.bundle
new file mode 100644
index 0000000..95246b9
--- /dev/null
+++ b/appserver/web/web-core/osgi.bundle
@@ -0,0 +1,48 @@
+#
+# Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v. 2.0, which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# This Source Code may also be made available under the following Secondary
+# Licenses when the conditions for such availability set forth in the
+# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+# version 2 with the GNU Classpath Exception, which is available at
+# https://www.gnu.org/software/classpath/license.html.
+#
+# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+#
+
+-exportcontents: \
+                        com.sun.enterprise.web.connector.grizzly; \
+                        org.apache.catalina; \
+                        org.apache.catalina.authenticator; \
+                        org.apache.catalina.connector; \
+                        org.apache.catalina.core; \
+                        org.apache.catalina.deploy; \
+                        org.apache.catalina.loader; \
+                        org.apache.catalina.logger; \
+                        org.apache.catalina.mbeans; \
+                        org.apache.catalina.net; \
+                        org.apache.catalina.realm; \
+                        org.apache.catalina.security; \
+                        org.apache.catalina.servlets; \
+                        org.apache.catalina.session; \
+                        org.apache.catalina.ssi; \
+                        org.apache.catalina.startup; \
+                        org.apache.catalina.util; \
+                        org.apache.catalina.valves; \
+                        org.apache.tomcat.util; \
+                        org.apache.tomcat.util.modeler; \
+                        org.glassfish.web.valve; version=${project.osgi.version}
+
+# Normally Import-Package is not required, as the default value of *
+# is enough to find all static dependencies. But, when a module is
+# doing Class.forName, there is no foolproof way to find out the
+# desired package name, hence we need to add those packages explicitly.
+# In this case, org.apache.jk.server is one such package. It is used
+# when mod_jk configuration is used. Since, it is not always needed,
+# the import has been marked as optional. By Sahoo.
+Import-Package: org.apache.jk.core;org.apache.jk.server; resolution:=optional; password=GlassFish; version=${project.osgi.version}, *
+
diff --git a/appserver/web/web-core/pom.xml b/appserver/web/web-core/pom.xml
new file mode 100755
index 0000000..ed935a4
--- /dev/null
+++ b/appserver/web/web-core/pom.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <groupId>org.glassfish.main.web</groupId>
+    	<artifactId>web</artifactId>
+        <version>5.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>web-core</artifactId>
+    <packaging>glassfish-jar</packaging>
+    
+    <name>Core Servlet Container</name>
+
+    <developers>
+        <developer>
+            <id>shingwaichan</id>
+            <name>Shing Wai Chan</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>lead</role>
+                <role>developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>amyroh</id>
+            <name>Amy Roh</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>edburns</id>
+            <name>Ed Burns</name>
+            <url>http://ridingthecrest.com/blog/</url>
+            <organization>Oracle</organization>
+            <roles>
+                <role>developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>kchung</id>
+            <name>Kin-man Chung</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>developer</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <properties>
+        <findbugs.exclude>${project.basedir}/exclude.xml</findbugs.exclude>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.common</groupId>
+            <artifactId>glassfish-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.common</groupId>
+            <artifactId>internal-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.common</groupId>
+            <artifactId>common-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.web</groupId>
+            <artifactId>war-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.common</groupId>
+            <artifactId>container-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.web</groupId>
+            <artifactId>web-naming</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.annotations</groupId>
+            <artifactId>logging-annotation-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency> <!-- for FindBugs -->
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+   </dependencies>
+</project>
diff --git a/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/Constants.java b/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/Constants.java
new file mode 100644
index 0000000..634bb4f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/Constants.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.enterprise.web.connector.grizzly;
+
+/**
+ * Constants for the <code>com.sun.enterprise.web.connector.grizzly</code>
+ * package.
+ */
+
+public final class Constants {
+
+    public static final String Package = "com.sun.enterprise.web.connector.grizzly";
+}
diff --git a/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/CoyoteConnectorLauncher.java b/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/CoyoteConnectorLauncher.java
new file mode 100644
index 0000000..498b1f9
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/CoyoteConnectorLauncher.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sun.enterprise.web.connector.grizzly;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.management.ObjectName;
+
+import org.apache.catalina.connector.ProtocolHandler;
+import org.glassfish.grizzly.config.ssl.ServerSocketFactory;
+import org.glassfish.grizzly.http.server.HttpHandler;
+
+/**
+ * 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 CoyoteConnectorLauncher implements ProtocolHandler
+{
+    // START SJSAS 6439313     
+    protected boolean blocking = false;
+    // END SJSAS 6439313     
+
+    /**
+     * The <code>SelectorThread</code> implementation class. Not used when 
+     * Coyote is used.
+     */
+    protected String selectorThreadImpl = null; 
+    
+    
+    public CoyoteConnectorLauncher() {
+        // START SJSAS 6439313 
+        this(false,false,null);
+    }
+
+    
+    public CoyoteConnectorLauncher(boolean secure, boolean blocking, 
+                          String selectorThreadImpl) {
+        this.secure = secure;
+        this.blocking = blocking; 
+        this.selectorThreadImpl = selectorThreadImpl;
+    }
+
+
+    public int getMaxHttpHeaderSize() {
+        return maxHttpHeaderSize;
+    }
+    
+
+    public void setMaxHttpHeaderSize(int valueI) {
+        maxHttpHeaderSize = valueI;
+        setAttribute("maxHttpHeaderSize", "" + valueI);
+    }
+    
+
+    /** Pass config info
+     */
+    @Override
+    public void setAttribute( String name, Object value ) {
+
+        attributes.put(name, value);
+/*
+        if ("maxKeepAliveRequests".equals(name)) {
+            maxKeepAliveRequests = Integer.parseInt((String) value.toString());
+        } else if ("port".equals(name)) {
+            setPort(Integer.parseInt((String) value.toString()));
+        }
+*/
+    }
+
+    @Override
+    public Object getAttribute( String key ) {
+        return attributes.get(key);
+    }
+
+    /**
+     * Set a property.
+     */
+    public void setProperty(String name, String value) {
+        setAttribute(name, value);
+    }
+
+    /**
+     * Get a property
+     */
+    public String getProperty(String name) {
+        return (String)getAttribute(name);
+    }
+
+    /** The adapter, used to call the connector 
+     */
+    @Override
+    public void setHandler(HttpHandler adapter) {
+        this.adapter=adapter;
+    }
+
+    @Override
+    public HttpHandler getHandler() {
+        return adapter;
+    }
+
+    
+    /** Start the protocol
+     */
+    @Override
+    public void init() throws Exception {
+    }
+    
+    @Override
+    public void start() throws Exception {
+
+    }
+
+    @Override
+    public void destroy() throws Exception {
+
+    }
+    
+    // -------------------- Properties--------------------
+    protected boolean secure;
+
+    // socket factory attributes ( XXX replace with normal setters )
+    protected Map<String, Object> attributes = new HashMap<String, Object>();
+    protected String socketFactoryName=null;
+    protected String sslImplementationName=null;
+
+    private int maxKeepAliveRequests=100; // as in Apache HTTPD server
+    protected int timeout = 300000;	// 5 minutes as in Apache HTTPD server
+    protected int maxPostSize = 2 * 1024 * 1024;
+    protected int maxHttpHeaderSize = 4 * 1024;
+    private String reportedname;
+    protected int socketCloseDelay=-1;
+    protected boolean disableUploadTimeout = true;
+    protected HttpHandler adapter;
+    
+    // START OF SJSAS PE 8.1 6172948
+    /**
+     * The input request buffer size.
+     */
+    protected int requestBufferSize = 4096;
+    // END OF SJSAS PE 8.1 6172948
+    
+    /**
+     * Compression value.
+     */
+    protected String compression = "off";
+
+    // -------------------- Pool setup --------------------
+
+
+    public String getSocketFactory() {
+        return socketFactoryName;
+    }
+
+    public void setSocketFactory( String valueS ) {
+        socketFactoryName = valueS;
+        setAttribute("socketFactory", valueS);
+    }
+
+    public String getSSLImplementation() {
+        return sslImplementationName;
+    }
+
+    public void setSSLImplementation( String valueS) {
+        sslImplementationName=valueS;
+        setAttribute("sslImplementation", valueS);
+    }
+
+    public boolean getDisableUploadTimeout() {
+        return disableUploadTimeout;
+    }
+
+    public void setDisableUploadTimeout(boolean isDisabled) {
+        disableUploadTimeout = isDisabled;
+    }
+
+    public String getCompression() {
+        return compression;
+    }
+
+    public void setCompression(String valueS) {
+        compression = valueS;
+        setAttribute("compression", valueS);
+    }
+
+    public int getMaxPostSize() {
+        return maxPostSize;
+    }
+
+    public void setMaxPostSize(int valueI) {
+        maxPostSize = valueI;
+        setAttribute("maxPostSize", "" + valueI);
+    }
+    
+    public 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);
+    }
+
+    // START GlassFish Issue 657
+    public void setTruststore(String truststore) {
+        setAttribute("truststore", truststore);
+    }
+
+    public void setTruststoreType(String truststoreType) {
+        setAttribute("truststoreType", truststoreType);
+    }    
+    // END GlassFish Issue 657
+
+    public String getClientauth() {
+        return getProperty("clientauth");
+    }
+
+    public void setClientauth( String k ) {
+        setAttribute("clientauth", k);
+    }
+    
+    public String getProtocol() {
+        return getProperty("protocol");
+    }
+
+    public void setProtocol( String k ) {
+        setAttribute("protocol", k);
+    }
+
+    public String getProtocols() {
+        return getProperty("protocols");
+    }
+
+    public void setProtocols(String k) {
+        setAttribute("protocols", k);
+    }
+
+    public String getAlgorithm() {
+        return getProperty("algorithm");
+    }
+
+    public void setAlgorithm( String k ) {
+        setAttribute("algorithm", k);
+    }
+
+    public boolean getSecure() {
+        return secure;
+    }
+
+    public void setSecure( boolean b ) {
+    	secure=b;
+        setAttribute("secure", "" + b);
+    }
+    
+    // START SJSAS 6439313     
+    public boolean getBlocking() {
+        return blocking;
+    }
+
+    public void setBlocking( boolean b ) {
+    	blocking=b;
+        setAttribute("blocking", "" + b);
+    }
+    // END SJSAS 6439313     
+    
+    public String getCiphers() {
+        return getProperty("ciphers");
+    }
+
+    public void setCiphers(String ciphers) {
+        setAttribute("ciphers", ciphers);
+    }
+
+    public String getKeyAlias() {
+        return getProperty("keyAlias");
+    }
+
+    public void setKeyAlias(String keyAlias) {
+        setAttribute("keyAlias", keyAlias);
+    }
+
+    public int getMaxKeepAliveRequests() {
+        return maxKeepAliveRequests;
+    }
+
+    /** Set the maximum number of Keep-Alive requests that we will honor.
+     */
+    public void setMaxKeepAliveRequests(int mkar) {
+	maxKeepAliveRequests = mkar;
+        setAttribute("maxKeepAliveRequests", "" + mkar);
+    }
+
+    public int getSocketCloseDelay() {
+        return socketCloseDelay;
+    }
+
+    public void setSocketCloseDelay( int d ) {
+        socketCloseDelay=d;
+        setAttribute("socketCloseDelay", "" + d);
+    }
+
+    protected static ServerSocketFactory string2SocketFactory( String val)
+	throws ClassNotFoundException, IllegalAccessException,
+	InstantiationException
+    {
+	Class chC=Class.forName( val );
+	return (ServerSocketFactory)chC.newInstance();
+    }
+
+    public int getTimeout() {
+        return timeout;
+    }
+
+    public void setTimeout( int timeouts ) {
+	timeout = timeouts * 1000;
+        setAttribute("timeout", "" + timeouts);
+    }
+ 
+    public String getReportedname() {
+        return reportedname;
+    }
+
+    public void setReportedname( String reportedName) {
+	reportedname = reportedName;
+    }
+    
+    // START OF SJSAS PE 8.1 6172948
+    /**
+     * Set the request input buffer size
+     */
+    public void setBufferSize(int requestBufferSize){
+        this.requestBufferSize = requestBufferSize;
+    }
+    
+
+    /**
+     * Return the request input buffer size
+     */
+    public int getBufferSize(){
+        return requestBufferSize;
+    }    
+    // END OF SJSAS PE 8.1 6172948 
+}
diff --git a/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/DummyConnectorLauncher.java b/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/DummyConnectorLauncher.java
new file mode 100644
index 0000000..4989288
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/com/sun/enterprise/web/connector/grizzly/DummyConnectorLauncher.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sun.enterprise.web.connector.grizzly;
+
+/**
+ * Dummy Connector Launcher that doesn't start any listener like Grizzly
+ * or Coyote.
+ * 
+ * @author Jean-Francois Arcand
+ */
+public class DummyConnectorLauncher extends CoyoteConnectorLauncher {    
+
+    // ------------------------------------------------------- Constructor --//
+    
+    public DummyConnectorLauncher(boolean secure, boolean blocking, 
+                               String selectorThreadImpl) {
+        super(secure,blocking,selectorThreadImpl);     
+    }   
+
+    /** 
+     * Start the protocol
+     */
+    @Override
+    public void init() throws Exception {
+    }
+    
+    
+    @Override
+    public void start() throws Exception {
+    }
+
+
+    @Override
+    public void destroy() throws Exception {
+    }
+    
+    
+  
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Auditor.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Auditor.java
new file mode 100644
index 0000000..736ec9b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Auditor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+/**
+ * Defines interface of classes which implement audit functionality.
+ *
+ * <P>An <b>Auditor</b> class can be registered with a Context and
+ * will receive notification of all auditable events processed by the
+ * Authenticator of that context.
+ *
+ * <P> IASRI 4823322
+ *
+ * @author Jyri J. Virkki
+ * @version $Revision: 1.2 $
+ *
+ */
+
+public interface Auditor
+{
+    
+    /**
+     * Notify auditor of an authentication event.
+     *
+     * <P>This method will get invoked on every login attempt whether
+     * it was approved or denied by the authentication infrastructure.
+     *
+     * @param user the user for whom authentication was processed
+     * @param realm the realm which handled the authentication
+     * @param success true if the authentication succeeded, false if denied
+     */
+    public void authentication(String user, String realm, boolean success);
+
+    
+    /**
+     * Notify auditor of a servlet container invocation.
+     *
+     * <P>This method will get invoked on every request whether it
+     * was permitted or not by the authorization infrastructure.
+     *     
+     * @param req the HttpRequest
+     * @param success true if the invocation was allowed, false if denied.
+     */
+    public void webInvocation(HttpRequest req, boolean success);
+
+    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Authenticator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Authenticator.java
new file mode 100644
index 0000000..ce256ed
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Authenticator.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import org.apache.catalina.HttpRequest;
+
+import javax.servlet.ServletException;
+
+/**
+ * An <b>Authenticator</b> is a component (usually a Valve or Container) that
+ * provides some sort of authentication service.  The interface itself has no
+ * functional significance,  but is used as a tagging mechanism so that other
+ * components can detect the presence (via an "instanceof Authenticator" test)
+ * of an already configured authentication service.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:13 $
+ */
+
+public interface Authenticator {
+    public void login(String userName, char[] password, HttpRequest request)
+            throws ServletException;
+
+    public void logout(HttpRequest request) throws ServletException;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/CometEvent.java b/appserver/web/web-core/src/main/java/org/apache/catalina/CometEvent.java
new file mode 100644
index 0000000..7b1b31d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/CometEvent.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * The CometEvent interface.
+ * 
+ * @author Filip Hanik
+ * @author Remy Maucherat
+ */
+public interface CometEvent {
+
+    /**
+     * Enumeration describing the major events that the container can invoke 
+     * the CometProcessors event() method with
+     * BEGIN - will be called at the beginning 
+     *  of the processing of the connection. It can be used to initialize any relevant 
+     *  fields using the request and response objects. Between the end of the processing 
+     *  of this event, and the beginning of the processing of the end or error events,
+     *  it is possible to use the response object to write data on the open connection.
+     *  Note that the response object and dependent OutputStream and Writer are still 
+     *  not synchronized, so when they are accessed by multiple threads, 
+     *  synchronization is mandatory. After processing the initial event, the request 
+     *  is considered to be committed.
+     * READ - This indicates that input data is available, and that one read can be made
+     *  without blocking. The available and ready methods of the InputStream or
+     *  Reader may be used to determine if there is a risk of blocking: the servlet
+     *  should read while data is reported available, and can make one additional read
+     *  without blocking. When encountering a read error or an EOF, the servlet MUST
+     *  report it by either returning false or throwing an exception such as an 
+     *  IOException. This will cause the error event to be invoked, and the connection
+     *  will be closed. It is not allowed to attempt reading data from the request object
+     *  outside of the execution of this method.
+     * END - End may be called to end the processing of the request. Fields that have
+     *  been initialized in the begin method should be reset. After this event has
+     *  been processed, the request and response objects, as well as all their dependent
+     *  objects will be recycled and used to process other requests.
+     * ERROR - Error will be called by the container in the case where an IO exception
+     *  or a similar unrecoverable error occurs on the connection. Fields that have
+     *  been initialized in the begin method should be reset. After this event has
+     *  been processed, the request and response objects, as well as all their dependent
+     *  objects will be recycled and used to process other requests.
+     */
+    public enum EventType {BEGIN, READ, END, ERROR}
+    
+    
+    /**
+     * Event details
+     * TIMEOUT - the connection timed out (sub type of ERROR); note that this ERROR type is not fatal, and
+     *   the connection will not be closed unless the servlet uses the close method of the event
+     * CLIENT_DISCONNECT - the client connection was closed (sub type of ERROR)
+     * IOEXCEPTION - an IO exception occurred, such as invalid content, for example, an invalid chunk block (sub type of ERROR)
+     * WEBAPP_RELOAD - the webapplication is being reloaded (sub type of END)
+     * SERVER_SHUTDOWN - the server is shutting down (sub type of END)
+     * SESSION_END - the servlet ended the session (sub type of END)
+     */
+    public enum EventSubType { TIMEOUT, CLIENT_DISCONNECT, IOEXCEPTION, WEBAPP_RELOAD, SERVER_SHUTDOWN, SESSION_END }
+    
+    
+    /**
+     * Returns the HttpServletRequest.
+     * 
+     * @return HttpServletRequest
+     */
+    public HttpServletRequest getHttpServletRequest();
+    
+    /**
+     * Returns the HttpServletResponse.
+     * 
+     * @return HttpServletResponse
+     */
+    public HttpServletResponse getHttpServletResponse();
+    
+    /**
+     * Returns the event type.
+     * 
+     * @return EventType
+     */
+    public EventType getEventType();
+    
+    /**
+     * Returns the sub type of this event.
+     * 
+     * @return EventSubType
+     */
+    public EventSubType getEventSubType();
+    
+    /**
+     * Ends the Comet session. This signals to the container that 
+     * the container wants to end the comet session. This will send back to the
+     * client a notice that the server has no more data to send as part of this
+     * request. The servlet should perform any needed cleanup as if it had received
+     * an END or ERROR event. 
+     * 
+     * @throws IOException if an IO exception occurs
+     */
+    public void close() throws IOException;
+    
+    /**
+     * Sets the timeout for this Comet connection. Please NOTE, that the implementation 
+     * of a per connection timeout is OPTIONAL and MAY NOT be implemented.<br/>
+     * This method sets the timeout in milliseconds of idle time on the connection.
+     * The timeout is reset every time data is received from the connection or data is flushed
+     * using <code>response.flushBuffer()</code>. If a timeout occurs, the 
+     * <code>error(HttpServletRequest, HttpServletResponse)</code> method is invoked. The 
+     * web application SHOULD NOT attempt to reuse the request and response objects after a timeout
+     * as the <code>error(HttpServletRequest, HttpServletResponse)</code> method indicates.<br/>
+     * This method should not be called asynchronously, as that will have no effect.
+     * 
+     * @param timeout The timeout in milliseconds for this connection, must be a positive value, larger than 0
+     * @throws IOException An IOException may be thrown to indicate an IO error, 
+     *         or that the EOF has been reached on the connection
+     * @throws ServletException An exception has occurred, as specified by the root
+     *         cause
+     * @throws UnsupportedOperationException if per connection timeout is not supported, either at all or at this phase
+     *         of the invocation.
+     */
+    public void setTimeout(int timeout)
+        throws IOException, ServletException, UnsupportedOperationException;
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Connector.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Connector.java
new file mode 100644
index 0000000..0470547
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Connector.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import java.security.cert.X509Certificate;
+
+import com.sun.appserv.ProxyHandler;
+import org.apache.catalina.net.ServerSocketFactory;
+import org.glassfish.grizzly.http.server.HttpHandler;
+
+// END SJSAS 6363251
+
+/**
+ * A <b>Connector</b> is a component responsible receiving requests from,
+ * and returning responses to, a client application.  A Connector performs
+ * the following general logic:
+ * <ul>
+ * <li>Receive a request from the client application.
+ * <li>Create (or allocate from a pool) appropriate Request and Response
+ *     instances, and populate their properties based on the contents of
+ *     the received request, as described below.
+ *     <ul>
+ *     <li>For all Requests, the <code>connector</code>,
+ *         <code>protocol</code>, <code>remoteAddr</code>,
+ *         <code>response</code>, <code>scheme</code>,
+ *         <code>secure</code>, <code>serverName</code>,
+ *         <code>serverPort</code> and <code>stream</code>
+ *         properties <b>MUST</b> be set. The <code>contentLength</code>
+ *         and <code>contentType</code> properties are also generally set.
+ *     <li>For HttpRequests, the <code>method</code>, <code>queryString</code>,
+ *         <code>requestedSessionCookie</code>,
+ *         <code>requestedSessionId</code>, <code>requestedSessionURL</code>,
+ *         <code>requestURI</code>, and <code>secure</code> properties
+ *         <b>MUST</b> be set.  In addition, the various <code>addXxx</code>
+ *         methods must be called to record the presence of cookies, headers,
+ *         and locales in the original request.
+ *     <li>For all Responses, the <code>connector</code>, <code>request</code>,
+ *         and <code>stream</code> properties <b>MUST</b> be set.
+ *     <li>No additional headers must be set by the Connector for
+ *         HttpResponses.
+ *     </ul>
+ * <li>Identify an appropriate Container to use for processing this request.
+ *     For a stand alone Catalina installation, this will probably be a
+ *     (singleton) Engine implementation.  For a Connector attaching Catalina
+ *     to a web server such as Apache, this step could take advantage of
+ *     parsing already performed within the web server to identify the
+ *     Context, and perhaps even the Wrapper, to utilize in satisfying this
+ *     Request.
+ * <li>Call the <code>invoke()</code> method of the selected Container,
+ *     passing the initialized Request and Response instances as arguments.
+ * <li>Return any response created by the Container to the client, or
+ *     return an appropriate error message if an exception of any type
+ *     was thrown.
+ * <li>If utilizing a pool of Request and Response objects, recycle the pair
+ *     of instances that was just used.
+ * </ul>
+ * It is expected that the implementation details of various Connectors will
+ * vary widely, so the logic above should considered typical rather than
+ * normative.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.6 $ $Date: 2007/05/05 05:31:50 $
+ */
+
+public interface Connector {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container used for processing requests received by this
+     * Connector.
+     */
+    Container getContainer();
+
+
+    /**
+     * Set the Container used for processing requests received by this
+     * Connector.
+     *
+     * @param container The new Container to use
+     */
+    void setContainer(Container container);
+
+
+    /**
+     * Return the "enable DNS lookups" flag.
+     */
+    boolean getEnableLookups();
+
+
+    /**
+     * Set the "enable DNS lookups" flag.
+     *
+     * @param enableLookups The new "enable DNS lookups" flag value
+     */
+    void setEnableLookups(boolean enableLookups);
+
+
+    /**
+     * Return the server socket factory used by this Container.
+     */
+    ServerSocketFactory getFactory();
+
+
+    /**
+     * Set the server socket factory used by this Container.
+     *
+     * @param factory The new server socket factory
+     */
+    void setFactory(ServerSocketFactory factory);
+
+
+    /**
+     * Return descriptive information about this Connector implementation.
+     */
+    String getInfo();
+
+
+    /**
+     * 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.
+     */
+    int getRedirectPort();
+
+
+    /**
+     * Set the redirect port number.
+     *
+     * @param redirectPort The redirect port number (non-SSL to SSL)
+     */
+    void setRedirectPort(int redirectPort);
+
+
+    /**
+     * Return the scheme that will be assigned to requests received
+     * through this connector.  Default value is "http".
+     */
+    String getScheme();
+
+
+    /**
+     * Set the scheme that will be assigned to requests received through
+     * this connector.
+     *
+     * @param scheme The new scheme
+     */
+    void setScheme(String scheme);
+
+
+    /**
+     * Return the secure connection flag that will be assigned to requests
+     * received through this connector.  Default value is "false".
+     */
+    boolean getSecure();
+
+
+    /**
+     * Set the secure connection flag that will be assigned to requests
+     * received through this connector.
+     *
+     * @param secure The new secure connection flag
+     */
+    void setSecure(boolean secure);
+
+
+    /**
+     * Return the <code>Service</code> with which we are associated (if any).
+     */
+    Service getService();
+
+
+    /**
+     * Set the <code>Service</code> with which we are associated (if any).
+     *
+     * @param service The service that owns this Engine
+     */
+    void setService(Service service);
+
+
+    // BEGIN S1AS 5000999
+    /**
+     * Sets the default host for this Connector.
+     *
+     * @param defaultHost The default host for this Connector
+     */
+    void setDefaultHost(String defaultHost);
+
+    /**
+     * Gets the default host of this Connector.
+     *
+     * @return The default host of this Connector
+     */
+    String getDefaultHost();
+    // END S1AS 5000999
+
+
+    // START S1AS 6188932
+    /**
+     * Returns the value of this connector's authPassthroughEnabled flag.
+     *
+     * @return true if this connector is receiving its requests from
+     * a trusted intermediate server, false otherwise
+     */
+    boolean getAuthPassthroughEnabled();
+
+    /**
+     * Sets the value of this connector's authPassthroughEnabled flag.
+     *
+     * @param authPassthroughEnabled true if this connector is receiving its
+     * requests from a trusted intermediate server, false otherwise
+     */
+    void setAuthPassthroughEnabled(boolean authPassthroughEnabled);
+
+    /**
+     * Gets the ProxyHandler instance associated with this CoyoteConnector.
+     * 
+     * @return ProxyHandler instance associated with this CoyoteConnector,
+     * or null
+     */
+    ProxyHandler getProxyHandler();
+
+    /**
+     * Sets the ProxyHandler implementation for this CoyoteConnector to use.
+     * 
+     * @param proxyHandler ProxyHandler instance to use
+     */
+    void setProxyHandler(ProxyHandler proxyHandler);
+    // END S1AS 6188932
+    
+    /**
+     * Gets the name of this Connector.
+     */
+    String getName();
+
+    /**
+     * Sets the jvmRoute associated with this connector.
+     */
+    void setJvmRoute(String jvmRoute);
+
+    /**
+     * Get the jvmRoute associated with this connector.
+     */
+    String getJvmRoute();
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Create (or allocate) and return a Request object suitable for
+     * specifying the contents of a Request to the responsible Container.
+     */
+    Request createRequest();
+
+
+    /**
+     * Create (or allocate) and return a Response object suitable for
+     * receiving the contents of a Response from the responsible Container.
+     */
+    Response createResponse();
+
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     *
+     * @exception LifecycleException If this server was already initialized.
+     */
+    void initialize()
+    throws LifecycleException;
+
+    // START OF SJSAS 8.1 PE 6191830
+    /**
+      * Get the underlying WebContainer certificate for the request     
+      */
+    X509Certificate[] getCertificates(Request request);
+    // END OF SJSAS 8.1 PE 6191830
+
+    // START CR 6309511
+    /**
+     * Get the encoding to be used for byte<-->char conversion for
+     * data sent/received via this Connector
+     */
+    String getURIEncoding();
+
+    /**
+     * Set the encoding to be used for byte<-->char conversion for
+     * data sent/received via this Connector
+     */
+    void setURIEncoding(String encoding);
+    // END CR 6309511
+
+    
+    // START SJSAS 6363251
+    /**
+     * Set the <code>Adapter</code> used by this connector.
+     */
+    void setHandler(HttpHandler adapter);
+    
+    
+    /**
+     * Get the <code>HttpHandler</code> used by this connector.
+     */
+    HttpHandler getHandler();
+    // END SJSAS 6363251
+
+    /**
+     * Set the maximum size of a POST which will be automatically
+     * parsed by the container.
+     *
+     * @param maxPostSize The new maximum size in bytes of a POST which will
+     * be automatically parsed by the container
+     */
+    void setMaxPostSize(int maxPostSize);
+
+
+    /**
+     * Return the maximum size of a POST which will be saved by the container
+     * during authentication.
+     */
+    int getMaxSavePostSize();
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Contained.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Contained.java
new file mode 100644
index 0000000..9e6d28b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Contained.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+/**
+ * <p>Decoupling interface which specifies that an implementing class is
+ * associated with at most one <strong>Container</strong> instance.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Peter Donald
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:14 $
+ */
+
+public interface Contained {
+
+
+    //-------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the <code>Container</code> with which this instance is associated
+     * (if any); otherwise return <code>null</code>.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the <code>Container</code> with which this instance is associated.
+     *
+     * @param container The Container instance with which this instance is to
+     *  be associated, or <code>null</code> to disassociate this instance
+     *  from any Container
+     */
+    public void setContainer(Container container);
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Container.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Container.java
new file mode 100644
index 0000000..7e4fa9e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Container.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletException;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+
+
+/**
+ * A <b>Container</b> is an object that can execute requests received from
+ * a client, and return responses based on those requests.  A Container may
+ * optionally support a pipeline of Valves that process the request in an
+ * order configured at runtime, by implementing the <b>Pipeline</b> interface
+ * as well.
+ * <p>
+ * Containers will exist at several conceptual levels within Catalina.  The
+ * following examples represent common cases:
+ * <ul>
+ * <li><b>Engine</b> - Representation of the entire Catalina servlet engine,
+ *     most likely containing one or more subcontainers that are either Host
+ *     or Context implementations, or other custom groups.
+ * <li><b>Host</b> - Representation of a virtual host containing a number
+ *     of Contexts.
+ * <li><b>Context</b> - Representation of a single ServletContext, which will
+ *     typically contain one or more Wrappers for the supported servlets.
+ * <li><b>Wrapper</b> - Representation of an individual servlet definition
+ *     (which may support multiple servlet instances if the servlet itself
+ *     implements SingleThreadModel).
+ * </ul>
+ * A given deployment of Catalina need not include Containers at all of the
+ * levels described above.  For example, an administration application
+ * embedded within a network device (such as a router) might only contain
+ * a single Context and a few Wrappers, or even a single Wrapper if the
+ * application is relatively small.  Therefore, Container implementations
+ * need to be designed so that they will operate correctly in the absence
+ * of parent Containers in a given deployment.
+ * <p>
+ * A Container may also be associated with a number of support components
+ * that provide functionality which might be shared (by attaching it to a
+ * parent Container) or individually customized.  The following support
+ * components are currently recognized:
+ * <ul>
+ * <li><b>Loader</b> - Class loader to use for integrating new Java classes
+ *     for this Container into the JVM in which Catalina is running.
+ * <li><b>Logger</b> - Implementation of the <code>log()</code> method
+ *     signatures of the <code>ServletContext</code> interface.
+ * <li><b>Manager</b> - Manager for the pool of Sessions associated with
+ *     this Container.
+ * <li><b>Realm</b> - Read-only interface to a security domain, for
+ *     authenticating user identities and their corresponding roles.
+ * <li><b>Resources</b> - JNDI directory context enabling access to static
+ *     resources, enabling custom linkages to existing server components when
+ *     Catalina is embedded in a larger server.
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.4 $ $Date: 2006/12/15 18:56:51 $
+ */
+
+public interface Container {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The ContainerEvent event type sent when a child container is added
+     * by <code>addChild()</code>.
+     */
+    String ADD_CHILD_EVENT = "addChild";
+
+
+    /**
+     * The ContainerEvent event type sent when a Mapper is added
+     * by <code>addMapper()</code>.
+     */
+    String ADD_MAPPER_EVENT = "addMapper";
+
+
+    /**
+     * The ContainerEvent event type sent when a valve is added
+     * by <code>addValve()</code>, if this Container supports pipelines.
+     */
+    String ADD_VALVE_EVENT = "addValve";
+
+
+    /**
+     * The ContainerEvent event type sent when a child container is removed
+     * by <code>removeChild()</code>.
+     */
+    String REMOVE_CHILD_EVENT = "removeChild";
+
+
+    /**
+     * The ContainerEvent event type sent when a Mapper is removed
+     * by <code>removeMapper()</code>.
+     */
+    String REMOVE_MAPPER_EVENT = "removeMapper";
+
+
+    /**
+     * The ContainerEvent event type sent when a valve is removed
+     * by <code>removeValve()</code>, if this Container supports pipelines.
+     */
+    String REMOVE_VALVE_EVENT = "removeValve";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    String getInfo();
+
+
+    /**
+     * Return the Loader with which this Container is associated.  If there is
+     * no associated Loader, return the Loader associated with our parent
+     * Container (if any); otherwise, return <code>null</code>.
+     */
+    Loader getLoader();
+
+
+    /**
+     * Set the Loader with which this Container is associated.
+     *
+     * @param loader The newly associated loader
+     */
+    void setLoader(Loader loader);
+
+
+    /**
+     * Return the Logger with which this Container is associated.  If there is
+     * no associated Logger, return the Logger associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    Logger getLogger();
+
+
+    /**
+     * Set the Logger with which this Container is associated.
+     *
+     * @param logger The newly associated Logger
+     */
+    void setLogger(Logger logger);
+
+
+    /**
+     * Return the Manager with which this Container is associated.  If there is
+     * no associated Manager, return the Manager associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    Manager getManager();
+
+
+    /**
+     * Set the Manager with which this Container is associated.
+     *
+     * @param manager The newly associated Manager
+     */
+    void setManager(Manager manager);
+
+
+    /**
+     * Return an object which may be utilized for mapping to this component.
+     */
+    Object getMappingObject();
+
+
+    /**
+     * Return the Pipeline object that manages the Valves associated with
+     * this Container.
+     */
+    Pipeline getPipeline();
+
+
+    /**
+     * @return true if this container was configured with a custom pipeline,
+     * false otherwise
+     */
+    boolean hasCustomPipeline();
+
+
+    /**
+     * Get the delay between the invocation of the backgroundProcess method on
+     * this container and its children. Child containers will not be invoked
+     * if their delay value is not negative (which would mean they are using 
+     * their own thread). Setting this to a positive value will cause 
+     * a thread to be spawn. After waiting the specified amount of time, 
+     * the thread will invoke the executePeriodic method on this container 
+     * and all its children.
+     */
+    int getBackgroundProcessorDelay();
+
+
+    /**
+     * Set the delay between the invocation of the execute method on this
+     * container and its children.
+     * 
+     * @param delay The delay in seconds between the invocation of 
+     *              backgroundProcess methods
+     */
+    void setBackgroundProcessorDelay(int delay);
+
+
+    /**
+     * Return a name string (suitable for use by humans) that describes this
+     * Container.  Within the set of child containers belonging to a particular
+     * parent, Container names must be unique.
+     */
+    String getName();
+
+
+    /**
+     * Set a name string (suitable for use by humans) that describes this
+     * Container.  Within the set of child containers belonging to a particular
+     * parent, Container names must be unique.
+     *
+     * @param name New name of this container
+     *
+     * @exception IllegalStateException if this Container has already been
+     *  added to the children of a parent Container (after which the name
+     *  may not be changed)
+     */
+    void setName(String name);
+
+
+    /**
+     * Return the Container for which this Container is a child, if there is
+     * one.  If there is no defined parent, return <code>null</code>.
+     */
+    Container getParent();
+
+
+    /**
+     * Set the parent Container to which this Container is being added as a
+     * child.  This Container may refuse to become attached to the specified
+     * Container by throwing an exception.
+     *
+     * @param container Container to which this Container is being added
+     *  as a child
+     *
+     * @exception IllegalArgumentException if this Container refuses to become
+     *  attached to the specified Container
+     */
+    void setParent(Container container);
+
+
+    /**
+     * Return the parent class loader (if any) for web applications.
+     */
+    ClassLoader getParentClassLoader();
+
+
+    /**
+     * Set the parent class loader (if any) for web applications.
+     * This call is meaningful only <strong>before</strong> a Loader has
+     * been configured, and the specified value (if non-null) should be
+     * passed as an argument to the class loader constructor.
+     *
+     * @param parent The new parent class loader
+     */
+    void setParentClassLoader(ClassLoader parent);
+
+
+    /**
+     * Return the Realm with which this Container is associated.  If there is
+     * no associated Realm, return the Realm associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    Realm getRealm();
+
+
+    /**
+     * Set the Realm with which this Container is associated.
+     *
+     * @param realm The newly associated Realm
+     */
+    void setRealm(Realm realm);
+
+
+    /**
+     * Return the Resources with which this Container is associated.  If there
+     * is no associated Resources object, return the Resources associated with
+     * our parent Container (if any); otherwise return <code>null</code>.
+     */
+    DirContext getResources();
+
+
+    /**
+     * Set the Resources object with which this Container is associated.
+     *
+     * @param resources The newly associated Resources
+     */
+    void setResources(DirContext resources) throws Exception;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    void backgroundProcess();
+
+
+    /**
+     * Add a new child Container to those associated with this Container,
+     * if supported.  Prior to adding this Container to the set of children,
+     * the child's <code>setParent()</code> method must be called, with this
+     * Container as an argument.  This method may thrown an
+     * <code>IllegalArgumentException</code> if this Container chooses not
+     * to be attached to the specified Container, in which case it is not added
+     *
+     * @param child New child Container to be added
+     *
+     * @exception IllegalArgumentException if this exception is thrown by
+     *  the <code>setParent()</code> method of the child Container
+     * @exception IllegalArgumentException if the new child does not have
+     *  a name unique from that of existing children of this Container
+     * @exception IllegalStateException if this Container does not support
+     *  child Containers
+     */
+    void addChild(Container child);
+
+
+    /**
+     * Add a container event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    void addContainerListener(ContainerListener listener);
+
+
+    /**
+     * Notifies all event listeners of this Container of the given event.
+     *
+     * @param type Event type
+     * @param data Event data
+     */
+    void fireContainerEvent(String type, Object data);
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Return the child Container, associated with this Container, with
+     * the specified name (if any); otherwise, return <code>null</code>
+     *
+     * @param name Name of the child Container to be retrieved
+     */
+    Container findChild(String name);
+
+
+    /**
+     * Return the set of children Containers associated with this Container.
+     * If this Container has no children, a zero-length array is returned.
+     */
+    Container[] findChildren();
+
+
+    /**
+     * Return the set of container listeners associated with this Container.
+     * If this Container has no registered container listeners, a zero-length
+     * array is returned.
+     */
+    ContainerListener[] findContainerListeners();
+
+
+    /**
+     * Process the specified Request, and generate the corresponding Response,
+     * according to the design of this particular Container.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     *
+     * @exception IOException if an input/output error occurred while
+     *  processing
+     * @exception ServletException if a ServletException was thrown
+     *  while processing this request
+     */
+    void invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+    /**
+     * Remove an existing child Container from association with this parent
+     * Container.
+     *
+     * @param child Existing child Container to be removed
+     */
+    void removeChild(Container child);
+
+
+    /**
+     * Remove a container event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    void removeContainerListener(ContainerListener listener);
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Indicates whether the request will be checked to see if it is secure
+     * before adding Pragma and Cache-control headers when proxy caching has
+     * been disabled.
+     *
+     * @return true if the check is required; false otherwise.
+     */
+    boolean isCheckIfRequestIsSecure();
+
+
+    /**
+     * Sets the checkIfRequestIsSecure property of this Container.
+     *
+     * Setting this property to true will check if the request is secure
+     * before adding Pragma and Cache-Control headers when proxy caching has
+     * been disabled.
+     *
+     * @param checkIfRequestIsSecure true if check is required, false
+     * otherwise
+     */
+    void setCheckIfRequestIsSecure(boolean checkIfRequestIsSecure);
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerEvent.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerEvent.java
new file mode 100644
index 0000000..c2e0c9c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerEvent.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import java.util.EventObject;
+
+
+/**
+ * General event for notifying listeners of significant changes on a Container.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.4 $ $Date: 2007/03/22 18:04:03 $
+ */
+
+public final class ContainerEvent extends EventObject {
+
+    public static final String PRE_DESTROY = "predestroy";
+
+    public static final String BEFORE_CONTEXT_INITIALIZER_ON_STARTUP
+        = "beforeContextInitializerOnStartup";
+
+    public static final String AFTER_CONTEXT_INITIALIZER_ON_STARTUP
+        = "afterContextInitializerOnStartup";
+
+    public static final String BEFORE_CONTEXT_INITIALIZED
+        = "beforeContextInitialized";
+
+    public static final String AFTER_CONTEXT_INITIALIZED
+        = "afterContextInitialized";
+    
+    public static final String BEFORE_CONTEXT_DESTROYED
+        = "beforeContextDestroyed";
+
+    public static final String AFTER_CONTEXT_DESTROYED
+        = "afterContextDestroyed";
+
+    public static final String BEFORE_CONTEXT_ATTRIBUTE_ADDED
+        = "beforeContextAttributeAdded";
+
+    public static final String AFTER_CONTEXT_ATTRIBUTE_ADDED
+        = "afterContextAttributeAdded";
+
+    public static final String BEFORE_CONTEXT_ATTRIBUTE_REMOVED
+        = "beforeContextAttributeRemoved";
+
+    public static final String AFTER_CONTEXT_ATTRIBUTE_REMOVED
+        = "afterContextAttributeRemoved";
+
+    public static final String BEFORE_CONTEXT_ATTRIBUTE_REPLACED
+        = "beforeContextAttributeReplaced";
+
+    public static final String AFTER_CONTEXT_ATTRIBUTE_REPLACED
+        = "afterContextAttributeReplaced";
+
+    public static final String BEFORE_REQUEST_INITIALIZED
+        = "beforeRequestInitialized";
+
+    public static final String AFTER_REQUEST_INITIALIZED
+        = "afterRequestInitialized";
+
+    public static final String BEFORE_REQUEST_DESTROYED
+        = "beforeRequestDestroyed";
+
+    public static final String AFTER_REQUEST_DESTROYED
+        = "afterRequestDestroyed";
+
+    public static final String BEFORE_SESSION_CREATED
+        = "beforeSessionCreated";
+
+    public static final String AFTER_SESSION_CREATED
+        = "afterSessionCreated";
+
+    public static final String BEFORE_SESSION_DESTROYED
+        = "beforeSessionDestroyed";
+
+    public static final String AFTER_SESSION_DESTROYED
+        = "afterSessionDestroyed";
+
+    public static final String BEFORE_SESSION_ID_CHANGED
+            = "beforeSessionIdChanged";
+
+    public static final String AFTER_SESSION_ID_CHANGED
+            = "afterSessionIdChanged";
+
+    public static final String BEFORE_SESSION_ATTRIBUTE_ADDED
+        = "beforeSessionAttributeAdded";
+
+    public static final String AFTER_SESSION_ATTRIBUTE_ADDED
+        = "afterSessionAttributeAdded";
+
+    public static final String BEFORE_SESSION_ATTRIBUTE_REMOVED
+        = "beforeSessionAttributeRemoved";
+
+    public static final String AFTER_SESSION_ATTRIBUTE_REMOVED
+        = "afterSessionAttributeRemoved";
+
+    public static final String BEFORE_SESSION_ATTRIBUTE_REPLACED
+        = "beforeSessionAttributeReplaced";
+
+    public static final String AFTER_SESSION_ATTRIBUTE_REPLACED
+        = "afterSessionAttributeReplaced";
+
+    public static final String BEFORE_SESSION_VALUE_UNBOUND
+        = "beforeSessionValueUnbound";
+
+    public static final String AFTER_SESSION_VALUE_UNBOUND
+        = "afterSessionValueUnbound";
+
+    public static final String BEFORE_FILTER_INITIALIZED
+        = "beforeFilterInitialized";
+
+    public static final String AFTER_FILTER_INITIALIZED
+        = "afterFilterInitialized";
+
+    public static final String BEFORE_FILTER_DESTROYED
+        = "beforeFilterDestroyed";
+
+    public static final String AFTER_FILTER_DESTROYED
+        = "afterFilterDestroyed";
+
+    public static final String BEFORE_UPGRADE_HANDLER_INITIALIZED
+        = "beforeUpgradeHandlerInitialized";
+
+    public static final String AFTER_UPGRADE_HANDLER_INITIALIZED
+        = "afterUpgradeHandlerInitialized";
+
+    public static final String BEFORE_UPGRADE_HANDLER_DESTROYED
+        = "beforeUpgradeHandlerDestroyed";
+
+    public static final String AFTER_UPGRADE_HANDLER_DESTROYED
+        = "afterUpgradeHandlerDestroyed";
+
+    public static final String BEFORE_READ_LISTENER_ON_DATA_AVAILABLE
+        = "beforeReadListenerOnDataAvailable";
+
+    public static final String AFTER_READ_LISTENER_ON_DATA_AVAILABLE
+        = "afterReadListenerOnDataAvailable";
+
+    public static final String BEFORE_READ_LISTENER_ON_ALL_DATA_READ
+        = "beforeReadListenerOnAllDataRead";
+
+    public static final String AFTER_READ_LISTENER_ON_ALL_DATA_READ
+        = "afterReadListenerOnAllDataRead";
+
+    public static final String BEFORE_READ_LISTENER_ON_ERROR
+        = "beforeReadListenerOnError";
+
+    public static final String AFTER_READ_LISTENER_ON_ERROR
+        = "afterReadListenerOnError";
+
+    public static final String BEFORE_WRITE_LISTENER_ON_WRITE_POSSIBLE
+        = "beforeWriteListenerOnWritePossible";
+
+    public static final String AFTER_WRITE_LISTENER_ON_WRITE_POSSIBLE
+        = "afterWriteListenerOnWritePossible";
+
+    public static final String BEFORE_WRITE_LISTENER_ON_ERROR
+        = "beforeWriteListenerOnError";
+
+    public static final String AFTER_WRITE_LISTENER_ON_ERROR
+        = "afterWriteListenerOnError";
+
+    public static final String BEFORE_AUTHENTICATION
+        = "beforeAuthentication";
+    public static final String AFTER_AUTHENTICATION
+        = "afterAuthentication";
+    public static final String BEFORE_POST_AUTHENTICATION
+        = "beforePostAuthentication";
+    public static final String AFTER_POST_AUTHENTICATION
+        = "afterPostAuthentication";
+    public static final String BEFORE_LOGOUT
+        = "beforePostAuthentication";
+    public static final String AFTER_LOGOUT
+        = "afterPostAuthentication";
+
+    /**
+     * The Container on which this event occurred.
+     */
+    private transient Container container = null;
+
+
+    /**
+     * The event data associated with this event.
+     */
+    private Object data = null;
+
+
+    /**
+     * The event type this instance represents.
+     */
+    private String type = null;
+
+
+    /**
+     * Construct a new ContainerEvent with the specified parameters.
+     *
+     * @param container Container on which this event occurred
+     * @param type Event type
+     * @param data Event data
+     */
+    public ContainerEvent(Container container, String type, Object data) {
+
+        super(container);
+        this.container = container;
+        this.type = type;
+        this.data = data;
+
+    }
+
+
+    /**
+     * Return the event data of this event.
+     */
+    public Object getData() {
+
+        return (this.data);
+
+    }
+
+
+    /**
+     * Return the Container on which this event occurred.
+     */
+    public Container getContainer() {
+
+        return (this.container);
+
+    }
+
+
+    /**
+     * Return the event type of this event.
+     */
+    public String getType() {
+
+        return (this.type);
+
+    }
+
+
+    /**
+     * Return a string representation of this event.
+     */
+    public String toString() {
+
+        return ("ContainerEvent['" + getContainer() + "','" +
+                getType() + "','" + getData() + "']");
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerListener.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerListener.java
new file mode 100644
index 0000000..48a12fd
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+/**
+ * Interface defining a listener for significant Container generated events.
+ * Note that "container start" and "container stop" events are normally
+ * LifecycleEvents, not ContainerEvents.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:14 $
+ */
+
+public interface ContainerListener {
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event ContainerEvent that has occurred
+     */
+    public void containerEvent(ContainerEvent event);
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerServlet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerServlet.java
new file mode 100644
index 0000000..205991d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ContainerServlet.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+/**
+ * A <b>ContainerServlet</b> is a servlet that has access to Catalina
+ * internal functionality, and is loaded from the Catalina class loader
+ * instead of the web application class loader.  The property setter
+ * methods must be called by the container whenever a new instance of
+ * this servlet is put into service.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:15 $
+ */
+
+public interface ContainerServlet {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Wrapper with which this Servlet is associated.
+     */
+    public Wrapper getWrapper();
+
+
+    /**
+     * Set the Wrapper with which this Servlet is associated.
+     *
+     * @param wrapper The new associated Wrapper
+     */
+    public void setWrapper(Wrapper wrapper);
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Context.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Context.java
new file mode 100644
index 0000000..a21e7ed
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Context.java
@@ -0,0 +1,1332 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import org.apache.catalina.deploy.*;
+import org.apache.catalina.util.CharsetMapper;
+
+import javax.servlet.*;
+import java.util.EventListener;
+import java.util.List;
+import java.util.Map;
+import org.glassfish.grizzly.http.server.util.Mapper;
+
+
+/**
+ * A <b>Context</b> is a Container that represents a servlet context, and
+ * therefore an individual web application, in the Catalina servlet engine.
+ * It is therefore useful in almost every deployment of Catalina (even if a
+ * Connector attached to a web server (such as Apache) uses the web server's
+ * facilities to identify the appropriate Wrapper to handle this request.
+ * It also provides a convenient mechanism to use Interceptors that see
+ * every request processed by this particular web application.
+ * <p>
+ * The parent Container attached to a Context is generally a Host, but may
+ * be some other implementation, or may be omitted if it is not necessary.
+ * <p>
+ * The child containers attached to a Context are generally implementations
+ * of Wrapper (representing individual servlet definitions).
+ * <p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.4 $ $Date: 2007/05/05 05:31:51 $
+ */
+
+public interface Context extends Container {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The LifecycleEvent type sent when a context is reloaded.
+     */
+    String RELOAD_EVENT = "reload";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * @return the list of initialized application event listeners
+     * of this application, in the order in which they have been specified
+     * in the deployment descriptor
+     */
+    List<EventListener> getApplicationEventListeners();
+
+
+    /**
+     * Return the application available flag for this Context.
+     */
+    boolean getAvailable();
+
+
+    /**
+     * Set the application available flag for this Context.
+     *
+     * @param available The new application available flag
+     */
+    void setAvailable(boolean available);
+
+
+    /**
+     * Return the Locale to character set mapper for this Context.
+     */
+    CharsetMapper getCharsetMapper();
+
+
+    /**
+     * Set the Locale to character set mapper for this Context.
+     *
+     * @param mapper The new mapper
+     */
+    void setCharsetMapper(CharsetMapper mapper);
+
+
+    /**
+     * Return the path to a file to save this Context information.
+     */
+    String getConfigFile();
+
+
+    /**
+     * Set the path to a file to save this Context information.
+     *
+     * @param configFile The path to a file to save this Context information.
+     */
+    void setConfigFile(String configFile);
+
+
+    /**
+     * Return the "correctly configured" flag for this Context.
+     */
+    boolean getConfigured();
+
+
+    /**
+     * Set the "correctly configured" flag for this Context.  This can be
+     * set to false by startup listeners that detect a fatal configuration
+     * error to avoid the application from being made available.
+     *
+     * @param configured The new correctly configured flag
+     */
+    void setConfigured(boolean configured);
+
+
+    /**
+     * Return the "use cookies for session ids" flag.
+     */
+    boolean getCookies();
+
+
+    /**
+     * Set the "use cookies for session ids" flag.
+     *
+     * @param cookies The new flag
+     */
+    void setCookies(boolean cookies);
+
+
+    /**
+     * @return the name that will be assigned to any session tracking
+     * cookies created on behalf of this context
+     */
+    public String getSessionCookieName();
+
+
+    /**
+     * @return the session tracking cookie configuration of this
+     * <tt>ServletContext</tt>.
+     */
+    public SessionCookieConfig getSessionCookieConfig();
+
+
+    /**
+     * @return the name that will be assigned to any session tracking
+     * parameter created on behalf of this context
+     */
+    public String getSessionParameterName();
+
+
+    /**
+     * Checks whether the rewriting of URLs with the jsessionids of
+     * HTTP sessions belonging to this context is enabled or not.
+     *
+     * @return true if the rewriting of URLs with the jsessionids of HTTP
+     * sessions belonging to this context is enabled, false otherwise
+     */
+    public boolean isEnableURLRewriting();
+
+
+    /**
+     * Enables or disables the rewriting of URLs with the jsessionids of
+     * HTTP sessions belonging to this context.
+     *
+     * @param enableURLRewriting true if the rewriting of URLs with the
+     * jsessionids of HTTP sessions belonging to this context should be
+     * enabled, false otherwise
+     */
+    public void setEnableURLRewriting(boolean enableURLRewriting);
+
+
+    /**
+     * Return the "allow crossing servlet contexts" flag.
+     */
+    boolean getCrossContext();
+
+
+
+    /**
+     * Return the alternate Deployment Descriptor name.
+     */
+    String getAltDDName();
+
+
+    /**
+     * Set an alternate Deployment Descriptor name.
+     */
+    void setAltDDName(String altDDName) ;
+
+
+    /**
+     * Set the "allow crossing servlet contexts" flag.
+     *
+     * @param crossContext The new cross contexts flag
+     */
+    void setCrossContext(boolean crossContext);
+
+
+    /**
+     * Return the display name of this web application.
+     */
+    String getDisplayName();
+
+
+    /**
+     * Set the display name of this web application.
+     *
+     * @param displayName The new display name
+     */
+    void setDisplayName(String displayName);
+
+
+    /**
+     * Return the distributable flag for this web application.
+     */
+    boolean getDistributable();
+
+
+    /**
+     * Set the distributable flag for this web application.
+     *
+     * @param distributable The new distributable flag
+     */
+    void setDistributable(boolean distributable);
+
+
+    /**
+     * Return the document root for this Context.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     */
+    String getDocBase();
+
+
+    /**
+     * Set the document root for this Context.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     *
+     * @param docBase The new document root
+     */
+    void setDocBase(String docBase);
+
+
+    /**
+     * Return the URL encoded context path, using UTF-8.
+     */
+    String getEncodedPath();
+
+
+    /**
+     * Return the login configuration descriptor for this web application.
+     */
+    LoginConfig getLoginConfig();
+
+
+    /**
+     * Set the login configuration descriptor for this web application.
+     *
+     * @param config The new login configuration
+     */
+    void setLoginConfig(LoginConfig config);
+
+
+    /**
+     * Get the request dispatcher mapper.
+     */
+    Mapper getMapper();
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    NamingResources getNamingResources();
+
+
+    /**
+     * Set the naming resources for this web application.
+     *
+     * @param namingResources The new naming resources
+     */
+    void setNamingResources(NamingResources namingResources);
+
+
+    /**
+     * Return the context path for this web application.
+     */
+    String getPath();
+
+
+    /**
+     * Set the context path for this web application.
+     *
+     * @param path The new context path
+     */
+    void setPath(String path);
+
+
+    /**
+     * Return the public identifier of the deployment descriptor DTD that is
+     * currently being parsed.
+     */
+    String getPublicId();
+
+
+    /**
+     * Set the public identifier of the deployment descriptor DTD that is
+     * currently being parsed.
+     *
+     * @param publicId The public identifier
+     */
+    void setPublicId(String publicId);
+
+
+    /**
+     * Return the reloadable flag for this web application.
+     */
+    boolean getReloadable();
+
+
+    /**
+     * Set the reloadable flag for this web application.
+     *
+     * @param reloadable The new reloadable flag
+     */
+    void setReloadable(boolean reloadable);
+
+
+    /**
+     * Return the override flag for this web application.
+     */
+    boolean getOverride();
+
+
+    /**
+     * Set the override flag for this web application.
+     *
+     * @param override The new override flag
+     */
+    void setOverride(boolean override);
+
+
+    /**
+     * Return the privileged flag for this web application.
+     */
+    boolean getPrivileged();
+
+
+    /**
+     * Set the privileged flag for this web application.
+     *
+     * @param privileged The new privileged flag
+     */
+    void setPrivileged(boolean privileged);
+
+
+    /**
+     * Return the servlet context for which this Context is a facade.
+     */
+    ServletContext getServletContext();
+
+
+    /**
+     * Return the default session timeout (in minutes) for this
+     * web application.
+     */
+    int getSessionTimeout();
+
+
+    /**
+     * Set the default session timeout (in minutes) for this
+     * web application.
+     *
+     * @param timeout The new default session timeout
+     */
+    void setSessionTimeout(int timeout);
+
+
+    // START IASRI 4823322
+    /**
+     * Get Auditors associated with this context, if any.
+     *
+     * @return array of Auditor objects, or null
+     *
+     */
+    Auditor[] getAuditors();
+
+
+    /**
+     * Set the Auditors associated with this context.
+     *
+     * @param auditor array of Auditor objects
+     *
+     */
+    void setAuditors(Auditor[] auditor);
+    // END IASRI 4823322
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Adds the Listener with the given class name that is declared in the
+     * deployment descriptor to the set of Listeners configured for this
+     * application.
+     *
+     * @param listener the fully qualified class name of the Listener
+     */
+    void addApplicationListener(String listener);
+
+
+    /**
+     * Add a new application parameter for this application.
+     *
+     * @param parameter The new application parameter
+     */
+    void addApplicationParameter(ApplicationParameter parameter);
+
+
+    /**
+     * Add a security constraint to the set for this web application.
+     */
+    void addConstraint(SecurityConstraint constraint);
+
+
+    /**
+     * Add an EJB resource reference for this web application.
+     *
+     * @param ejb New EJB resource reference
+     */
+    void addEjb(ContextEjb ejb);
+
+
+    /**
+     * Add an environment entry for this web application.
+     *
+     * @param environment New environment entry
+     */
+    void addEnvironment(ContextEnvironment environment);
+
+
+    /**
+     * Add an error page for the specified error or Java exception.
+     *
+     * @param errorPage The error page definition to be added
+     */
+    void addErrorPage(ErrorPage errorPage);
+
+
+    /**
+     * Add a filter definition to this Context.
+     *
+     * @param filterDef The filter definition to be added
+     */
+    void addFilterDef(FilterDef filterDef);
+
+
+    /**
+     * Add a filter mapping to this Context.
+     *
+     * @param filterMap The filter mapping to be added
+     */
+    void addFilterMap(FilterMap filterMap);
+
+
+    /**
+     * Add the classname of an InstanceListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of an InstanceListener class
+     */
+    void addInstanceListener(String listener);
+
+
+    /**
+     * Add the given URL pattern as a jsp-property-group.  This maps
+     * resources that match the given pattern so they will be passed
+     * to the JSP container.  Though there are other elements in the
+     * property group, we only care about the URL pattern here.  The
+     * JSP container will parse the rest.
+     *
+     * @param pattern URL pattern to be mapped
+     */
+    void addJspMapping(String pattern);
+
+
+    /**
+     * Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4)
+     *
+     * @param locale locale to map an encoding for
+     * @param encoding encoding to be used for a give locale
+     */
+    void addLocaleEncodingMappingParameter(String locale, String encoding);
+
+
+    /**
+     * Add a local EJB resource reference for this web application.
+     *
+     * @param ejb New local EJB resource reference
+     */
+    void addLocalEjb(ContextLocalEjb ejb);
+
+
+    /**
+     * Add a new MIME mapping, replacing any existing mapping for
+     * the specified extension.
+     *
+     * @param extension Filename extension being mapped
+     * @param mimeType Corresponding MIME type
+     */
+    void addMimeMapping(String extension, String mimeType);
+
+
+    /**
+     * Add a new context initialization parameter, replacing any existing
+     * value for the specified name.
+     *
+     * @param name Name of the new parameter
+     * @param value Value of the new  parameter
+     */
+    void addParameter(String name, String value);
+
+
+    /**
+     * Add a resource reference for this web application.
+     *
+     * @param resource New resource reference
+     */
+    void addResource(ContextResource resource);
+
+
+    /**
+     * Add a resource environment reference for this web application.
+     *
+     * @param name The resource environment reference name
+     * @param type The resource environment reference type
+     */
+    void addResourceEnvRef(String name, String type);
+
+
+    /**
+     * Add a resource link for this web application.
+     *
+     * @param resourceLink New resource link
+     */
+    void addResourceLink(ContextResourceLink resourceLink);
+
+
+    /**
+     * Add a security role reference for this web application.
+     *
+     * @param role Security role used in the application
+     * @param link Actual security role to check for
+     */
+    void addRoleMapping(String role, String link);
+
+
+    /**
+     * Add a new security role for this web application.
+     *
+     * @param role New security role
+     */
+    void addSecurityRole(String role);
+
+
+    /**
+     * Adds the given servlet mapping to this Context, overriding any
+     * existing mapping for the specified pattern.
+     *
+     * @param pattern the URL pattern to be mapped
+     * @param name the name of the Servlet to which to map
+     */
+    void addServletMapping(String pattern, String name);
+
+
+    /**
+     * Add a resource which will be watched for reloading by the host auto
+     * deployer. Note: this will not be used in embedded mode.
+     *
+     * @param name Path to the resource, relative to docBase
+     */
+    void addWatchedResource(String name);
+
+
+    /**
+     * Add a new welcome file to the set recognized by this Context.
+     *
+     * @param name New welcome file name
+     */
+    void addWelcomeFile(String name);
+
+
+    /**
+     * Add the classname of a LifecycleListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of a LifecycleListener class
+     */
+    void addWrapperLifecycle(String listener);
+
+
+    /**
+     * Add the classname of a ContainerListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of a ContainerListener class
+     */
+    void addWrapperListener(String listener);
+
+
+    /**
+     * Factory method to create and return a new Wrapper instance, of
+     * the Java implementation class appropriate for this Context
+     * implementation.  The constructor of the instantiated Wrapper
+     * will have been called, but no properties will have been set.
+     */
+    Wrapper createWrapper();
+
+
+    /**
+     * Adds the given servlet instance with the given name to this servlet
+     * context and initializes it.
+     *
+     * <p>In order to add any URL patterns that will be mapped to the
+     * given servlet, addServletMappings must be used. If this context
+     * has already been started, the URL patterns must be passed to
+     * addServlet instead.
+     *
+     * @param servletName the servlet name
+     * @param instance the servlet instance
+     * @param initParams Map containing the initialization parameters for
+     * the servlet
+     *
+     * @return the ServletRegistration through which the servlet may be
+     * further configured
+     *
+     * @throws ServletException if the servlet fails to be initialized
+     */
+    public ServletRegistration.Dynamic addServlet(String servletName,
+            Servlet instance, Map<String, String> initParams)
+        throws ServletException;
+
+
+    /**
+     * Adds the given servlet instance with the given name and URL patterns
+     * to this servlet context, and initializes it.
+     *
+     * @param servletName the servlet name
+     * @param instance the servlet instance
+     * @param initParams Map containing the initialization parameters for
+     * the servlet
+     * @param urlPatterns the URL patterns that will be mapped to the servlet
+     *
+     * @return the ServletRegistration through which the servlet may be
+     * further configured
+     *
+     * @throws ServletException if the servlet fails to be initialized
+     */
+    public ServletRegistration.Dynamic addServlet(String servletName,
+            Servlet instance, Map<String, String> initParams,
+            String... urlPatterns)
+        throws ServletException;
+
+
+    /**
+     * Gets the (possibly empty) list of application parameters for this
+     * application.
+     */
+    List<ApplicationParameter> findApplicationParameters();
+
+
+    /**
+     * Gets the (possibly empty) list of security constraints defined for
+     * this web application.
+     */
+    List<SecurityConstraint> getConstraints();
+
+
+    /**
+     * Checks whether this web application has any security constraints
+     * defined.
+     */
+    public boolean hasConstraints();
+
+
+    /**
+     * Removes any security constraints from this web application.
+     */
+    void removeConstraints();
+
+
+    /**
+     * Return the EJB resource reference with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired EJB resource reference
+     */
+    ContextEjb findEjb(String name);
+
+
+    /**
+     * Return the defined EJB resource references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    ContextEjb[] findEjbs();
+
+
+    /**
+     * Return the environment entry with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired environment entry
+     */
+    ContextEnvironment findEnvironment(String name);
+
+
+    /**
+     * Return the set of defined environment entries for this web
+     * application.  If none have been defined, a zero-length array
+     * is returned.
+     */
+    ContextEnvironment[] findEnvironments();
+
+
+    /**
+     * Return the error page entry for the specified HTTP error code,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param errorCode Error code to look up
+     */
+    ErrorPage findErrorPage(int errorCode);
+
+
+    /**
+     * Return the error page entry for the specified Java exception type,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param exceptionType Exception type to look up
+     */
+    ErrorPage findErrorPage(String exceptionType);
+
+
+    /**
+     * Gets the default error page of this context.
+     *
+     * <p>A default error page is an error page that was declared without
+     * any exception-type and error-code.
+     *
+     * @return the default error page of this context, or null if this
+     * context does not have any default error page
+     */
+    public ErrorPage getDefaultErrorPage();
+
+
+    /**
+     * Return the filter definition for the specified filter name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param filterName Filter name to look up
+     */
+    FilterDef findFilterDef(String filterName);
+
+
+    /**
+     * Return the set of defined filters for this Context.
+     */
+    FilterDef[] findFilterDefs();
+
+
+    /**
+     * Gets the (possibly empty) list of filter mappings for this Context.
+     */
+    List<FilterMap> findFilterMaps();
+
+
+    /**
+     * Gets the (possibly empty) list of InstanceListener classes that
+     * will be added to newly created Wrappers automatically.
+     */
+    List<String> findInstanceListeners();
+
+
+    /**
+     * Return the local EJB resource reference with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired EJB resource reference
+     */
+    ContextLocalEjb findLocalEjb(String name);
+
+
+    /**
+     * Return the defined local EJB resource references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    ContextLocalEjb[] findLocalEjbs();
+
+
+    /**
+     * Return the MIME type to which the specified extension is mapped,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param extension Extension to map to a MIME type
+     */
+    String findMimeMapping(String extension);
+
+
+    /**
+     * Return the extensions for which MIME mappings are defined.  If there
+     * are none, a zero-length array is returned.
+     */
+    String[] findMimeMappings();
+
+
+    /**
+     * Return the value for the specified context initialization
+     * parameter name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the parameter to return
+     */
+    String findParameter(String name);
+
+
+    /**
+     * Return the names of all defined context initialization parameters
+     * for this Context.  If no parameters are defined, a zero-length
+     * array is returned.
+     */
+    String[] findParameters();
+
+
+    /**
+     * Return the resource reference with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource reference
+     */
+    ContextResource findResource(String name);
+
+
+    /**
+     * Return the resource environment reference type for the specified
+     * name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource environment reference
+     */
+    String findResourceEnvRef(String name);
+
+
+    /**
+     * Return the set of resource environment reference names for this
+     * web application.  If none have been specified, a zero-length
+     * array is returned.
+     */
+    String[] findResourceEnvRefs();
+
+
+    /**
+     * Return the resource link with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource link
+     */
+    ContextResourceLink findResourceLink(String name);
+
+
+    /**
+     * Return the defined resource links for this application.  If
+     * none have been defined, a zero-length array is returned.
+     */
+    ContextResourceLink[] findResourceLinks();
+
+
+    /**
+     * Return the defined resource references for this application.  If
+     * none have been defined, a zero-length array is returned.
+     */
+    ContextResource[] findResources();
+
+
+    /**
+     * For the given security role (as used by an application), return the
+     * corresponding role name (as defined by the underlying Realm) if there
+     * is one.  Otherwise, return the specified role unchanged.
+     *
+     * @param role Security role to map
+     */
+    String findRoleMapping(String role);
+
+
+    /**
+     * Checks if the given security role is defined for this application.
+     *
+     * @param role Security role to check for
+     *
+     * @return true if the specified security role is defined
+     * for this application, false otherwise
+     */
+    boolean hasSecurityRole(String role);
+
+
+    /**
+     * Removes any security roles defined for this application.
+     */
+    void removeSecurityRoles();
+
+
+    /**
+     * Return the servlet name mapped by the specified pattern (if any);
+     * otherwise return <code>null</code>.
+     *
+     * @param pattern Pattern for which a mapping is requested
+     */
+    String findServletMapping(String pattern);
+
+
+    /**
+     * Return the patterns of all defined servlet mappings for this
+     * Context.  If no mappings are defined, a zero-length array is returned.
+     */
+    String[] findServletMappings();
+
+
+    /**
+     * Return the context-relative URI of the error page for the specified
+     * HTTP status code, if any; otherwise return <code>null</code>.
+     *
+     * @param status HTTP status code to look up
+     */
+    ErrorPage findStatusPage(int status);
+
+
+    /**
+     * Return the set of HTTP status codes for which error pages have
+     * been specified.  If none are specified, a zero-length array
+     * is returned.
+     */
+    int[] findStatusPages();
+
+
+    /**
+     * Gets the watched resources defined for this web application.
+     */
+    List<String> getWatchedResources();
+
+
+    /**
+     * Return <code>true</code> if the specified welcome file is defined
+     * for this Context; otherwise return <code>false</code>.
+     *
+     * @param name Welcome file to verify
+     */
+    boolean findWelcomeFile(String name);
+
+
+    /**
+     * Return the set of welcome files defined for this Context.  If none are
+     * defined, a zero-length array is returned.
+     */
+    String[] findWelcomeFiles();
+
+
+    /**
+     * Gets the (possibly empty) list of LifecycleListener classes that
+     * will be added to newly created Wrappers automatically.
+     */
+    List<String> findWrapperLifecycles();
+
+
+    /**
+     * Gets the (possibly empty) list of ContainerListener classes that
+     * will be added to newly created Wrappers automatically.
+     */
+    List<String> findWrapperListeners();
+
+
+    /**
+     * Reload this web application, if reloading is supported.
+     *
+     * @exception IllegalStateException if the <code>reloadable</code>
+     *  property is set to <code>false</code>.
+     */
+    void reload();
+
+
+    /**
+     * Remove the application parameter with the specified name from
+     * the set for this application.
+     *
+     * @param name Name of the application parameter to remove
+     */
+    void removeApplicationParameter(String name);
+
+
+    /**
+     * Remove any EJB resource reference with the specified name.
+     *
+     * @param name Name of the EJB resource reference to remove
+     */
+    void removeEjb(String name);
+
+
+    /**
+     * Remove any environment entry with the specified name.
+     *
+     * @param name Name of the environment entry to remove
+     */
+    void removeEnvironment(String name);
+
+
+    /**
+     * Removes any error page declarations from this Context.
+     */
+    void removeErrorPages();
+
+
+    /**
+     * Remove the specified filter definition from this Context, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param filterDef Filter definition to be removed
+     */
+    void removeFilterDef(FilterDef filterDef);
+
+
+    /**
+     * Removes any filter mappings from this Context.
+     */
+    void removeFilterMaps();
+
+
+    /**
+     * Remove a class name from the set of InstanceListener classes that
+     * will be added to newly created Wrappers.
+     *
+     * @param listener Class name of an InstanceListener class to be removed
+     */
+    void removeInstanceListener(String listener);
+
+
+    /**
+     * Remove any local EJB resource reference with the specified name.
+     *
+     * @param name Name of the EJB resource reference to remove
+     */
+    void removeLocalEjb(String name);
+
+
+    /**
+     * Remove the MIME mapping for the specified extension, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param extension Extension to remove the mapping for
+     */
+    void removeMimeMapping(String extension);
+
+
+    /**
+     * Remove the context initialization parameter with the specified
+     * name, if it exists; otherwise, no action is taken.
+     *
+     * @param name Name of the parameter to remove
+     */
+    void removeParameter(String name);
+
+
+    /**
+     * Remove any resource reference with the specified name.
+     *
+     * @param name Name of the resource reference to remove
+     */
+    void removeResource(String name);
+
+
+    /**
+     * Remove any resource environment reference with the specified name.
+     *
+     * @param name Name of the resource environment reference to remove
+     */
+    void removeResourceEnvRef(String name);
+
+
+    /**
+     * Remove any resource link with the specified name.
+     *
+     * @param name Name of the resource link to remove
+     */
+    void removeResourceLink(String name);
+
+
+    /**
+     * Remove any security role reference for the specified name
+     *
+     * @param role Security role (as used in the application) to remove
+     */
+    void removeRoleMapping(String role);
+
+
+    /**
+     * Remove any servlet mapping for the specified pattern, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param pattern URL pattern of the mapping to remove
+     */
+    void removeServletMapping(String pattern);
+
+
+    /**
+     * Checks whether this web application has any watched resources
+     * defined.
+     */
+    public boolean hasWatchedResources();
+
+
+    /**
+     * Clears any watched resources defined for this web application.
+     */
+    public void removeWatchedResources();
+
+
+    /**
+     * Removes any Wrapper lifecycle listeners from this Context
+     */
+    void removeWrapperLifecycles();
+
+
+    /**
+     * Removes any Wrapper listeners from this Context
+     */
+    void removeWrapperListeners();
+
+
+    public void removeWelcomeFiles();
+
+
+    // START S1AS8PE 4817642
+    /**
+     * Return the "reuse session IDs when creating sessions" flag
+     */
+    boolean getReuseSessionID();
+
+    /**
+     * Set the "reuse session IDs when creating sessions" flag
+     *
+     * @param reuse The new value for the flag
+     */
+    void setReuseSessionID(boolean reuse);
+    // END S1AS8PE 4817642
+
+
+    // START RIMOD 4642650
+    /**
+     * Return whether this context allows sendRedirect() to redirect
+     * to a relative URL.
+     *
+     * The default value for this property is 'false'.
+     */
+    boolean getAllowRelativeRedirect();
+
+
+    /**
+     * Set whether this context allows sendRedirect() to redirect
+     * to a relative URL.
+     *
+     * @param allowRelativeURLs The new value for this property. The
+     *                          default value for this flag is 'false'.
+     */
+    void setAllowRelativeRedirect(boolean allowRelativeURLs);
+
+
+    // END RIMOD 4642650
+       /**
+     * Get the server.xml <context> attribute's xmlNamespaceAware.
+     * @return true if namespace awareness is enabled.
+     *
+     */
+    boolean getXmlNamespaceAware();
+
+
+    /**
+     * Get the server.xml <context> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    boolean getXmlValidation();
+
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlValidation true to enable xml instance validation
+     */
+    void setXmlValidation(boolean xmlValidation);
+
+
+   /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+   void setXmlNamespaceAware(boolean xmlNamespaceAware);
+    /**
+     * Get the server.xml <context> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     */
+
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing tlds files.
+     * @param tldValidation true to enable xml instance validation
+     */
+    void setTldValidation(boolean tldValidation);
+
+
+    /**
+     * Get the server.xml <context> attribute's webXmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    boolean getTldValidation();
+
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awareness is enabled.
+     */
+    boolean getTldNamespaceAware();
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param tldNamespaceAware true to enable namespace awareness
+     */
+    void setTldNamespaceAware(boolean tldNamespaceAware);
+
+
+    // START SJSAS 8.1 5049111
+    /**
+     * Return <code>true</code> if this context contains the JSF servlet.
+     */
+    boolean isJsfApplication();
+    // END SJSAS 8.1 5049111
+
+
+    // START SJSAS 6253524
+    /**
+     * Indicates whether this web module contains any ad-hoc paths.
+     *
+     * An ad-hoc path is a servlet path that is mapped to a servlet
+     * not declared in the web module's deployment descriptor.
+     *
+     * A web module all of whose mappings are for ad-hoc paths is called an
+     * ad-hoc web module.
+     *
+     * @return true if this web module contains any ad-hoc paths, false
+     * otherwise
+     */
+    boolean hasAdHocPaths();
+
+    /**
+     * Returns the name of the ad-hoc servlet responsible for servicing the
+     * given path.
+     *
+     * @param path The path to service
+     *
+     * @return The name of the ad-hoc servlet responsible for servicing the
+     * given path, or null if the given path is not an ad-hoc path
+     */
+    String getAdHocServletName(String path);
+    // END SJSAS 6253524
+
+    /**
+     * Indicates whether the Pragma and Cache-Control headers will be set
+     * to "No-cache" if proxy caching has been disabled.
+     *
+     * @return true if Pragma and Cache-Control headers will be set to
+     * "No-cache" if proxy caching has been disabled; false otherwise.
+     */
+    boolean isSecurePagesWithPragma();
+
+    /**
+     * Sets the securePagesWithPragma property of this Context.
+     *
+     * Setting this property to true will result in Pragma and Cache-Control
+     * headers with a value of "No-cache" if proxy caching has been disabled.
+     *
+     * Setting this property to false will not add any Pragma header,
+     * but will set the Cache-Control header to "private".
+     *
+     * @param securePagesWithPragma true if Pragma and Cache-Control headers
+     * are to be set to "No-cache" if proxy caching has been disabled, false
+     * otherwise
+     */
+    void setSecurePagesWithPragma(boolean securePagesWithPragma);
+
+    /**
+     * Gets the Authenticator of this Context.
+     *
+     * @return the Authenticator of this Context
+     */
+    Authenticator getAuthenticator();
+
+    /**
+     * Notifies all ServletRequestListener instances configured for this Context
+     * of the requestInitialized event.
+     *
+     * @param request
+     */
+    public void fireRequestInitializedEvent(ServletRequest request);
+
+    /**
+     * Notifies all ServletRequestListener instances configured for this Context
+     * of the requestDestroyed event.
+     *
+     * @param request
+     */
+    public void fireRequestDestroyedEvent(ServletRequest request);
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Deployer.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Deployer.java
new file mode 100644
index 0000000..74067d4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Deployer.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import java.io.IOException;
+import java.net.URL;
+
+
+/**
+ * A <b>Deployer</b> is a specialized Container into which web applications
+ * can be deployed and undeployed.  Such a Container will create and install
+ * child Context instances for each deployed application.  The unique key
+ * for each web application will be the context path to which it is attached.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:15 $
+ */
+
+/* public interface Deployer extends Container { */
+public interface Deployer  {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The ContainerEvent event type sent when a new application is
+     * being installed by <code>install()</code>, before it has been
+     * started.
+     */
+    public static final String PRE_INSTALL_EVENT = "pre-install";
+
+
+    /**
+     * The ContainerEvent event type sent when a new application is
+     * installed by <code>install()</code>, after it has been started.
+     */
+    public static final String INSTALL_EVENT = "install";
+
+
+    /**
+     * The ContainerEvent event type sent when an existing application is
+     * removed by <code>remove()</code>.
+     */
+    public static final String REMOVE_EVENT = "remove";
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the name of the Container with which this Deployer is associated.
+     */
+    public String getName();
+
+
+    /**
+     * Install a new web application, whose web application archive is at the
+     * specified URL, into this container with the specified context path.
+     * A context path of "" (the empty string) should be used for the root
+     * application for this container.  Otherwise, the context path must
+     * start with a slash.
+     * <p>
+     * If this application is successfully installed, a ContainerEvent of type
+     * <code>INSTALL_EVENT</code> will be sent to all registered listeners,
+     * with the newly created <code>Context</code> as an argument.
+     *
+     * @param contextPath The context path to which this application should
+     *  be installed (must be unique)
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalStateException if the specified context path
+     *  is already attached to an existing web application
+     * @exception IOException if an input/output error was encountered
+     *  during installation
+     */
+    public void install(String contextPath, URL war) throws IOException;
+
+
+    /**
+     * <p>Install a new web application, whose context configuration file
+     * (consisting of a <code>&lt;Context&gt;</code> element) and web
+     * application archive are at the specified URLs.</p>
+     *
+     * <p>If this application is successfully installed, a ContainerEvent
+     * of type <code>INSTALL_EVENT</code> will be sent to all registered
+     * listeners, with the newly created <code>Context</code> as an argument.
+     * </p>
+     *
+     * @param config A URL that points to the context configuration file to
+     *  be used for configuring the new Context
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed
+     *
+     * @exception IllegalArgumentException if one of the specified URLs is
+     *  null
+     * @exception IllegalStateException if the context path specified in the
+     *  context configuration file is already attached to an existing web
+     *  application
+     * @exception IOException if an input/output error was encountered
+     *  during installation
+     */
+    public void install(URL config, URL war) throws IOException;
+
+
+    /**
+     * Return the Context for the deployed application that is associated
+     * with the specified context path (if any); otherwise return
+     * <code>null</code>.
+     *
+     * @param contextPath The context path of the requested web application
+     */
+    public Context findDeployedApp(String contextPath);
+
+
+    /**
+     * Return the context paths of all deployed web applications in this
+     * Container.  If there are no deployed applications, a zero-length
+     * array is returned.
+     */
+    public String[] findDeployedApps();
+
+
+    /**
+     * Remove an existing web application, attached to the specified context
+     * path.  If this application is successfully removed, a
+     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+     * registered listeners, with the removed <code>Context</code> as
+     * an argument.
+     *
+     * @param contextPath The context path of the application to be removed
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  removal
+     */
+    public void remove(String contextPath) throws IOException;
+
+
+    /**
+     * Remove an existing web application, attached to the specified context
+     * path.  If this application is successfully removed, a
+     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+     * registered listeners, with the removed <code>Context</code> as
+     * an argument. Deletes the web application war file and/or directory
+     * if they exist in the Host's appBase.
+     *
+     * @param contextPath The context path of the application to be removed
+     * @param undeploy boolean flag to remove web application from server
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  removal
+     */
+    public void remove(String contextPath, boolean undeploy) throws IOException;
+
+
+    /**
+     * Start an existing web application, attached to the specified context
+     * path.  Only starts a web application if it is not running.
+     *
+     * @param contextPath The context path of the application to be started
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  startup
+     */
+    public void start(String contextPath) throws IOException;
+
+
+    /**
+     * Stop an existing web application, attached to the specified context
+     * path.  Only stops a web application if it is running.
+     *
+     * @param contextPath The context path of the application to be stopped
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs while stopping
+     *  the web application
+     */
+    public void stop(String contextPath) throws IOException;
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Engine.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Engine.java
new file mode 100644
index 0000000..70cc574
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Engine.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+/**
+ * An <b>Engine</b> is a Container that represents the entire Catalina servlet
+ * engine.  It is useful in the following types of scenarios:
+ * <ul>
+ * <li>You wish to use Interceptors that see every single request processed
+ *     by the entire engine.
+ * <li>You wish to run Catalina in with a standalone HTTP connector, but still
+ *     want support for multiple virtual hosts.
+ * </ul>
+ * In general, you would not use an Engine when deploying Catalina connected
+ * to a web server (such as Apache), because the Connector will have
+ * utilized the web server's facilities to determine which Context (or
+ * perhaps even which Wrapper) should be utilized to process this request.
+ * <p>
+ * The child containers attached to an Engine are generally implementations
+ * of Host (representing a virtual host) or Context (representing individual
+ * an individual servlet context), depending upon the Engine implementation.
+ * <p>
+ * If used, an Engine is always the top level Container in a Catalina
+ * hierarchy. Therefore, the implementation's <code>setParent()</code> method
+ * should throw <code>IllegalArgumentException</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:16 $
+ */
+
+public interface Engine extends Container {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the default hostname for this Engine.
+     */
+    public String getDefaultHost();
+
+
+    /**
+     * Set the default hostname for this Engine.
+     *
+     * @param defaultHost The new default host
+     */
+    public void setDefaultHost(String defaultHost);
+
+
+    /**
+     * Retrieve the JvmRouteId for this engine.
+     */
+    public String getJvmRoute();
+
+
+    /**
+     * Set the JvmRouteId for this engine.
+     *
+     * @param jvmRouteId the (new) JVM Route ID. Each Engine within a cluster
+     *        must have a unique JVM Route ID.
+     */
+    public void setJvmRoute(String jvmRouteId);
+
+
+    /**
+     * Return the <code>Service</code> with which we are associated (if any).
+     */
+    public Service getService();
+
+
+    /**
+     * 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);
+
+    
+    // --------------------------------------------------------- Public Methods
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Globals.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Globals.java
new file mode 100644
index 0000000..3c464cb
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Globals.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+// START SJSAS
+import org.apache.catalina.servlets.DefaultServlet;
+// END SJSAS
+
+/**
+ * Global constants that are applicable to multiple packages within Catalina.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.12 $ $Date: 2007/06/18 23:14:22 $
+ */
+
+public final class Globals {
+
+    /**
+     * The servlet context attribute under which we store the alternate
+     * deployment descriptor for this web application 
+     */
+    public static final String ALT_DD_ATTR = 
+        "org.apache.catalina.deploy.alt_dd";
+
+    /**
+     * The request attribute under which we store the array of X509Certificate
+     * objects representing the certificate chain presented by our client,
+     * if any.
+     */
+    public static final String CERTIFICATES_ATTR =
+        "javax.servlet.request.X509Certificate";
+
+    /**
+     * SSL Certificate Request Attributite.
+     */
+    public static final String SSL_CERTIFICATE_ATTR = "org.apache.coyote.request.X509Certificate";
+
+    /**
+     * The request attribute under which we store the name of the cipher suite
+     * being used on an SSL connection (as an object of type
+     * java.lang.String).
+     */
+    public static final String CIPHER_SUITE_ATTR =
+        "javax.servlet.request.cipher_suite";
+
+
+    /**
+     * The servlet context attribute under which we store the class loader
+     * used for loading servlets (as an object of type java.lang.ClassLoader).
+     */
+    public static final String CLASS_LOADER_ATTR =
+        "org.apache.catalina.classloader";
+
+    /**
+     * Request dispatcher state.
+     */
+    public static final String DISPATCHER_TYPE_ATTR = 
+        "org.apache.catalina.core.DISPATCHER_TYPE";
+
+    /**
+     * Request dispatcher path.
+     */
+    public static final String DISPATCHER_REQUEST_PATH_ATTR = 
+        "org.apache.catalina.core.DISPATCHER_REQUEST_PATH";
+
+    /**
+     * The JNDI directory context which is associated with the context. This
+     * context can be used to manipulate static files.
+     */
+    public static final String RESOURCES_ATTR =
+        "org.apache.catalina.resources";
+    public static final String ALTERNATE_RESOURCES_ATTR =
+        "org.apache.catalina.alternateResources";
+
+
+    /**
+     * The servlet context attribute under which we store the class path
+     * for our application class loader (as an object of type String),
+     * delimited with the appropriate path delimiter for this platform.
+     */
+    public static final String CLASS_PATH_ATTR =
+        "org.apache.catalina.jsp_classpath";
+
+
+    /**
+     * The request attribute under which the Invoker servlet will store
+     * the invoking servlet path, if it was used to execute a servlet
+     * indirectly instead of through a servlet mapping.
+     */
+    public static final String INVOKED_ATTR =
+        "org.apache.catalina.INVOKED";
+
+
+    /**
+     * The request attribute under which we expose the value of the
+     * <code>&lt;jsp-file&gt;</code> value associated with this servlet,
+     * if any.
+     */
+    public static final String JSP_FILE_ATTR =
+        "org.apache.catalina.jsp_file";
+
+
+    /**
+     * The request attribute under which we store the key size being used for
+     * this SSL connection (as an object of type java.lang.Integer).
+     */
+    public static final String KEY_SIZE_ATTR =
+        "javax.servlet.request.key_size";
+
+
+    /**
+     * The request attribute under which we store the session id being used
+     * for this SSL connection (as an object of type java.lang.String).
+     */
+    public static final String SSL_SESSION_ID_ATTR =
+        "javax.servlet.request.ssl_session_id";
+    
+
+    /**
+     * The servlet context attribute under which the managed bean Registry
+     * will be stored for privileged contexts (if enabled).
+     */
+    public static final String MBEAN_REGISTRY_ATTR =
+        "org.apache.catalina.Registry";
+
+
+    /**
+     * The servlet context attribute under which the MBeanServer will be stored
+     * for privileged contexts (if enabled).
+     */
+    public static final String MBEAN_SERVER_ATTR =
+        "org.apache.catalina.MBeanServer";
+
+
+    /**
+     * The request attribute under which we store the servlet name on a
+     * named dispatcher request.
+     */
+    public static final String NAMED_DISPATCHER_ATTR =
+        "org.apache.catalina.NAMED";
+
+
+    /**
+     * The name of the cookie used to pass the session identifier back
+     * and forth with the client.
+     */
+    public static final String SESSION_COOKIE_NAME = "JSESSIONID";
+
+
+    /**
+     * The name of the path parameter used to pass the session identifier
+     * back and forth with the client.
+     */
+    public static final String SESSION_PARAMETER_NAME = "jsessionid";
+
+
+    /**
+     * The subject under which the AccessControlContext is running.
+     */
+    public static final String SUBJECT_ATTR =
+        "javax.security.auth.subject";
+
+    
+    // START SJSAS
+    /**
+     * The class name of the default servlet
+     */
+    public static final String DEFAULT_SERVLET_CLASS_NAME =
+        DefaultServlet.class.getName();
+    // END SJSAS
+
+
+    /**
+     * Has security been turned on?
+     */
+    public static final boolean IS_SECURITY_ENABLED = 
+        (System.getSecurityManager() != null);
+
+
+    // START GlassFish 740
+    public static final String JSP_PROPERTY_GROUPS_CONTEXT_ATTRIBUTE =
+        "com.sun.jsp.propertyGroups";
+
+    public static final String WEB_XML_VERSION_CONTEXT_ATTRIBUTE =
+        "com.sun.servlet.webxml.version";
+    // END GlassFish 740
+
+    // START GlassFish 747
+    public static final String JSP_TLD_URI_TO_LOCATION_MAP =
+        "com.sun.jsp.tldUriToLocationMap";
+    // END GlassFish 747
+
+    // START GlassFish 896
+    public static final String SESSION_TRACKER =
+        "com.sun.enterprise.http.sessionTracker";    
+    // END GlassFish 896
+
+    public static final String REQUEST_FACADE_HELPER =
+        "org.glassfish.web.RequestFacadeHelper";
+
+    /**
+     * The name of the cookie used to carry a session's version info
+     */
+    public static final String SESSION_VERSION_COOKIE_NAME =
+        "JSESSIONIDVERSION";
+
+    /**
+     * The name of the path parameter used to carry a session's version info
+     */
+    public static final String SESSION_VERSION_PARAMETER_NAME =
+        "jsessionidversion";
+
+    public static final String SESSION_VERSION_PARAMETER =
+        ";" + SESSION_VERSION_PARAMETER_NAME + "=";
+
+    public static final String SESSION_VERSIONS_REQUEST_ATTRIBUTE =
+        "com.sun.enterprise.http.sessionVersions";
+
+    public static final String JREPLICA_COOKIE_NAME = "JREPLICA";
+
+    public static final String JREPLICA_PARAMETER_NAME = "jreplica";
+
+    public static final String JREPLICA_PARAMETER =
+        ";" + JREPLICA_PARAMETER_NAME + "=";
+
+    public static final String JREPLICA_SESSION_NOTE =
+        "com.sun.enterprise.http.jreplicaLocation";
+
+    public static final String WRAPPED_REQUEST =
+        "__javax.security.auth.message.request";
+    
+    public static final String WRAPPED_RESPONSE =
+        "__javax.security.auth.message.response"; 
+    
+    
+    /**
+     * The servlet context attribute under which we store a flag used
+     * to mark this request as having been processed by the SSIServlet.
+     * We do this because of the pathInfo mangling happening when using
+     * the CGIServlet in conjunction with the SSI servlet. (value stored
+     * as an object of type String)
+     */
+     public static final String SSI_FLAG_ATTR =
+         "org.apache.catalina.ssi.SSIServlet";
+
+    /**
+     * Request path.
+     */
+    public static final String CONSTRAINT_URI =
+        "org.apache.catalina.CONSTRAINT_URI";
+
+    public static final String META_INF_RESOURCES = "META-INF/resources";
+
+    public static final String ISO_8859_1_ENCODING = "ISO-8859-1";
+
+    public static final String FACES_INITIALIZER = "com.sun.faces.config.FacesInitializer";
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Host.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Host.java
new file mode 100644
index 0000000..e43f2a0
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Host.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+/**
+ * A <b>Host</b> is a Container that represents a virtual host in the
+ * Catalina servlet engine.  It is useful in the following types of scenarios:
+ * <ul>
+ * <li>You wish to use Interceptors that see every single request processed
+ *     by this particular virtual host.
+ * <li>You wish to run Catalina in with a standalone HTTP connector, but still
+ *     want support for multiple virtual hosts.
+ * </ul>
+ * In general, you would not use a Host when deploying Catalina connected
+ * to a web server (such as Apache), because the Connector will have
+ * utilized the web server's facilities to determine which Context (or
+ * perhaps even which Wrapper) should be utilized to process this request.
+ * <p>
+ * The parent Container attached to a Host is generally an Engine, but may
+ * be some other implementation, or may be omitted if it is not necessary.
+ * <p>
+ * The child containers attached to a Host are generally implementations
+ * of Context (representing an individual servlet context).
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:16 $
+ */
+
+public interface Host extends Container {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The ContainerEvent event type sent when a new alias is added
+     * by <code>addAlias()</code>.
+     */
+    public static final String ADD_ALIAS_EVENT = "addAlias";
+
+
+    /**
+     * The ContainerEvent event type sent when an old alias is removed
+     * by <code>removeAlias()</code>.
+     */
+    public static final String REMOVE_ALIAS_EVENT = "removeAlias";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the application root for this Host.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     */
+    public String getAppBase();
+
+
+    /**
+     * Set the application root for this Host.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     *
+     * @param appBase The new application root
+     */
+    public void setAppBase(String appBase);
+
+
+    /**
+     * Return the value of the auto deploy flag.  If true, it indicates that 
+     * this host's child webapps should be discovered and automatically 
+     * deployed dynamically.
+     */
+    public boolean getAutoDeploy();
+
+
+    /**
+     * Set the auto deploy flag value for this host.
+     * 
+     * @param autoDeploy The new auto deploy flag
+     */
+    public void setAutoDeploy(boolean autoDeploy);
+
+
+    /**
+     * Return the value of the deploy on startup flag.  If true, it indicates 
+     * that this host's child webapps should be discovered and automatically 
+     * deployed.
+     */
+    public boolean getDeployOnStartup();
+
+
+    /**
+     * Set the deploy on startup flag value for this host.
+     * 
+     * @param deployOnStartup The new deploy on startup flag
+     */
+    public void setDeployOnStartup(boolean deployOnStartup);
+
+
+    /**
+     * Return the canonical, fully qualified, name of the virtual host
+     * this Container represents.
+     */
+    public String getName();
+
+
+    /**
+     * Set the canonical, fully qualified, name of the virtual host
+     * this Container represents.
+     *
+     * @param name Virtual host name
+     *
+     * @exception IllegalArgumentException if name is null
+     */
+    public void setName(String name);
+
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awareness is enabled.
+     *
+     */
+    public boolean getXmlNamespaceAware();
+
+
+    /**
+     * Get the server.xml <host> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation();
+
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean xmlValidation);
+
+
+   /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean xmlNamespaceAware);
+
+
+    // BEGIN S1AS 5000999
+    /**
+     * Associates this Host with the given network listener names.
+     *
+     * @param networkListenerNames The network listener names with which to associate this Host
+     */
+    public void setNetworkListenerNames(String[] networkListenerNames);
+
+    /**
+     * Gets the network listener names with which this Host is associated.
+     *
+     * @return The network listener names with which this Host is associated,
+     * or null if this Host has not been associated with any network listener
+     */
+    public String[] getNetworkListenerNames();
+    // END S1AS 5000999
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an alias name that should be mapped to this same Host.
+     *
+     * @param alias The alias to be added
+     */
+    public void addAlias(String alias);
+
+
+    /**
+     * Return the set of alias names for this Host.  If none are defined,
+     * a zero length array is returned.
+     */
+    public String[] findAliases();
+
+
+    /**
+     * Return the Context that would be used to process the specified
+     * host-relative request URI, if any; otherwise return <code>null</code>.
+     *
+     * @param uri Request URI to be mapped
+     */
+    public Context map(String uri);
+
+
+    /**
+     * Remove the specified alias name from the aliases for this Host.
+     *
+     * @param alias Alias name to be removed
+     */
+    public void removeAlias(String alias);
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/HttpRequest.java b/appserver/web/web-core/src/main/java/org/apache/catalina/HttpRequest.java
new file mode 100644
index 0000000..589ab1c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/HttpRequest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import java.security.Principal;
+import java.util.Locale;
+import javax.servlet.http.Cookie;
+
+import org.glassfish.grizzly.http.util.DataChunk;
+
+/**
+ * An <b>HttpRequest</b> is the Catalina internal facade for an
+ * <code>HttpServletRequest</code> that is to be processed, in order to
+ * produce the corresponding <code>HttpResponse</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2007/05/05 05:31:51 $
+ */
+
+public interface HttpRequest extends Request {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a Cookie to the set of Cookies associated with this Request.
+     *
+     * @param cookie The new cookie
+     */
+    void addCookie(Cookie cookie);
+
+
+    /**
+     * Add a Header to the set of Headers associated with this Request.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    void addHeader(String name, String value);
+
+
+    /**
+     * 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
+     */
+    void addLocale(Locale 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
+     */
+    void addParameter(String name, String values[]);
+
+
+    /**
+     * Clear the collection of Cookies associated with this Request.
+     */
+    void clearCookies();
+
+
+    /**
+     * Clear the collection of Headers associated with this Request.
+     */
+    void clearHeaders();
+
+
+    /**
+     * Clear the collection of Locales associated with this Request.
+     */
+    void clearLocales();
+
+
+    /**
+     * Clear the collection of parameters associated with this Request.
+     */
+    void clearParameters();
+
+
+    void replayPayload(byte[] payloadByteArray);
+
+
+    /**
+     * 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
+     */
+    void setAuthType(String type);
+
+
+    /**
+     * Set the HTTP request method used for this Request.
+     *
+     * @param method The request method
+     */
+    void setMethod(String method);
+
+
+    /**
+     * 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
+     */
+    void setQueryString(String query);
+
+
+    /**
+     * 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
+     */
+    void setPathInfo(String path);
+
+
+    /**
+     * Get the request path.
+     * 
+     * @return the request path
+     */
+    DataChunk getRequestPathMB();
+
+
+    /**
+     * 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
+     */
+    void setRequestedSessionCookie(boolean 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
+     */
+    void setRequestedSessionId(String 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
+     */
+    void setRequestedSessionURL(boolean 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
+     */
+    void setRequestURI(String uri);
+
+
+    /**
+     * Get the decoded request URI.
+     * 
+     * @return the URL decoded request URI
+     */
+    String getDecodedRequestURI();
+
+
+    /**
+     * 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
+     */
+    void setServletPath(String 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
+     */
+    void setUserPrincipal(Principal principal);
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/HttpResponse.java b/appserver/web/web-core/src/main/java/org/apache/catalina/HttpResponse.java
new file mode 100644
index 0000000..333a017
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/HttpResponse.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import javax.servlet.http.Cookie;
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * An <b>HttpResponse</b> is the Catalina-internal facade for an
+ * <code>HttpServletResponse</code> that is to be produced,
+ * based on the processing of a corresponding <code>HttpRequest</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:16 $
+ */
+
+public interface HttpResponse
+    extends Response {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * 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 {@link #getHeaders(String)} to
+     * retrieve all of them.
+     *
+     * @param name Header name to look up
+     */
+    public String getHeader(String name);
+
+
+    /**
+     * @return a (possibly empty) <code>Collection</code> of the names
+     * of the headers of this response
+     */
+    public Collection<String> getHeaderNames();
+
+
+    /**
+     * @param name the name of the response header whose values to return
+     *
+     * @return a (possibly empty) <code>Collection</code> of the values
+     * of the response header with the given name
+     */
+    public Collection<String> getHeaders(String name);
+
+
+    /**
+     * Special method for adding a session cookie as we should be overriding 
+     * any previous 
+     * @param cookie
+     */
+    public void addSessionCookieInternal(final Cookie cookie);
+
+
+    /**
+     * Return the error message that was set with <code>sendError()</code>
+     * for this Response.
+     */
+    public String getMessage();
+
+
+    /**
+     * Return the HTTP status code associated with this Response.
+     */
+    public int 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);
+
+
+    /**
+     * Send an acknowledgment of a request.   An acknowledgment in this
+     * case is simply an HTTP response status line, i.e.
+     * <code>HTTP/1.1 [STATUS] [REASON-PHRASE]<code>.
+     *
+     * @exception java.io.IOException if an input/output error occurs
+     */
+    public void sendAcknowledgement() throws IOException;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/InstanceEvent.java b/appserver/web/web-core/src/main/java/org/apache/catalina/InstanceEvent.java
new file mode 100644
index 0000000..0b4e6e4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/InstanceEvent.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.util.EventObject;
+
+
+/**
+ * General event for notifying listeners of significant events related to
+ * a specific instance of a Servlet, or a specific instance of a Filter,
+ * as opposed to the Wrapper component that manages it.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:17 $
+ */
+
+public final class InstanceEvent
+    extends EventObject {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+    public enum EventType {
+
+        /**
+         * The event indicating that the <code>init()</code> method is about
+         * to be called for this instance.
+         */
+        BEFORE_INIT_EVENT("beforeInit"),
+
+
+        /**
+         * The event indicating that the <code>init()</code> method has returned.
+         */
+        AFTER_INIT_EVENT("afterInit"),
+
+
+        /**
+         * The event indicating that the <code>service()</code> method is about
+         * to be called on a servlet.  The <code>servlet</code> property contains
+         * the servlet being called, and the <code>request</code> and
+         * <code>response</code> properties contain the current request and
+         * response being processed.
+         */
+        BEFORE_SERVICE_EVENT("beforeService"),
+
+
+        /**
+         * The event indicating that the <code>service()</code> method has
+         * returned.  The <code>servlet</code> property contains the servlet
+         * that was called, and the <code>request</code> and
+         * <code>response</code> properties contain the current request and
+         * response being processed.
+         */
+        AFTER_SERVICE_EVENT("afterService"),
+
+
+        /**
+         * The event indicating that the <code>destroy</code> method is about
+         * to be called for this instance.
+         */
+        BEFORE_DESTROY_EVENT("beforeDestroy"),
+
+
+        /**
+         * The event indicating that the <code>destroy()</code> method has
+         * returned.
+         */
+        AFTER_DESTROY_EVENT("afterDestroy"),
+
+
+        /**
+         * The event indicating that the <code>service()</code> method of a
+         * servlet accessed via a request dispatcher is about to be called.
+         * The <code>servlet</code> property contains a reference to the
+         * dispatched-to servlet instance, and the <code>request</code> and
+         * <code>response</code> properties contain the current request and
+         * response being processed.  The <code>wrapper</code> property will
+         * contain a reference to the dispatched-to Wrapper.
+         */
+        BEFORE_DISPATCH_EVENT("beforeDispatch"),
+
+
+        /**
+         * The event indicating that the <code>service()</code> method of a
+         * servlet accessed via a request dispatcher has returned.  The
+         * <code>servlet</code> property contains a reference to the
+         * dispatched-to servlet instance, and the <code>request</code> and
+         * <code>response</code> properties contain the current request and
+         * response being processed.  The <code>wrapper</code> property will
+         * contain a reference to the dispatched-to Wrapper.
+         */
+        AFTER_DISPATCH_EVENT("afterDispatch"),
+
+
+        /**
+         * The event indicating that the <code>doFilter()</code> method of a
+         * Filter is about to be called.  The <code>filter</code> property
+         * contains a reference to the relevant filter instance, and the
+         * <code>request</code> and <code>response</code> properties contain
+         * the current request and response being processed.
+         */
+        BEFORE_FILTER_EVENT("beforeFilter"),
+
+
+        /**
+         * The event indicating that the <code>doFilter()</code> method of a
+         * Filter has returned.  The <code>filter</code> property contains
+         * a reference to the relevant filter instance, and the
+         * <code>request</code> and <code>response</code> properties contain
+         * the current request and response being processed.
+         */
+        AFTER_FILTER_EVENT("afterFilter");
+
+        public final String value;
+        public final boolean isBefore;
+
+        EventType(String value) {
+            this.value = value;
+            isBefore = value.startsWith("before");
+        }
+
+
+    }
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for filter lifecycle events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param filter Filter instance for which this event occurred
+     * @param type Event type (required)
+     */
+    public InstanceEvent(Wrapper wrapper, Filter filter, EventType type) {
+
+      super(wrapper);
+      this.filter = filter;
+      this.servlet = null;
+      this.type = type;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for filter lifecycle events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param filter Filter instance for which this event occurred
+     * @param type Event type (required)
+     * @param exception Exception that occurred
+     */
+    public InstanceEvent(Wrapper wrapper, Filter filter, EventType type,
+                         Throwable exception) {
+
+      super(wrapper);
+      this.filter = filter;
+      this.servlet = null;
+      this.type = type;
+      this.exception = exception;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for filter processing events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param filter Filter instance for which this event occurred
+     * @param type Event type (required)
+     * @param request Servlet request we are processing
+     * @param response Servlet response we are processing
+     */
+    public InstanceEvent(Wrapper wrapper, Filter filter, EventType type,
+                         ServletRequest request, ServletResponse response) {
+
+      super(wrapper);
+      this.filter = filter;
+      this.servlet = null;
+      this.type = type;
+      this.request = request;
+      this.response = response;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for filter processing events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param filter Filter instance for which this event occurred
+     * @param type Event type (required)
+     * @param request Servlet request we are processing
+     * @param response Servlet response we are processing
+     * @param exception Exception that occurred
+     */
+    public InstanceEvent(Wrapper wrapper, Filter filter, EventType type,
+                         ServletRequest request, ServletResponse response,
+                         Throwable exception) {
+
+      super(wrapper);
+      this.filter = filter;
+      this.servlet = null;
+      this.type = type;
+      this.request = request;
+      this.response = response;
+      this.exception = exception;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for processing servlet lifecycle events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param servlet Servlet instance for which this event occurred
+     * @param type Event type (required)
+     */
+    public InstanceEvent(Wrapper wrapper, Servlet servlet, EventType type) {
+
+      super(wrapper);
+      this.filter = null;
+      this.servlet = servlet;
+      this.type = type;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for processing servlet lifecycle events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param servlet Servlet instance for which this event occurred
+     * @param type Event type (required)
+     * @param exception Exception that occurred
+     */
+    public InstanceEvent(Wrapper wrapper, Servlet servlet, EventType type,
+                         Throwable exception) {
+
+      super(wrapper);
+      this.filter = null;
+      this.servlet = servlet;
+      this.type = type;
+      this.exception = exception;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for processing servlet processing events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param servlet Servlet instance for which this event occurred
+     * @param type Event type (required)
+     * @param request Servlet request we are processing
+     * @param response Servlet response we are processing
+     */
+    public InstanceEvent(Wrapper wrapper, Servlet servlet, EventType type,
+                         ServletRequest request, ServletResponse response) {
+
+      super(wrapper);
+      this.filter = null;
+      this.servlet = servlet;
+      this.type = type;
+      this.request = request;
+      this.response = response;
+
+    }
+
+
+    /**
+     * Construct a new InstanceEvent with the specified parameters.  This
+     * constructor is used for processing servlet processing events.
+     *
+     * @param wrapper Wrapper managing this servlet instance
+     * @param servlet Servlet instance for which this event occurred
+     * @param type Event type (required)
+     * @param request Servlet request we are processing
+     * @param response Servlet response we are processing
+     * @param exception Exception that occurred
+     */
+    public InstanceEvent(Wrapper wrapper, Servlet servlet, EventType type,
+                         ServletRequest request, ServletResponse response,
+                         Throwable exception) {
+
+      super(wrapper);
+      this.filter = null;
+      this.servlet = servlet;
+      this.type = type;
+      this.request = request;
+      this.response = response;
+      this.exception = exception;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The exception that was thrown during the processing being reported
+     * by this event (AFTER_INIT_EVENT, AFTER_SERVICE_EVENT, 
+     * AFTER_DESTROY_EVENT, AFTER_DISPATCH_EVENT, and AFTER_FILTER_EVENT only).
+     */
+    private Throwable exception = null;
+
+
+    /**
+     * The Filter instance for which this event occurred (BEFORE_FILTER_EVENT
+     * and AFTER_FILTER_EVENT only).
+     */
+    private transient Filter filter = null;
+
+
+    /**
+     * The servlet request being processed (BEFORE_FILTER_EVENT,
+     * AFTER_FILTER_EVENT, BEFORE_SERVICE_EVENT, and AFTER_SERVICE_EVENT).
+     */
+    private transient ServletRequest request = null;
+
+
+    /**
+     * The servlet response being processed (BEFORE_FILTER_EVENT,
+     * AFTER_FILTER_EVENT, BEFORE_SERVICE_EVENT, and AFTER_SERVICE_EVENT).
+     */
+    private transient ServletResponse response = null;
+
+
+    /**
+     * The Servlet instance for which this event occurred (not present on
+     * BEFORE_FILTER_EVENT or AFTER_FILTER_EVENT events).
+     */
+    private transient Servlet servlet = null;
+
+
+    /**
+     * The event type this instance represents.
+     */
+    private EventType type = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the exception that occurred during the processing
+     * that was reported by this event.
+     */
+    public Throwable getException() {
+
+        return (this.exception);
+
+    }
+
+
+    /**
+     * Return the filter instance for which this event occurred.
+     */
+    public Filter getFilter() {
+
+        return (this.filter);
+
+    }
+
+
+    /**
+     * Return the servlet request for which this event occurred.
+     */
+    public ServletRequest getRequest() {
+
+        return (this.request);
+
+    }
+
+
+    /**
+     * Return the servlet response for which this event occurred.
+     */
+    public ServletResponse getResponse() {
+
+        return (this.response);
+
+    }
+
+
+    /**
+     * Return the servlet instance for which this event occurred.
+     */
+    public Servlet getServlet() {
+
+        return (this.servlet);
+
+    }
+
+
+    /**
+     * Return the event type of this event.
+     */
+    public EventType getType() {
+
+        return (this.type);
+
+    }
+
+
+    /**
+     * Return the Wrapper managing the servlet instance for which this
+     * event occurred.
+     */
+    public Wrapper getWrapper() {
+
+        return (Wrapper)getSource();
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/InstanceListener.java b/appserver/web/web-core/src/main/java/org/apache/catalina/InstanceListener.java
new file mode 100644
index 0000000..292e2fc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/InstanceListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import org.jvnet.hk2.annotations.Contract;
+
+
+/**
+ * Interface defining a listener for significant events related to a
+ * specific servlet instance, rather than to the {@link Wrapper} component that
+ * is managing that instance.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:17 $
+ */
+@Contract
+public interface InstanceListener {
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event InstanceEvent that has occurred
+     */
+    public void instanceEvent(InstanceEvent event);
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Lifecycle.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Lifecycle.java
new file mode 100644
index 0000000..6ef17e3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Lifecycle.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import java.util.List;
+
+/**
+ * Common interface for component life cycle methods.  Catalina components
+ * may, but are not required to, implement this interface (as well as the
+ * appropriate interface(s) for the functionality they support) in order to
+ * provide a consistent mechanism to start and stop the component.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.1.2.1 $ $Date: 2007/08/17 15:46:26 $
+ */
+
+public interface Lifecycle {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    // START GlassFish 2439
+    /**
+     * The LifecycleEvent type for the "component init" event.
+     */
+    String INIT_EVENT = "init";
+    // END GlassFish 2439
+
+
+    /**
+     * The LifecycleEvent type for the "component start" event.
+     */
+    String START_EVENT = "start";
+
+
+    /**
+     * The LifecycleEvent type for the "component before start" event.
+     */
+    String BEFORE_START_EVENT = "before_start";
+
+
+    /**
+     * The LifecycleEvent type for the "component after start" event.
+     */
+    String AFTER_START_EVENT = "after_start";
+
+
+    /**
+     * The LifecycleEvent type for the "component stop" event.
+     */
+    String STOP_EVENT = "stop";
+
+
+    /**
+     * The LifecycleEvent type for the "component before stop" event.
+     */
+    String BEFORE_STOP_EVENT = "before_stop";
+
+
+    /**
+     * The LifecycleEvent type for the "component after stop" event.
+     */
+    String AFTER_STOP_EVENT = "after_stop";
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a LifecycleEvent listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    void addLifecycleListener(LifecycleListener listener);
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this Lifecycle instance.
+     */
+    List<LifecycleListener> findLifecycleListeners();
+
+
+    /**
+     * Remove a LifecycleEvent listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    void removeLifecycleListener(LifecycleListener listener);
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called before any of the public
+     * methods of this component are utilized.  It should also send a
+     * LifecycleEvent of type START_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    void start() throws LifecycleException;
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.  It should also send a LifecycleEvent
+     * of type STOP_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    void stop() throws LifecycleException;
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleEvent.java b/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleEvent.java
new file mode 100644
index 0000000..271f4c0
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleEvent.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import java.util.EventObject;
+
+
+/**
+ * General event for notifying listeners of significant changes on a component
+ * that implements the Lifecycle interface.  In particular, this will be useful
+ * on Containers, where these events replace the ContextInterceptor concept in
+ * Tomcat 3.x.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.1.2.1 $ $Date: 2007/08/17 15:46:26 $
+ */
+
+public final class LifecycleEvent
+    extends EventObject {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new LifecycleEvent with the specified parameters.
+     *
+     * @param lifecycle Component on which this event occurred
+     * @param type Event type (required)
+     */
+    public LifecycleEvent(Lifecycle lifecycle, String type) {
+
+        this(lifecycle, type, null);
+
+    }
+
+
+    /**
+     * Construct a new LifecycleEvent with the specified parameters.
+     *
+     * @param lifecycle Component on which this event occurred
+     * @param type Event type (required)
+     * @param data Event data (if any)
+     */
+    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
+
+        super(lifecycle);
+        this.lifecycle = lifecycle;
+        this.type = type;
+        this.data = data;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The event data associated with this event.
+     */
+    private Object data = null;
+
+
+    /**
+     * The Lifecycle on which this event occurred.
+     */
+    private transient Lifecycle lifecycle = null;
+
+
+    /**
+     * The event type this instance represents.
+     */
+    private String type = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the event data of this event.
+     */
+    public Object getData() {
+
+        return (this.data);
+
+    }
+
+
+    /**
+     * Return the Lifecycle on which this event occurred.
+     */
+    public Lifecycle getLifecycle() {
+
+        return (this.lifecycle);
+
+    }
+
+
+    /**
+     * Return the event type of this event.
+     */
+    public String getType() {
+
+        return (this.type);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleException.java b/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleException.java
new file mode 100644
index 0000000..00c8b65
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleException.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+/**
+ * General purpose exception that is thrown to indicate a lifecycle related
+ * problem.  Such exceptions should generally be considered fatal to the
+ * operation of the application containing this component.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.1.2.1 $ $Date: 2007/08/17 15:46:26 $
+ */
+
+public final class LifecycleException extends Exception {
+
+
+    //------------------------------------------------------------ Constructors
+
+
+    /**
+     * Construct a new LifecycleException with no other information.
+     */
+    public LifecycleException() {
+        super();
+    }
+
+
+    /**
+     * Construct a new LifecycleException for the specified message.
+     *
+     * @param message Message describing this exception
+     */
+    public LifecycleException(String message) {
+        super(message);
+    }
+
+
+    /**
+     * Construct a new LifecycleException for the specified throwable.
+     *
+     * @param throwable Throwable that caused this exception
+     */
+    public LifecycleException(Throwable throwable) {
+        super(throwable);
+    }
+
+
+    /**
+     * Construct a new LifecycleException for the specified message
+     * and throwable.
+     *
+     * @param message Message describing this exception
+     * @param throwable Throwable that caused this exception
+     */
+    public LifecycleException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleListener.java b/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleListener.java
new file mode 100644
index 0000000..bb98358
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/LifecycleListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+/**
+ * Interface defining a listener for significant events (including "component
+ * start" and "component stop" generated by a component that implements the
+ * Lifecycle interface.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.1.2.1 $ $Date: 2007/08/17 15:46:26 $
+ */
+
+public interface LifecycleListener {
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event LifecycleEvent that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event)
+        throws LifecycleException;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Loader.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Loader.java
new file mode 100644
index 0000000..701ae39
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Loader.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import java.beans.PropertyChangeListener;
+
+
+/**
+ * A <b>Loader</b> represents a Java ClassLoader implementation that can
+ * be used by a Container to load class files (within a repository associated
+ * with the Loader) that are designed to be reloaded upon request, as well as
+ * a mechanism to detect whether changes have occurred in the underlying
+ * repository.
+ * <p>
+ * In order for a <code>Loader</code> implementation to successfully operate
+ * with a <code>Context</code> implementation that implements reloading, it
+ * must obey the following constraints:
+ * <ul>
+ * <li>Must implement <code>Lifecycle</code> so that the Context can indicate
+ *     that a new class loader is required.
+ * <li>The <code>start()</code> method must unconditionally create a new
+ *     <code>ClassLoader</code> implementation.
+ * <li>The <code>stop()</code> method must throw away its reference to the
+ *     <code>ClassLoader</code> previously utilized, so that the class loader,
+ *     all classes loaded by it, and all objects of those classes, can be
+ *     garbage collected.
+ * <li>Must allow a call to <code>stop()</code> to be followed by a call to
+ *     <code>start()</code> on the same <code>Loader</code> instance.
+ * <li>Based on a policy chosen by the implementation, must call the
+ *     <code>Context.reload()</code> method on the owning <code>Context</code>
+ *     when a change to one or more of the class files loaded by this class
+ *     loader is detected.
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:27:18 $
+ */
+
+public interface Loader {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Java class loader to be used by this Container.
+     */
+    public ClassLoader getClassLoader();
+
+
+    /**
+     * Return the Container with which this Loader has been associated.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the Container with which this Loader has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container);
+
+    
+    /**
+     * Return the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     */
+    public boolean getDelegate();
+
+
+    /**
+     * Set the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     *
+     * @param delegate The new flag
+     */
+    public void setDelegate(boolean delegate);
+
+
+    /**
+     * Return descriptive information about this Loader implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the reloadable flag for this Loader.
+     */
+    public boolean getReloadable();
+
+
+    /**
+     * Set the reloadable flag for this Loader.
+     *
+     * @param reloadable The new reloadable flag
+     */
+    public void setReloadable(boolean reloadable);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Add a new repository to the set of repositories for this class loader.
+     *
+     * @param repository Repository to be added
+     */
+    public void addRepository(String repository);
+
+
+    /**
+     * Return the set of repositories defined for this class loader.
+     * If none are defined, a zero-length array is returned.
+     */
+    public String[] findRepositories();
+
+
+    /**
+     * Has the internal repository associated with this Loader been modified,
+     * such that the loaded classes should be reloaded?
+     */
+    public boolean modified();
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+    // START PE 4985680
+    /**
+     * Adds the given package name to the list of packages that may always be
+     * overriden, regardless of whether they belong to a protected namespace
+     */
+    public void addOverridablePackage(String packageName);
+    // END PE 4985680
+
+
+    // START PWC 1.1 6314481
+    public void setIgnoreHiddenJarFiles(boolean ignoreHiddenJarFiles);
+    // END PWC 1.1 6314481
+}
+   
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/LogFacade.java b/appserver/web/web-core/src/main/java/org/apache/catalina/LogFacade.java
new file mode 100644
index 0000000..5f42885
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/LogFacade.java
@@ -0,0 +1,3609 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import org.glassfish.logging.annotation.LogMessageInfo;
+import org.glassfish.logging.annotation.LoggerInfo;
+import org.glassfish.logging.annotation.LogMessagesResourceBundle;
+import java.util.logging.Logger;
+
+/**
+ *
+ * Provides the logging facilities.
+ *
+ * @author Shing Wai Chan
+ */
+public class LogFacade {
+    @LogMessagesResourceBundle
+    private static final String SHARED_LOG_MESSAGE_RESOURCE =
+             "org.apache.catalina.core.LogMessages";
+
+    @LoggerInfo(subsystem = "WEB", description = "WEB Core Logger", publish = true)
+    private static final String WEB_CORE_LOGGER = "javax.enterprise.web.core";
+
+    private static final Logger LOGGER =
+             Logger.getLogger(WEB_CORE_LOGGER, SHARED_LOG_MESSAGE_RESOURCE);
+
+    private LogFacade() {}
+
+    public static Logger getLogger() {
+        return LOGGER;
+    }
+
+    private static final String prefix = "AS-WEB-CORE-";
+
+    @LogMessageInfo(
+            message = "Configuration error:  Must be attached to a Context",
+            level = "WARNING"
+    )
+    public static final String CONFIG_ERROR_MUST_ATTACH_TO_CONTEXT = prefix + "00001";
+
+    @LogMessageInfo(
+            message = "Authenticator[{0}]: {1}",
+            level = "INFO"
+    )
+    public static final String AUTHENTICATOR_INFO = prefix + "00002";
+
+    @LogMessageInfo(
+            message = "Exception getting debug value",
+            level = "SEVERE",
+            cause = "Could not get the method or invoke underlying method",
+            action = "Verify the existence of such method and access permission"
+    )
+    public static final String GETTING_DEBUG_VALUE_EXCEPTION = prefix + "00003";
+
+    @LogMessageInfo(
+            message = "Unexpected error forwarding or redirecting to login page",
+            level = "WARNING"
+    )
+    public static final String UNEXPECTED_ERROR_FORWARDING_TO_LOGIN_PAGE = prefix + "00004";
+
+    @LogMessageInfo(
+            message = "Started",
+            level = "INFO"
+    )
+    public static final String START_COMPONENT_INFO = prefix + "00005";
+
+    @LogMessageInfo(
+            message = "Stopped",
+            level = "INFO"
+    )
+    public static final String STOP_COMPONENT_INFO = prefix + "00006";
+
+    @LogMessageInfo(
+            message = "Process session destroyed on {0}",
+            level = "INFO"
+    )
+    public static final String PROCESS_SESSION_DESTROYED_INFO = prefix + "00007";
+
+    @LogMessageInfo(
+            message = "Process request for ''{0}''",
+            level = "INFO"
+    )
+    public static final String PROCESS_REQUEST_INFO = prefix + "00008";
+
+    @LogMessageInfo(
+            message = "Principal {0} has already been authenticated",
+            level = "INFO"
+    )
+    public static final String PRINCIPAL_BEEN_AUTHENTICATED_INFO = prefix + "00009";
+
+    @LogMessageInfo(
+            message = "Checking for SSO cookie",
+            level = "INFO"
+    )
+    public static final String CHECK_SSO_COOKIE_INFO = prefix + "00010";
+
+    @LogMessageInfo(
+            message = "SSO cookie is not present",
+            level = "INFO"
+    )
+    public static final String SSO_COOKIE_NOT_PRESENT_INFO = prefix + "00011";
+
+    @LogMessageInfo(
+            message = "Checking for cached principal for {0}",
+            level = "INFO"
+    )
+    public static final String CHECK_CACHED_PRINCIPAL_INFO = prefix + "00012";
+
+    @LogMessageInfo(
+            message = "Found cached principal {0} with auth type {1}",
+            level = "INFO"
+    )
+    public static final String FOUND_CACHED_PRINCIPAL_AUTH_TYPE_INFO = prefix + "00013";
+
+    @LogMessageInfo(
+            message = "No cached principal found, erasing SSO cookie",
+            level = "INFO"
+    )
+    public static final String NO_CACHED_PRINCIPAL_FOUND_INFO = prefix + "00014";
+
+    @LogMessageInfo(
+            message = "Associate sso id {0} with session {1}",
+            level = "INFO"
+    )
+    public static final String ASSOCIATE_SSO_WITH_SESSION_INFO = prefix + "00015";
+
+    @LogMessageInfo(
+            message = "Registering sso id {0} for user {1} with auth type {2}",
+            level = "INFO"
+    )
+    public static final String REGISTERING_SSO_INFO = prefix + "00016";
+
+    @LogMessageInfo(
+            message = "Looking up certificates",
+            level = "INFO"
+    )
+    public static final String LOOK_UP_CERTIFICATE_INFO = prefix + "00017";
+
+    @LogMessageInfo(
+            message = "No certificates included with this request",
+            level = "INFO"
+    )
+    public static final String NO_CERTIFICATE_INCLUDED_INFO = prefix + "00018";
+
+    @LogMessageInfo(
+            message = "No client certificate chain in this request",
+            level = "WARNING"
+    )
+    public static final String NO_CLIENT_CERTIFICATE_CHAIN = prefix + "00019";
+
+    @LogMessageInfo(
+            message = "Cannot authenticate with the provided credentials",
+            level = "WARNING"
+    )
+    public static final String CANNOT_AUTHENTICATE_WITH_CREDENTIALS = prefix + "00020";
+
+    @LogMessageInfo(
+            message = "Unable to determine target of zero-arg dispatcher",
+            level = "WARNING"
+    )
+    public static final String UNABLE_DETERMINE_TARGET_OF_DISPATCHER = prefix + "00021";
+
+    @LogMessageInfo(
+            message = "Unable to acquire RequestDispatcher for {0}",
+            level = "WARNING"
+    )
+    public static final String UNABLE_ACQUIRE_REQUEST_DISPATCHER = prefix + "00022";
+
+    @LogMessageInfo(
+            message = "Unable to acquire RequestDispatcher for {0} in servlet context {1}",
+            level = "WARNING"
+    )
+    public static final String UNABLE_ACQUIRE_REQUEST_DISPATCHER_IN_SERVLET_CONTEXT = prefix + "00023";
+
+    @LogMessageInfo(
+            message = "Error invoking AsyncListener",
+            level = "WARNING"
+    )
+    public static final String ERROR_INVOKE_ASYNCLISTENER = prefix + "00024";
+
+    @LogMessageInfo(
+            message = "Asynchronous dispatch already in progress, must call ServletRequest.startAsync first",
+            level = "WARNING"
+    )
+    public static final String ASYNC_DISPATCH_ALREADY_IN_PROGRESS_EXCEPTION = prefix + "00025";
+
+    @LogMessageInfo(
+            message = "Must not call AsyncContext.addListener after the container-initiated dispatch during which ServletRequest.startAsync was called has returned to the container",
+            level = "WARNING"
+    )
+    public static final String ASYNC_CONTEXT_ADD_LISTENER_EXCEPTION = prefix + "00026";
+
+    @LogMessageInfo(
+            message = "Must not call AsyncContext.setTimeout after the container-initiated dispatch during which ServletRequest.startAsync was called has returned to the container",
+            level = "WARNING"
+    )
+    public static final String ASYNC_CONTEXT_SET_TIMEOUT_EXCEPTION = prefix + "00027";
+
+    @LogMessageInfo(
+            message = "The connector has already been initialized",
+            level = "INFO"
+    )
+    public static final String CONNECTOR_BEEN_INIT = prefix + "00028";
+
+    @LogMessageInfo(
+            message = "Error registering connector ",
+            level = "SEVERE",
+            cause = "Could not register connector",
+            action = "Verify domain name and type"
+    )
+    public static final String ERROR_REGISTER_CONNECTOR_EXCEPTION = prefix + "00029";
+
+    @LogMessageInfo(
+            message = "Failed to instanciate HttpHandler ",
+            level = "WARNING"
+    )
+    public static final String FAILED_INSTANCIATE_HTTP_HANDLER_EXCEPTION = prefix + "00030";
+
+    @LogMessageInfo(
+            message = "mod_jk invalid Adapter implementation: {0} ",
+            level = "WARNING"
+    )
+    public static final String INVALID_ADAPTER_IMPLEMENTATION_EXCEPTION = prefix + "00031";
+
+    @LogMessageInfo(
+            message = "Protocol handler instantiation failed: {0}",
+            level = "WARNING"
+    )
+    public static final String PROTOCOL_HANDLER_INIT_FAILED_EXCEPTION = prefix + "00032";
+
+    @LogMessageInfo(
+            message = "The connector has already been started",
+            level = "INFO"
+    )
+    public static final String CONNECTOR_BEEN_STARTED = prefix + "00033";
+
+    @LogMessageInfo(
+            message = "Protocol handler start failed: {0}",
+            level = "WARNING"
+    )
+    public static final String PROTOCOL_HANDLER_START_FAILED_EXCEPTION = prefix + "00034";
+
+    @LogMessageInfo(
+            message = "Coyote connector has not been started",
+            level = "SEVERE",
+            cause = "Could not stop processing requests via this Connector",
+            action = "Verify if the connector has not been started"
+    )
+    public static final String CONNECTOR_NOT_BEEN_STARTED = prefix + "00035";
+
+    @LogMessageInfo(
+            message = "Protocol handler destroy failed: {0}",
+            level = "WARNING"
+    )
+    public static final String PROTOCOL_HANDLER_DESTROY_FAILED_EXCEPTION = prefix + "00036";
+
+    @LogMessageInfo(
+            message = "An exception or error occurred in the container during the request processing",
+            level = "SEVERE",
+            cause = "Could not process the request in the container",
+            action = "Verify certificate chain retrieved from the request header and the correctness of request"
+    )
+    public static final String REQUEST_PROCESSING_EXCEPTION = prefix + "00037";
+
+    @LogMessageInfo(
+            message = "HTTP listener on port {0} has been disabled",
+            level = "FINE"
+    )
+    public static final String HTTP_LISTENER_DISABLED = prefix + "00038";
+
+    @LogMessageInfo(
+            message = "Error parsing client cert chain into array of java.security.cert.X509Certificate instances",
+            level = "SEVERE",
+            cause = "Could not get the SSL client certificate chain",
+            action = "Verify certificate chain and the request"
+    )
+    public static final String PARSING_CLIENT_CERT_EXCEPTION = prefix + "00039";
+
+    @LogMessageInfo(
+            message = "No Host matches server name {0}",
+            level = "INFO"
+    )
+    public static final String NO_HOST_MATCHES_SERVER_NAME_INFO = prefix + "00040";
+
+    @LogMessageInfo(
+            message = "Cannot use this object outside a servlet's service method or outside a filter's doFilter method",
+            level = "WARNING"
+    )
+    public static final String OBJECT_INVALID_SCOPE_EXCEPTION = prefix + "00041";
+
+    @LogMessageInfo(
+            message = "Cannot set a null ReadListener object",
+            level = "WARNING"
+    )
+    public static final String NULL_READ_LISTENER_EXCEPTION = prefix + "00042";
+
+    @LogMessageInfo(
+            message = "Cannot set a null WriteListener object",
+            level = "WARNING"
+    )
+    public static final String NULL_WRITE_LISTENER_EXCEPTION = prefix + "00043";
+
+    @LogMessageInfo(
+            message = "Failed to skip {0} characters in the underlying buffer of CoyoteReader on readLine().",
+            level = "WARNING"
+    )
+    public static final String FAILED_SKIP_CHARS_IN_BUFFER = prefix + "00044";
+
+    @LogMessageInfo(
+            message = "Stream closed",
+            level = "WARNING"
+    )
+    public static final String STREAM_CLOSED = prefix + "00045";
+
+    @LogMessageInfo(
+            message = "Already set read listener",
+            level = "WARNING"
+    )
+    public static final String ALREADY_SET_READ_LISTENER = prefix + "00046";
+
+    @LogMessageInfo(
+            message = "Cannot set ReaderListener for non-async or non-upgrade request",
+            level = "WARNING"
+    )
+    public static final String NON_ASYNC_UPGRADE_READER_EXCEPTION = prefix + "00047";
+
+    @LogMessageInfo(
+            message = "Error in invoking ReadListener.onDataAvailable",
+            level = "WARNING"
+     )
+    public static final String READ_LISTENER_ON_DATA_AVAILABLE_ERROR = prefix + "00048";
+
+    @LogMessageInfo(
+            message = "The WriteListener has already been set.",
+            level = "WARNING"
+    )
+    public static final String WRITE_LISTENER_BEEN_SET = prefix + "00049";
+
+    @LogMessageInfo(
+            message = "Cannot set WriteListener for non-async or non-upgrade request",
+            level = "WARNING"
+    )
+    public static final String NON_ASYNC_UPGRADE_WRITER_EXCEPTION = prefix + "00050";
+
+    @LogMessageInfo(
+            message = "Error in invoking WriteListener.onWritePossible",
+            level = "WARNING"
+    )
+    public static final String WRITE_LISTENER_ON_WRITE_POSSIBLE_ERROR = prefix + "00051";
+
+    @LogMessageInfo(
+            message = "getReader() has already been called for this request",
+            level = "WARNING"
+    )
+    public static final String GETREADER_BEEN_CALLED_EXCEPTION = prefix + "00052";
+
+    @LogMessageInfo(
+            message = "getInputStream() has already been called for this request",
+            level = "WARNING"
+    )
+    public static final String GETINPUTSTREAM_BEEN_CALLED_EXCEPTION = prefix + "00053";
+
+    @LogMessageInfo(
+            message = "Unable to determine client remote address from proxy (returns null)",
+            level = "WARNING"
+    )
+    public static final String UNABLE_DETERMINE_CLIENT_ADDRESS = prefix + "00054";
+
+    @LogMessageInfo(
+            message = "Unable to resolve IP address {0} into host name",
+            level = "WARNING"
+    )
+    public static final String UNABLE_RESOLVE_IP_EXCEPTION = prefix + "00055";
+
+    @LogMessageInfo(
+            message = "Exception thrown by attributes event listener",
+            level = "WARNING"
+    )
+    public static final String ATTRIBUTE_EVENT_LISTENER_EXCEPTION = prefix + "00056";
+
+    @LogMessageInfo(
+            message = "Cannot call setAttribute with a null name",
+            level ="WARNING"
+    )
+    public static final String NULL_ATTRIBUTE_NAME_EXCEPTION = prefix + "00057";
+
+    @LogMessageInfo(
+            message = "Unable to determine canonical name of file [{0}] specified for use with sendfile",
+            level = "WARNING"
+    )
+    public static final String UNABLE_DETERMINE_CANONICAL_NAME = prefix + "00058";
+
+    @LogMessageInfo(
+            message = "Unable to set request character encoding to {0} from context {1}, because request parameters have already been read, or ServletRequest.getReader() has already been called",
+            level = "WARNING"
+    )
+    public static final String UNABLE_SET_REQUEST_CHARS = prefix + "00059";
+
+    @LogMessageInfo(
+            message = "Attempt to re-login while the user identity already exists",
+            level = "SEVERE",
+            cause = "Could not re-login",
+            action = "Verify if user has already login"
+    )
+    public static final String ATTEMPT_RELOGIN_EXCEPTION = prefix + "00060";
+
+    @LogMessageInfo(
+            message = "changeSessionId has been called without a session",
+            level = "WARNING"
+    )
+    public static final String CHANGE_SESSION_ID_BEEN_CALLED_EXCEPTION = prefix + "00061";
+
+    @LogMessageInfo(
+            message = "Cannot create a session after the response has been committed",
+            level = "WARNING"
+    )
+    public static final String CANNOT_CREATE_SESSION_EXCEPTION = prefix + "00062";
+
+    @LogMessageInfo(
+            message = "Invalid URI encoding; using HTTP default",
+            level = "SEVERE",
+            cause = "Could not set URI converter",
+            action = "Verify URI encoding, using HTTP default"
+    )
+    public static final String INVALID_URI_ENCODING = prefix + "00063";
+
+    @LogMessageInfo(
+            message = "Invalid URI character encoding; trying ascii",
+            level = "SEVERE",
+            cause = "Could not encode URI character",
+            action = "Verify URI encoding, trying ascii"
+    )
+    public static final String INVALID_URI_CHAR_ENCODING = prefix + "00064";
+
+    @LogMessageInfo(
+            message = "Request is within the scope of a filter or servlet that does not support asynchronous operations",
+            level = "WARNING"
+    )
+    public static final String REQUEST_WITHIN_SCOPE_OF_FILTER_OR_SERVLET_EXCEPTION = prefix + "00065";
+
+    @LogMessageInfo(
+            message = "ServletRequest.startAsync called again without any asynchronous dispatch, or called outside the scope of any such dispatch, or called again within the scope of the same dispatch",
+            level = "WARNING"
+    )
+    public static final String START_ASYNC_CALLED_AGAIN_EXCEPTION = prefix + "00066";
+
+    @LogMessageInfo(
+            message = "Response already closed",
+            level = "WARNING"
+    )
+    public static final String ASYNC_ALREADY_COMPLETE_EXCEPTION = prefix + "00067";
+
+    @LogMessageInfo(
+            message = "ServletRequest.startAsync called outside the scope of an async dispatch",
+            level = "WARNING"
+    )
+    public static final String START_ASYNC_CALLED_OUTSIDE_SCOPE_EXCEPTION = prefix + "00068";
+
+    @LogMessageInfo(
+            message = "The request has not been put into asynchronous mode, must call ServletRequest.startAsync first",
+            level = "WARNING"
+    )
+    public static final String REQUEST_NOT_PUT_INTO_ASYNC_MODE_EXCEPTION = prefix + "00069";
+
+    @LogMessageInfo(
+            message = "Request already released from asynchronous mode",
+            level = "WARNING"
+    )
+    public static final String REQUEST_ALREADY_RELEASED_EXCEPTION = prefix + "00070";
+
+    @LogMessageInfo(
+            message = "Unable to perform error dispatch",
+            level = "SEVERE",
+            cause = "Could not perform post-request processing as required by this Valve",
+            action = "Verify if I/O exception or servlet exception occur"
+    )
+    public static final String UNABLE_PERFORM_ERROR_DISPATCH = prefix + "00071";
+
+    @LogMessageInfo(
+            message = "Request.{0} is called without multipart configuration. Either add a @MultipartConfig to the servlet, or a multipart-config element to web.xml",
+            level = "WARNING"
+    )
+    public static final String REQUEST_CALLED_WITHOUT_MULTIPART_CONFIG_EXCEPTION = prefix + "00072";
+
+    @LogMessageInfo(
+            message = "This should not happen-breaking background lock: sess = {0}",
+            level = "WARNING"
+    )
+    public static final String BREAKING_BACKGROUND_LOCK_EXCEPTION = prefix + "00073";
+
+    @LogMessageInfo(
+            message = " Must not use request object outside the scope of a servlet's service or a filter's doFilter method",
+            level = "WARNING"
+    )
+    public static final String CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION = prefix + "00074";
+
+    @LogMessageInfo(
+            message = "Error during finishResponse",
+            level = "WARNING"
+    )
+    public static final String ERROR_DURING_FINISH_RESPONSE = prefix + "00075";
+
+    @LogMessageInfo(
+            message = "getWriter() has already been called for this response",
+            level = "WARNING"
+    )
+    public static final String GET_WRITER_BEEN_CALLED_EXCEPTION = prefix + "00076";
+
+    @LogMessageInfo(
+            message = "getOutputStream() has already been called for this response",
+            level = "WARNING"
+    )
+    public static final String GET_OUTPUT_STREAM_BEEN_CALLED_EXCEPTION = prefix + "00077";
+
+    @LogMessageInfo(
+            message = "Cannot reset buffer after response has been committed",
+            level = "WARNING"
+    )
+    public static final String CANNOT_RESET_BUFFER_EXCEPTION = prefix + "00078";
+
+    @LogMessageInfo(
+            message = "Cannot change buffer size after data has been written",
+            level = "WARNING"
+    )
+    public static final String CANNOT_CHANGE_BUFFER_SIZE_EXCEPTION = prefix + "00079";
+
+    @LogMessageInfo(
+            message = "Cannot call sendError() after the response has been committed",
+            level = "WARNING"
+    )
+    public static final String CANNOT_CALL_SEND_ERROR_EXCEPTION = prefix + "00080";
+
+    @LogMessageInfo(
+            message = "Cannot call sendRedirect() after the response has been committed",
+            level = "WARNING"
+    )
+    public static final String CANNOT_CALL_SEND_REDIRECT_EXCEPTION = prefix + "00081";
+
+    @LogMessageInfo(
+            message = "Null response object",
+            level = "WARNING"
+    )
+    public static final String NULL_RESPONSE_OBJECT = prefix + "00082";
+
+    @LogMessageInfo(
+        message = "Not allowed to call this javax.servlet.ServletContext method from a ServletContextListener that was neither declared in the application's deployment descriptor nor annotated with WebListener",
+        level = "INFO"
+    )
+    public static final String UNSUPPORTED_OPERATION_EXCEPTION = prefix + "00083";
+
+    @LogMessageInfo(
+        message = "Exception thrown by attributes event listener",
+        level = "WARNING",
+        cause = "Could not modify attribute",
+        action = "Verify name and value from Servlet Context"
+    )
+    public static final String ATTRIBUTES_EVENT_LISTENER_EXCEPTION = prefix + "00084";
+
+    @LogMessageInfo(
+        message = "Name cannot be null",
+        level = "INFO"
+    )
+    public static final String NULL_NAME_EXCEPTION = prefix + "00085";
+
+    @LogMessageInfo(
+        message = "Cannot forward after response has been committed",
+        level = "INFO"
+    )
+    public static final String ILLEGAL_STATE_EXCEPTION = prefix + "00086";
+
+    @LogMessageInfo(
+        message = "Servlet {0} is currently unavailable",
+        level = "WARNING"
+    )
+    public static final String UNAVAILABLE_SERVLET = prefix + "00087";
+
+    @LogMessageInfo(
+        message = "Allocate exception for servlet {0}",
+        level = "SEVERE",
+        cause = "Could not allocate servlet instance",
+        action = "Verify the configuration of wrapper"
+    )
+    public static final String ALLOCATE_SERVLET_EXCEPTION = prefix + "00088";
+
+    @LogMessageInfo(
+        message = "Exceeded maximum depth for nested request dispatches: {0}",
+        level = "INFO"
+    )
+    public static final String MAX_DISPATCH_DEPTH_REACHED = prefix + "00089";
+
+    @LogMessageInfo(
+        message = "Servlet.service() for servlet {0} threw exception",
+        level = "WARNING"
+    )
+    public static final String SERVLET_SERVICE_EXCEPTION = prefix + "00090";
+
+    @LogMessageInfo(
+        message = "Release filters exception for servlet {0}",
+        level = "SEVERE",
+        cause = "Could not release filter chain",
+        action = "Verify the availability of current filter chain"
+    )
+    public static final String RELEASE_FILTERS_EXCEPTION_SEVERE = prefix + "00091";
+
+    @LogMessageInfo(
+        message = "Deallocate exception for servlet {0}",
+        level = "SEVERE",
+        cause = "Could not deallocate the allocated servlet instance",
+        action = "Verify the availability of servlet instance"
+    )
+    public static final String DEALLOCATE_SERVLET_EXCEPTION = prefix + "00092";
+
+    @LogMessageInfo(
+        message = "ApplicationDispatcher[{0}]: {1}",
+        level = "INFO"
+    )
+    public static final String APPLICATION_DISPATCHER_INFO = prefix + "00093";
+
+    @LogMessageInfo(
+        message = "ApplicationDispatcher[{0}]: {1}",
+        level = "WARNING",
+        cause = "Could not get logger from parent context",
+        action = "Verify if logger is null"
+    )
+    public static final String APPLICATION_DISPATCHER_WARNING = prefix + "00094";
+
+    @LogMessageInfo(
+            message = "Exception processing {0}",
+            level = "WARNING")
+    public static final String EXCEPTION_PROCESSING = prefix + "00095";
+
+    @LogMessageInfo(
+            message = "Exception sending default error page",
+            level = "WARNING")
+    public static final String EXCEPTION_SENDING_DEFAULT_ERROR_PAGE = prefix + "00096";
+
+    @LogMessageInfo(
+        message = "Filter execution threw an exception",
+        level = "WARNING"
+    )
+    public static final String FILTER_EXECUTION_EXCEPTION = prefix + "00097";
+
+    @LogMessageInfo(
+        message = "ApplicationFilterConfig.doAsPrivilege",
+        level = "SEVERE",
+        cause = "Could not release allocated filter instance",
+        action = "Verify the privilege"
+    )
+    public static final String DO_AS_PRIVILEGE = prefix + "00098";
+
+    @LogMessageInfo(
+        message = "ContainerBase.setLoader: stop: ",
+        level = "SEVERE",
+        cause = "Could not stop previous loader",
+        action = "Verify previous loader"
+    )
+    public static final String CONTAINER_BASE_SET_LOADER_STOP = prefix + "00099";
+
+    @LogMessageInfo(
+        message = "ContainerBase.setLoader: start:",
+        level = "SEVERE",
+        cause = "Could not start new loader",
+        action = "Verify the configuration of container"
+    )
+    public static final String CONTAINER_BASE_SET_LOADER_START = prefix + "00100";
+
+    @LogMessageInfo(
+        message = "ContainerBase.setLogger: stop: ",
+        level = "SEVERE",
+        cause = "Could not stop previous logger",
+        action = "Verify previous logger"
+    )
+    public static final String CONTAINER_BASE_SET_LOGGER_STOP = prefix + "00101";
+
+    @LogMessageInfo(
+        message = "ContainerBase.setLogger: start: ",
+        level = "SEVERE",
+        cause = "Could not start new logger",
+        action = "Verify the configuration of container"
+    )
+    public static final String CONTAINER_BASE_SET_LOGGER_START = prefix + "00102";
+
+    @LogMessageInfo(
+        message = "ContainerBase.setManager: stop: ",
+        level = "SEVERE",
+        cause = "Could not stop previous manager",
+        action = "Verify previous manager"
+    )
+    public static final String CONTAINER_BASE_SET_MANAGER_STOP = prefix + "00103";
+
+    @LogMessageInfo(
+        message = "ContainerBase.setManager: start: ",
+        level = "SEVERE",
+        cause = "Could not start new manager",
+        action = "Verify the configuration of container"
+    )
+    public static final String CONTAINER_BASE_SET_MANAGER_START = prefix + "00104";
+
+    @LogMessageInfo(
+        message = "ContainerBase.setRealm: stop: ",
+        level = "SEVERE",
+        cause = "Could not stop previous realm",
+        action = "Verify previous realm"
+    )
+    public static final String CONTAINER_BASE_SET_REALM_STOP = prefix + "00105";
+
+    @LogMessageInfo(
+        message = "ContainerBase.setRealm: start: ",
+        level = "SEVERE",
+        cause = "Could not start new realm",
+        action = "Verify the configuration of container"
+    )
+    public static final String CONTAINER_BASE_SET_REALM_START = prefix + "00106";
+
+    @LogMessageInfo(
+        message = "addChild: Child name {0} is not unique",
+        level = "WARNING"
+    )
+    public static final String DUPLICATE_CHILD_NAME_EXCEPTION = prefix + "00107";
+
+    @LogMessageInfo(
+        message = "ContainerBase.addChild: start: ",
+        level = "SEVERE",
+        cause = "Could not start new child container",
+        action = "Verify the configuration of parent container"
+    )
+    public static final String CONTAINER_BASE_ADD_CHILD_START = prefix + "00108";
+
+    @LogMessageInfo(
+        message = "ContainerBase.removeChild: stop: ",
+        level = "SEVERE",
+        cause = "Could not stop existing child container",
+        action = "Verify existing child container"
+    )
+    public static final String CONTAINER_BASE_REMOVE_CHILD_STOP = prefix + "00109";
+
+    @LogMessageInfo(
+        message = "Container {0} has already been started",
+        level = "INFO"
+    )
+    public static final String CONTAINER_STARTED = prefix + "00110";
+
+    @LogMessageInfo(
+        message = "Container {0} has not been started",
+        level = "SEVERE",
+        cause = "Current container has not been started",
+        action = "Verify the current container"
+    )
+    public static final String CONTAINER_NOT_STARTED_EXCEPTION = prefix + "00111";
+
+    @LogMessageInfo(
+        message = "Error stopping container {0}",
+        level = "SEVERE",
+        cause = "Could not stop child container",
+        action = "Verify the existence of current child container"
+    )
+    public static final String ERROR_STOPPING_CONTAINER = prefix + "00112";
+
+    @LogMessageInfo(
+        message = "Error unregistering ",
+        level = "SEVERE",
+        cause = "Could not unregister current container",
+        action = "Verify if the container has been registered"
+    )
+    public static final String ERROR_UNREGISTERING = prefix + "00113";
+
+    @LogMessageInfo(
+        message = "Exception invoking periodic operation: ",
+        level = "SEVERE",
+        cause = "Could not set the context ClassLoader",
+        action = "Verify the security permission"
+    )
+    public static final String EXCEPTION_INVOKES_PERIODIC_OP = prefix + "00114";
+
+    @LogMessageInfo(
+        message = "Unable to configure {0} for filter {1} of servlet context {2}, because this servlet context has already been initialized",
+        level = "WARNING"
+    )
+    public static final String DYNAMIC_FILTER_REGISTRATION_ALREADY_INIT = prefix + "00115";
+
+    @LogMessageInfo(
+        message = "Unable to configure {0} for servlet {1} of servlet context {2}, because this servlet context has already been initialized",
+        level = "WARNING"
+    )
+    public static final String DYNAMIC_SERVLET_REGISTRATION_ALREADY_INIT = prefix + "00116";
+
+    @LogMessageInfo(
+        message = "Unable to configure {0} for filter {1} of servlet context {2}, because this servlet context has already been initialized",
+        level = "WARNING"
+    )
+    public static final String FILTER_REGISTRATION_ALREADY_INIT = prefix + "00117";
+
+    @LogMessageInfo(
+        message = "Unable to configure mapping for filter {0} of servlet context {1}, because servlet names are null or empty",
+        level = "WARNING"
+    )
+    public static final String FILTER_REGISTRATION_MAPPING_SERVLET_NAME_EXCEPTION = prefix + "00118";
+
+    @LogMessageInfo(
+        message = "Unable to configure mapping for filter {0} of servlet context {1}, because URL patterns are null or empty",
+        level = "WARNING"
+    )
+    public static final String FILTER_REGISTRATION_MAPPING_URL_PATTERNS_EXCEPTION = prefix + "00119";
+
+    @LogMessageInfo(
+        message = "Creation of the naming context failed: {0}",
+        level = "WARNING"
+    )
+    public static final String CREATION_NAMING_CONTEXT_FAILED = prefix + "00120";
+
+    @LogMessageInfo(
+        message = "Failed to bind object: {0}",
+        level = "WARNING"
+    )
+    public static final String BIND_OBJECT_FAILED = prefix + "00121";
+
+    @LogMessageInfo(
+        message = "Environment entry {0} has an invalid type",
+        level = "WARNING"
+    )
+    public static final String ENV_ENTRY_INVALID_TYPE = prefix + "00122";
+
+    @LogMessageInfo(
+        message = "Environment entry {0} has an invalid value",
+        level = "WARNING"
+    )
+    public static final String ENV_ENTRY_INVALID_VALUE = prefix + "00123";
+
+    @LogMessageInfo(
+        message = "Failed to unbind object: {0}",
+        level = "WARNING"
+    )
+    public static final String UNBIND_OBJECT_FAILED = prefix + "00124";
+
+    @LogMessageInfo(
+        message = "Must not use request object outside the scope of a servlet's service or a filter's doFilter method",
+        level = "WARNING"
+    )
+    public static final String VALIDATE_REQUEST_EXCEPTION = prefix + "00125";
+
+    @LogMessageInfo(
+        message = "Null response object",
+        level = "WARNING"
+    )
+    public static final String VALIDATE_RESPONSE_EXCEPTION = prefix + "00126";
+
+    @LogMessageInfo(
+        message = "Unable to configure {0} for servlet {1} of servlet context {2}, because this servlet context has already been initialized",
+        level = "WARNING"
+    )
+    public static final String SERVLET_REGISTRATION_ALREADY_INIT = prefix + "00127";
+
+    @LogMessageInfo(
+        message = "Unable to configure mapping for servlet {0} of servlet context {1}, because URL patterns are null or empty",
+        level = "WARNING"
+    )
+    public static final String SERVLET_REGISTRATION_MAPPING_URL_PATTERNS_EXCEPTION = prefix + "00128";
+
+    @LogMessageInfo(
+        message = "Unable to configure {0} session tracking cookie property for servlet context {1}, because this servlet context has already been initialized",
+        level = "WARNING"
+    )
+    public static final String SESSION_COOKIE_CONFIG_ALREADY_INIT = prefix + "00129";
+
+    @LogMessageInfo(
+        message = "Missing alternate docbase URL pattern or directory location",
+        level = "WARNING"
+    )
+    public static final String MISS_PATH_OR_URL_PATTERN_EXCEPTION = prefix + "00130";
+
+    @LogMessageInfo(
+        message = "LoginConfig cannot be null",
+        level = "WARNING"
+    )
+    public static final String LOGIN_CONFIG_REQUIRED_EXCEPTION = prefix + "00131";
+
+    @LogMessageInfo(
+        message = "Form login page {0} must start with a ''/''",
+        level = "WARNING"
+    )
+    public static final String LOGIN_CONFIG_LOGIN_PAGE_EXCEPTION = prefix + "00132";
+
+    @LogMessageInfo(
+        message = "Form error page {0} must start with a ''/''",
+        level = "WARNING"
+    )
+    public static final String LOGIN_CONFIG_ERROR_PAGE_EXCEPTION = prefix + "00133";
+
+    @LogMessageInfo(
+        message = "Child of a Context must be a Wrapper",
+        level = "WARNING"
+    )
+    public static final String NO_WRAPPER_EXCEPTION = prefix + "00134";
+
+    @LogMessageInfo(
+        message = "JSP file {0} must start with a ''/''",
+        level = "WARNING"
+    )
+    public static final String WRAPPER_ERROR_EXCEPTION = prefix + "00135";
+
+    @LogMessageInfo(
+        message = "Invalid <url-pattern> {0} in security constraint",
+        level = "WARNING"
+    )
+    public static final String SECURITY_CONSTRAINT_PATTERN_EXCEPTION = prefix + "00136";
+
+    @LogMessageInfo(
+        message = "ErrorPage cannot be null",
+        level = "WARNING"
+    )
+    public static final String ERROR_PAGE_REQUIRED_EXCEPTION = prefix + "00137";
+
+    @LogMessageInfo(
+        message = "Error page location {0} must start with a ''/''",
+        level = "WARNING"
+    )
+    public static final String ERROR_PAGE_LOCATION_EXCEPTION = prefix + "00138";
+
+    @LogMessageInfo(
+        message = "Invalid status code {0} for error-page mapping. HTTP error codes are defined in the range from 400-600",
+        level = "SEVERE",
+        cause = "Invalid error page code",
+        action = "Verify the error code"
+    )
+    public static final String INVALID_ERROR_PAGE_CODE_EXCEPTION = prefix + "00139";
+
+    @LogMessageInfo(
+        message = "Filter mapping specifies an unknown filter name {0}",
+        level = "WARNING"
+    )
+    public static final String FILTER_MAPPING_NAME_EXCEPTION = prefix + "00140";
+
+    @LogMessageInfo(
+        message = "Filter mapping must specify either a <url-pattern> or a <servlet-name>",
+        level = "WARNING"
+    )
+    public static final String FILTER_MAPPING_EITHER_EXCEPTION = prefix + "00141";
+
+    @LogMessageInfo(
+        message = "Invalid <url-pattern> {0} in filter mapping",
+        level = "WARNING"
+    )
+    public static final String FILTER_MAPPING_INVALID_URL_EXCEPTION = prefix + "00142";
+
+    @LogMessageInfo(
+        message = "Unable to call method {0} on servlet context {1}, because this servlet context has already been initialized",
+        level = "WARNING"
+    )
+    public static final String SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION = prefix + "00143";
+
+    @LogMessageInfo(
+        message = "Filter name is null or an empty String",
+        level = "WARNING"
+    )
+    public static final String NULL_EMPTY_FILTER_NAME_EXCEPTION = prefix + "00144";
+
+    @LogMessageInfo(
+        message = "Unable to set {0} session tracking mode on servlet context {1}, because it is not supported",
+        level = "WARNING"
+    )
+    public static final String UNSUPPORTED_TRACKING_MODE_EXCEPTION = prefix + "00145";
+
+    @LogMessageInfo(
+        message = "Unable to add listener of type: {0}, because it does not implement any of the required ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener, HttpSessionListener, or HttpSessionAttributeListener interfaces",
+        level = "WARNING"
+    )
+    public static final String UNABLE_ADD_LISTENER_EXCEPTION = prefix + "00146";
+
+    @LogMessageInfo(
+        message = "Both parameter name and parameter value are required, parameter name is {0}",
+        level = "WARNING"
+    )
+    public static final String PARAMETER_REQUIRED_EXCEPTION = prefix + "00147";
+
+    @LogMessageInfo(
+        message = "Duplicate context initialization parameter {0}",
+        level = "WARNING"
+    )
+    public static final String DUPLICATE_PARAMETER_EXCEPTION = prefix + "00148";
+
+    @LogMessageInfo(
+        message = "Invalid <url-pattern> {0} in servlet mapping",
+        level = "WARNING"
+    )
+    public static final String SERVLET_MAPPING_INVALID_URL_EXCEPTION = prefix + "00149";
+
+    @LogMessageInfo(
+        message = "Servlet mapping specifies an unknown servlet name {0}",
+        level = "WARNING"
+    )
+    public static final String SERVLET_MAPPING_UNKNOWN_NAME_EXCEPTION = prefix + "00150";
+
+    @LogMessageInfo(
+        message = "Unable to map Servlet [{0}] to URL pattern [{1}], because Servlet [{2}] is already mapped to it",
+        level = "WARNING"
+    )
+    public static final String DUPLICATE_SERVLET_MAPPING_EXCEPTION = prefix + "00151";
+
+    @LogMessageInfo(
+        message = "Error creating instance listener {0}",
+        level = "SEVERE",
+        cause = "Could not create new instance",
+        action = "Verify the configuration of Wrapper and InstanceListener"
+    )
+    public static final String CREATING_INSTANCE_LISTENER_EXCEPTION = prefix + "00152";
+
+    @LogMessageInfo(
+        message = "Error creating lifecycle listener {0}",
+        level = "SEVERE",
+        cause = "Could not create new instance for lifecycle listener",
+        action = "Verify the permit of current class to access newInstance()"
+    )
+    public static final String CREATING_LIFECYCLE_LISTENER_EXCEPTION = prefix + "00153";
+
+    @LogMessageInfo(
+        message = "Error creating container listener {0}",
+        level = "SEVERE",
+        cause = "Could not create new instance for container listener",
+        action = "Verify the permit of current class to access newInstance()"
+    )
+    public static final String CREATING_CONTAINER_LISTENER_EXCEPTION = prefix + "00154";
+
+    @LogMessageInfo(
+        message = "Reloading this Context has started",
+        level = "INFO"
+    )
+    public static final String RELOADING_STARTED = prefix + "00155";
+
+    @LogMessageInfo(
+        message = "Error stopping context {0}",
+        level = "SEVERE",
+        cause = "Could not stop context component",
+        action = "Verify stop() to guarantee the whole domain is being stopped correctly"
+    )
+    public static final String STOPPING_CONTEXT_EXCEPTION = prefix + "00156";
+
+    @LogMessageInfo(
+        message = "Error starting context {0}",
+        level = "SEVERE",
+        cause = "Could not start context component",
+        action = "Verify start() to guarantee the context component is being started correctly"
+    )
+    public static final String STARTING_CONTEXT_EXCEPTION = prefix + "00157";
+
+    @LogMessageInfo(
+        message = "Error invoking requestInitialized method on ServletRequestListener {0}",
+        level = "WARNING"
+    )
+    public static final String REQUEST_INIT_EXCEPTION = prefix + "00158";
+
+    @LogMessageInfo(
+        message = "Error invoking requestDestroyed method on ServletRequestListener {0}",
+        level = "WARNING"
+    )
+    public static final String REQUEST_DESTROY_EXCEPTION = prefix + "00159";
+
+    @LogMessageInfo(
+        message = "Exception starting filter {0}",
+        level = "WARNING"
+    )
+    public static final String STARTING_FILTER_EXCEPTION = prefix + "00160";
+
+    @LogMessageInfo(
+        message = "Servlet with name {0} does not have any servlet-class or jsp-file configured",
+        level = "WARNING"
+    )
+    public static final String SERVLET_WITHOUT_ANY_CLASS_OR_JSP = prefix + "00161";
+
+    @LogMessageInfo(
+        message = "Filter with name {0} does not have any class configured",
+        level = "WARNING"
+    )
+    public static final String FILTER_WITHOUT_ANY_CLASS = prefix + "00162";
+
+    @LogMessageInfo(
+        message = "Exception sending context destroyed event to listener instance of class {0}",
+        level = "WARNING"
+    )
+    public static final String LISTENER_STOP_EXCEPTION = prefix + "00163";
+
+    @LogMessageInfo(
+        message = "Error starting resources in context {0}",
+        level = "SEVERE",
+        cause = "Could not get the proxy directory context",
+        action = "Verify the existence of the context"
+    )
+    public static final String STARTING_RESOURCES_EXCEPTION = prefix + "00164";
+
+    @LogMessageInfo(
+        message = "Error stopping static resources",
+        level = "SEVERE",
+        cause = "Could not deallocate resource and destroy proxy",
+        action = "Verify if a fatal error that prevents this component from being used"
+    )
+    public static final String STOPPING_RESOURCES_EXCEPTION = prefix + "00165";
+
+    @LogMessageInfo(
+        message = "Current container has already been started with a DirContext object",
+        level = "WARNING"
+    )
+    public static final String RESOURCES_STARTED = prefix + "00166";
+
+    @LogMessageInfo(
+        message = "Error starting resources in context {0} with Exception message: {1}",
+        level = "SEVERE",
+        cause = "Could not get the proxy directory context",
+        action = "Verify the existence of the context"
+    )
+    public static final String STARTING_RESOURCE_EXCEPTION_MESSAGE = prefix + "00167";
+
+    @LogMessageInfo(
+        message = "Form login page {0} must start with a ''/'' in Servlet 2.4",
+        level = "FINE"
+    )
+    public static final String FORM_LOGIN_PAGE_FINE = prefix + "00168";
+
+    @LogMessageInfo(
+        message = "Form error page {0} must start with a ''/'' in Servlet 2.4",
+        level = "FINE"
+    )
+    public static final String FORM_ERROR_PAGE_FINE = prefix + "00169";
+
+    @LogMessageInfo(
+        message = "JSP file {0} must start with a ''/'' in Servlet 2.4",
+        level = "FINE"
+    )
+    public static final String JSP_FILE_FINE = prefix + "00170";
+
+    @LogMessageInfo(
+        message = "Container {0} has already been started",
+        level = "INFO"
+    )
+    public static final String CONTAINER_ALREADY_STARTED_EXCEPTION = prefix + "00171";
+
+    @LogMessageInfo(
+        message = "Error initialzing resources{0}",
+        level = "WARNING"
+    )
+    public static final String INIT_RESOURCES_EXCEPTION = prefix + "00172";
+
+    @LogMessageInfo(
+        message = "Error in dependency check for standard context {0}",
+        level = "WARNING"
+    )
+    public static final String DEPENDENCY_CHECK_EXCEPTION = prefix + "00173";
+
+    @LogMessageInfo(
+        message = "Startup of context {0} failed due to previous errors",
+        level = "SEVERE",
+        cause = "Could not startup servlet",
+        action = "Verify the initialization process"
+    )
+    public static final String STARTUP_CONTEXT_FAILED_EXCEPTION = prefix + "00174";
+
+    @LogMessageInfo(
+        message = "Exception during cleanup after start failed",
+        level = "SEVERE",
+        cause = "Stop staring up failed",
+        action = "Verify configurations to stop starting up"
+    )
+    public static final String CLEANUP_FAILED_EXCEPTION = prefix + "00175";
+
+    @LogMessageInfo(
+        message = "Error invoking ServletContainerInitializer {0}",
+        level = "SEVERE",
+        cause = "Could not instantiate servlet container initializer",
+        action = "Verify the access permission of current class loader"
+    )
+    public static final String INVOKING_SERVLET_CONTAINER_INIT_EXCEPTION = prefix + "00176";
+
+    @LogMessageInfo(
+        message = "Error resetting context {0}",
+        level = "SEVERE",
+        cause = "Could not restore original state",
+        action = "Verify if extend 'this' method, and make sure to clean up"
+    )
+    public static final String RESETTING_CONTEXT_EXCEPTION = prefix + "00177";
+
+    @LogMessageInfo(
+        message = "URL pattern {0} must start with a ''/'' in Servlet 2.4",
+        level = "FINE"
+    )
+    public static final String URL_PATTERN_WARNING = prefix + "00178";
+
+    @LogMessageInfo(
+        message = "Failed to create work directory {0}",
+        level = "SEVERE",
+        cause = "Could not create work directory",
+        action = "Verify the directory name, and access permission"
+    )
+    public static final String CREATE_WORK_DIR_EXCEPTION = prefix + "00179";
+
+    @LogMessageInfo(
+        message = "The URL pattern {0} contains a CR or LF and so can never be matched",
+        level = "WARNING"
+    )
+    public static final String URL_PATTERN_CANNOT_BE_MATCHED_EXCEPTION = prefix + "00180";
+
+    @LogMessageInfo(
+        message = "Missing name attribute in {0}",
+        level = "SEVERE",
+        cause = "Could not get the attribute",
+        action = "Verify the existence of the value associated with the key"
+    )
+    public static final String MISSING_ATTRIBUTE = prefix + "00181";
+
+    @LogMessageInfo(
+        message = "Malformed name {0}, value of name attribute does not start with ''//''",
+        level = "SEVERE",
+        cause = "Illegal path name",
+        action = "Verify path name"
+    )
+    public static final String MALFORMED_NAME = prefix + "00182";
+
+    @LogMessageInfo(
+        message = "Path {0} does not start with ''/''",
+        level = "WARNING"
+    )
+    public static final String INCORRECT_PATH = prefix + "00183";
+
+    @LogMessageInfo(
+        message = "Path {0} does not start with ''/'' and is not empty",
+        level = "WARNING"
+    )
+    public static final String INCORRECT_OR_NOT_EMPTY_PATH = prefix + "00184";
+
+    @LogMessageInfo(
+        message = "Error during mapping",
+        level = "WARNING"
+    )
+    public static final String MAPPING_ERROR_EXCEPTION = prefix + "00185";
+
+    @LogMessageInfo(
+        message = "Unable to create custom ObjectInputStream",
+        level = "SEVERE",
+        cause = "Could not create custom ObjectInputStream",
+        action = "Verify input stream and class loader"
+    )
+    public static final String CANNOT_CREATE_OBJECT_INPUT_STREAM = prefix + "00186";
+
+    @LogMessageInfo(
+        message = "Error during bindThread",
+        level = "WARNING"
+    )
+    public static final String BIND_THREAD_EXCEPTION = prefix + "00187";
+
+    @LogMessageInfo(
+        message = "Servlet {0} threw load() exception",
+        level = "WARNING"
+    )
+    public static final String SERVLET_LOAD_EXCEPTION = prefix + "00188";
+
+    @LogMessageInfo(
+        message = "Error updating ctx with jmx {0} {1} {2}",
+        level = "INFO"
+    )
+    public static final String ERROR_UPDATING_CTX_INFO = prefix + "00189";
+
+    @LogMessageInfo(
+        message = "Error registering wrapper with jmx {0} {1} {2}",
+        level = "INFO"
+    )
+    public static final String ERROR_REGISTERING_WRAPPER_INFO = prefix + "00190";
+
+    @LogMessageInfo(
+            message = "Null filter instance",
+            level = "WARNING"
+    )
+    public static final String NULL_FILTER_INSTANCE_EXCEPTION = prefix + "00191";
+
+    @LogMessageInfo(
+            message = "Servlet name is null or an empty String",
+            level = "WARNING"
+    )
+    public static final String NULL_EMPTY_SERVLET_NAME_EXCEPTION = prefix + "00192";
+
+    @LogMessageInfo(
+            message = "Null servlet instance",
+            level = "WARNING"
+    )
+    public static final String NULL_SERVLET_INSTANCE_EXCEPTION = prefix + "00193";
+
+    @LogMessageInfo(
+        message = "Child of an Engine must be a Host",
+        level = "WARNING"
+    )
+    public static final String CHILD_OF_ENGINE_MUST_BE_HOST_EXCEPTION = prefix + "00194";
+
+    @LogMessageInfo(
+        message = "Engine cannot have a parent Container",
+        level = "WARNING"
+    )
+    public static final String CANNOT_HAVE_PARENT_CONTAINER_EXCEPTION = prefix + "00195";
+
+    @LogMessageInfo(
+        message = "Error registering",
+        level = "WARNING"
+    )
+    public static final String ERROR_REGISTERING_EXCEPTION = prefix + "00196";
+
+    @LogMessageInfo(
+        message = "No Host matches server name {0}",
+        level = "WARNING"
+    )
+    public static final String NO_HOST_MATCH = prefix + "00197";
+
+     @LogMessageInfo(
+         message = "Host name is required",
+         level = "WARNING"
+     )
+     public static final String HOST_NAME_REQUIRED_EXCEPTION = prefix + "00198";
+
+     @LogMessageInfo(
+         message = "Child of a Host must be a Context",
+         level = "WARNING"
+     )
+     public static final String CHILD_MUST_BE_CONTEXT_EXCEPTION = prefix + "00199";
+
+     @LogMessageInfo(
+         message = "MAPPING configuration error for request URI {0}",
+         level = "SEVERE",
+         cause = "No context has been selected",
+         action = "Verify the uri or default context"
+     )
+     public static final String MAPPING_CONF_REQUEST_URI_EXCEPTION = prefix + "00200";
+
+     @LogMessageInfo(
+         message = "ErrorPage must not be null",
+         level = "WARNING"
+     )
+     public static final String ERROR_PAGE_CANNOT_BE_NULL_EXCEPTION = prefix + "00201";
+
+     @LogMessageInfo(
+         message = "XML validation enabled",
+         level = "FINE"
+     )
+     public static final String XML_VALIDATION_ENABLED = prefix + "00202";
+
+     @LogMessageInfo(
+         message = "Create Host deployer for direct deployment ( non-jmx )",
+         level = "INFO"
+     )
+     public static final String CREATE_HOST_DEPLOYER_INFO = prefix + "00203";
+
+     @LogMessageInfo(
+         message = "Error creating deployer ",
+         level = "SEVERE",
+         cause = "Could not instantiate deployer",
+         action = "Verify access permission"
+     )
+     public static final String ERROR_CREATING_DEPLOYER_EXCEPTION = prefix + "00204";
+
+     @LogMessageInfo(
+         message = "Error registering host {0}",
+         level = "SEVERE",
+         cause = "Initialization failed",
+         action = "Verify domain and host name"
+     )
+     public static final String ERROR_REGISTERING_HOST_EXCEPTION = prefix + "00205";
+
+     @LogMessageInfo(
+         message = "Couldn't load specified error report valve class: {0}",
+         level = "SEVERE",
+         cause = "Could not load instance of host valve",
+         action = "Verify access permission"
+     )
+     public static final String LOAD_SPEC_ERROR_REPORT_EXCEPTION = prefix + "00206";
+
+    @LogMessageInfo(
+        message = "Context path is required",
+        level = "WARNING"
+    )
+    public static final String CONTEXT_PATH_REQUIRED_EXCEPTION = prefix + "00207";
+
+    @LogMessageInfo(
+        message = "Invalid context path: {0}",
+        level = "WARNING"
+    )
+    public static final String INVALID_CONTEXT_PATH_EXCEPTION = prefix + "00208";
+
+    @LogMessageInfo(
+        message = "Context path {0} is already in use",
+        level = "WARNING"
+    )
+    public static final String CONTEXT_PATH_ALREADY_USED_EXCEPTION = prefix + "00209";
+
+    @LogMessageInfo(
+        message = "URL to web application archive is required",
+        level = "WARNING"
+    )
+    public static final String URL_WEB_APP_ARCHIVE_REQUIRED_EXCEPTION = prefix + "00210";
+
+    @LogMessageInfo(
+        message = "Installing web application at context path {0} from URL {1}",
+        level = "INFO"
+    )
+    public static final String INSTALLING_WEB_APP_INFO = prefix + "00211";
+
+    @LogMessageInfo(
+        message = "Invalid URL for web application archive: {0}",
+        level = "WARNING"
+    )
+    public static final String INVALID_URL_WEB_APP_EXCEPTION = prefix + "00212";
+
+    @LogMessageInfo(
+        message = "Only web applications in the Host web application directory can be installed, invalid URL: {0}",
+        level = "WARNING"
+    )
+    public static final String HOST_WEB_APP_DIR_CAN_BE_INSTALLED_EXCEPTION = prefix + "00213";
+
+    @LogMessageInfo(
+        message = "Context path {0} must match the directory or WAR file name: {1}",
+        level = "WARNING"
+    )
+    public static final String CONSTEXT_PATH_MATCH_DIR_WAR_NAME_EXCEPTION = prefix + "00214";
+
+    @LogMessageInfo(
+        message = "Error installing",
+        level = "WARNING"
+    )
+    public static final String ERROR_INSTALLING_EXCEPTION = prefix + "00215";
+
+    @LogMessageInfo(
+        message = "Error deploying application at context path {0}",
+        level = "SEVERE",
+        cause = "Could not initiate life cycle listener",
+        action = "Verify the access permission"
+    )
+    public static final String ERROR_DEPLOYING_APP_CONTEXT_PATH_EXCEPTION = prefix + "00216";
+
+    @LogMessageInfo(
+        message = "URL to configuration file is required",
+        level = "WARNING"
+    )
+    public static final String URL_CONFIG_FILE_REQUIRED_EXCEPTION = prefix + "00217";
+
+    @LogMessageInfo(
+        message = "Use of configuration file is not allowed",
+        level = "WARNING"
+    )
+    public static final String USE_CONFIG_FILE_NOT_ALLOWED = prefix + "00218";
+
+    @LogMessageInfo(
+        message = "Processing Context configuration file URL {0}",
+        level = "INFO"
+    )
+    public static final String PROCESSING_CONTEXT_CONFIG_INFO = prefix + "00219";
+
+    @LogMessageInfo(
+        message = "Installing web application from URL {0}",
+        level = "INFO"
+    )
+    public static final String INSTALLING_WEB_APP_FROM_URL_INFO = prefix + "00220";
+
+    @LogMessageInfo(
+        message = "Context path {0} is not currently in use",
+        level = "WARNING"
+    )
+    public static final String CONTEXT_PATH_NOT_IN_USE = prefix + "00221";
+
+    @LogMessageInfo(
+        message = "Removing web application at context path {0}",
+        level = "INFO"
+    )
+    public static final String REMOVING_WEB_APP_INFO = prefix + "00222";
+
+    @LogMessageInfo(
+        message = "Error removing application at context path {0}",
+        level = "SEVERE",
+        cause = "Could not remove an existing child Container",
+        action = "Verify if there are any I/O errors"
+    )
+    public static final String ERROR_REMOVING_APP_EXCEPTION = prefix + "00223";
+
+    @LogMessageInfo(
+        message = "Starting web application at context path {0}",
+        level = "INFO"
+    )
+    public static final String STARTING_WEB_APP_INFO = prefix + "00224";
+
+    @LogMessageInfo(
+        message = "Starting web application at context path {0} failed",
+        level = "SEVERE",
+        cause = "Could not start web application at current context path",
+        action = "Verify if start() is called before any of the public " +
+                 "methods of this component are utilized, and it should " +
+                 "send START_EVENT to any registered listeners"
+    )
+    public static final String STARTING_WEB_APP_FAILED_EXCEPTION = prefix + "00225";
+
+    @LogMessageInfo(
+        message = "Stopping web application at context path {0}",
+        level = "INFO"
+    )
+    public static final String STOPPING_WEB_APP_INFO = prefix + "00226";
+
+    @LogMessageInfo(
+        message = "Stopping web application at context path {0} failed",
+        level = "SEVERE",
+        cause = "Could not terminate the active use of the public methods of this component",
+        action = "Verify if stop() is the last one called on a given instance of this component, " +
+                 "and it should send STOP_EVENT to any registered listeners"
+    )
+    public static final String STOPPING_WEB_APP_FAILED_EXCEPTION = prefix + "00227";
+
+    @LogMessageInfo(
+        message = "Failed to remove file {0}",
+        level = "WARNING"
+    )
+    public static final String FAILED_REMOVE_FILE = prefix + "00228";
+
+    @LogMessageInfo(
+        message = "Remote Client Aborted Request, IOException: {0}",
+        level = "FINE"
+    )
+    public static final String REMOTE_CLIENT_ABORTED_EXCEPTION = prefix + "00229";
+
+    @LogMessageInfo(
+        message = "The error-page {0} or {1} does not exist",
+        level = "WARNING"
+    )
+    public static final String ERROR_PAGE_NOT_EXIST = prefix + "00230";
+
+    @LogMessageInfo(
+        message = "No Context configured to process this request",
+        level = "WARNING"
+    )
+    public static final String NO_CONTEXT_TO_PROCESS = prefix + "00231";
+
+    @LogMessageInfo(
+        message = "Pipeline has already been started",
+        level = "WARNING"
+    )
+    public static final String PIPLINE_STARTED = prefix + "00232";
+
+    @LogMessageInfo(
+        message = "Pipeline has not been started",
+        level = "WARNING"
+    )
+    public static final String PIPLINE_NOT_STARTED = prefix + "00233";
+
+    @LogMessageInfo(
+        message = "Exception occurred when stopping GlassFishValve in StandardPipeline.setBasic",
+        level = "SEVERE",
+        cause = "Could not terminate the active use of the public methods of this component",
+        action = "Verify if stop() is the last one called on a given instance of this component, " +
+                 "and it should send STOP_EVENT to any registered listeners"
+    )
+    public static final String SET_BASIC_STOP_EXCEPTION = prefix + "00234";
+
+    @LogMessageInfo(
+        message = "Exception occurred when starting GlassFishValve in StandardPipeline.setBasic",
+        level = "SEVERE",
+        cause = "Could not prepare for the beginning of active use of the public methods of this component",
+        action = "Verify if start() is called before any of the public " +
+                 "methods of this component are utilized, and it should " +
+                 "send START_EVENT to any registered listeners"
+    )
+    public static final String SET_BASIC_START_EXCEPTION = prefix + "00235";
+
+    @LogMessageInfo(
+        message = "Exception occurred when starting GlassFishValve in StandardPipline.addValve",
+        level = "SEVERE",
+        cause = "Specific valve could not be associated with current container",
+        action = "Verify the availability of current valve"
+    )
+    public static final String ADD_VALVE_EXCEPTION = prefix + "00236";
+
+    @LogMessageInfo(
+        message = "Unable to add valve {0}",
+        level = "SEVERE",
+        cause = "Could not add tomcat-style valve",
+        action = "Verify if this is a GlassFish-style valve that was compiled against" +
+                 " the old org.apache.catalina.Valve interface"
+    )
+    public static final String ADD_TOMCAT_STYLE_VALVE_EXCEPTION = prefix + "00237";
+
+    @LogMessageInfo(
+        message = "No more Valves in the Pipeline processing this request",
+        level = "WARNING"
+    )
+    public static final String NO_VALVES_IN_PIPELINE_EXCEPTION = prefix + "00238";
+
+    @LogMessageInfo(
+        message = "HttpUpgradeHandler handler cannot be null",
+        level = "WARNING"
+    )
+    public static final String PROTOCOL_HANDLER_REQUIRED_EXCEPTION = prefix + "00239";
+
+    @LogMessageInfo(
+        message = "Exception occurred when stopping GlassFishValve in StandardPipeline.removeValve",
+        level = "SEVERE",
+        cause = "Could not terminate the active use of the public methods of this component",
+        action = "Verify if stop() is the last one called on a given instance of this component, " +
+                 "and it should send STOP_EVENT to any registered listeners"
+    )
+    public static final String REMOVE_VALVE_EXCEPTION = prefix + "00240";
+
+    @LogMessageInfo(
+        message = "StandardPipeline[{0}]: {1}",
+        level = "INFO"
+    )
+    public static final String STANDARD_PIPELINE_INFO = prefix + "00241";
+
+    @LogMessageInfo(
+        message = "StandardPipeline[null]: {0}",
+        level = "INFO"
+    )
+    public static final String STANDARD_PIPELINE_NULL_INFO = prefix + "00242";
+
+     @LogMessageInfo(
+         message = "LifecycleException occurred during service initialization: {0}",
+         level = "SEVERE",
+         cause = "This service was already initialized",
+         action = "Verify if the service is not already initialized")
+     public static final String LIFECYCLE_EXCEPTION_DURING_SERVICE_INIT = prefix + "00243";
+
+     @LogMessageInfo(
+         message = "Exception StandardServer.await: create[{0}]",
+         level = "SEVERE",
+         cause = "An I/O error occurred when opening the socket",
+         action = "Verify the port number and try again")
+     public static final String STANDARD_SERVER_AWAIT_CREATE_EXCEPTION = prefix + "00244";
+
+     @LogMessageInfo(
+         message = "StandardServer.accept security exception: {0}",
+         level = "WARNING",
+         cause = "Could not get connection",
+         action = "Verify the connection settings and try again")
+     public static final String STANDARD_SERVER_ACCEPT_SECURITY_EXCEPTION = prefix + "00245";
+
+     @LogMessageInfo(
+         message = "StandardServer.await: accept: {0}",
+         level = "SEVERE",
+         cause = "Could not get input stream",
+         action = "Verify the input stream and try again")
+     public static final String STANDARD_SERVER_AWAIT_ACCEPT_EXCEPTION = prefix + "00246";
+
+     @LogMessageInfo(
+         message = "StandardServer.await: read: {0}",
+         level = "WARNING",
+         cause = "Could not read from input stream",
+         action = "Verify the input stream and try again")
+     public static final String STANDARD_SERVER_AWAIT_READ_EXCEPTION = prefix + "00247";
+
+     @LogMessageInfo(
+         message = "StandardServer.await: Invalid command {0} received",
+         level = "WARNING",
+         cause = "Invalid command",
+         action = "Verify the command")
+     public static final String STANDARD_SERVER_AWAIT_INVALID_COMMAND_RECEIVED_EXCEPTION = prefix + "00248";
+
+     @LogMessageInfo(
+         message = "This service has already been initialized",
+         level = "INFO")
+     public static final String STANDARD_SERVER_INITIALIZE_INITIALIZED = prefix + "00249";
+
+     @LogMessageInfo(
+         message = "Error registering: {0}",
+         level = "SEVERE",
+         cause = "Could not register ObjectName: \"Catalina:type=Server\"",
+         action = "Verify the configuration and try again")
+     public static final String ERROR_REGISTERING = prefix + "00250";
+
+     @LogMessageInfo(
+         message = "This service has already been started",
+         level = "INFO"
+     )
+     public static final String SERVICE_STARTED = prefix + "00251";
+
+     @LogMessageInfo(
+         message = "Starting service {0}",
+         level = "INFO"
+     )
+     public static final String STARTING_SERVICE = prefix + "00252";
+
+     @LogMessageInfo(
+         message = "Stopping service {0}",
+         level = "INFO"
+     )
+     public static final String STOPPING_SERVICE = prefix + "00253";
+
+     @LogMessageInfo(
+         message = "This service has already been initialized",
+         level = "INFO"
+     )
+     public static final String SERVICE_HAS_BEEN_INIT = prefix + "00254";
+
+     @LogMessageInfo(
+         message = "Error registering Service at domain {0}",
+         level = "SEVERE",
+         cause = "Could not register service",
+         action = "Verify the domain name and service name"
+     )
+     public static final String ERROR_REGISTER_SERVICE_EXCEPTION = prefix + "00255";
+
+     @LogMessageInfo(
+         message = "Service initializing at {0} failed",
+         level = "SEVERE",
+         cause = "Could not pre-startup initialization",
+         action = "Verify if server was already initialized"
+     )
+     public static final String FAILED_SERVICE_INIT_EXCEPTION = prefix + "00256";
+
+    @LogMessageInfo(
+        message = "Parent container of a Wrapper must be a Context",
+        level = "WARNING"
+    )
+    public static final String PARENT_CONTAINER_MUST_BE_CONTEXT_EXCEPTION = prefix + "00257";
+
+    @LogMessageInfo(
+        message = "Wrapper container may not have child containers",
+        level = "WARNING"
+    )
+    public static final String WRAPPER_CONTAINER_NO_CHILD_EXCEPTION = prefix + "00258";
+
+    @LogMessageInfo(
+        message = "Cannot allocate servlet {0} because it is being unloaded",
+        level = "WARNING"
+    )
+    public static final String CANNOT_ALLOCATE_SERVLET_EXCEPTION = prefix + "00259";
+
+    @LogMessageInfo(
+        message = "Error allocating a servlet instance",
+        level = "WARNING"
+    )
+    public static final String ERROR_ALLOCATE_SERVLET_INSTANCE_EXCEPTION = prefix + "00260";
+
+    @LogMessageInfo(
+        message = "Class {0} is not a Servlet",
+        level = "WARNING"
+    )
+    public static final String CLASS_IS_NOT_SERVLET_EXCEPTION = prefix + "00261";
+
+    @LogMessageInfo(
+        message = "Error instantiating servlet class {0}",
+        level = "WARNING"
+    )
+    public static final String ERROR_INSTANTIATE_SERVLET_CLASS_EXCEPTION = prefix + "00262";
+
+    @LogMessageInfo(
+        message = "Servlet of class {0} is privileged and cannot be loaded by this web application",
+        level = "WARNING"
+    )
+    public static final String PRIVILEGED_SERVLET_CANNOT_BE_LOADED_EXCEPTION = prefix + "00263";
+
+    @LogMessageInfo(
+        message = "No servlet class has been specified for servlet {0}",
+        level = "WARNING"
+    )
+    public static final String NO_SERVLET_BE_SPECIFIED_EXCEPTION = prefix + "00264";
+
+    @LogMessageInfo(
+        message = "Wrapper cannot find Loader for servlet {0}",
+        level = "WARNING"
+    )
+    public static final String CANNOT_FIND_LOADER_EXCEPTION = prefix + "00265";
+
+    @LogMessageInfo(
+        message = "Wrapper cannot find servlet class {0} or a class it depends on",
+        level = "WARNING"
+    )
+    public static final String CANNOT_FIND_SERVLET_CLASS_EXCEPTION = prefix + "00266";
+
+    @LogMessageInfo(
+        message = "Servlet.init() for servlet {0} threw exception",
+        level = "WARNING"
+    )
+    public static final String SERVLET_INIT_EXCEPTION = prefix + "00267";
+
+    @LogMessageInfo(
+        message = "Servlet execution threw an exception",
+        level = "WARNING"
+    )
+    public static final String SERVLET_EXECUTION_EXCEPTION = prefix + "00268";
+
+    @LogMessageInfo(
+        message = "Marking servlet {0} as unavailable",
+        level = "FINE"
+    )
+    public static final String MARK_SERVLET_UNAVAILABLE = prefix + "00269";
+
+    @LogMessageInfo(
+        message = "Waiting for {0} instance(s) of {1} to be deallocated",
+        level = "INFO"
+    )
+    public static final String WAITING_INSTANCE_BE_DEALLOCATED = prefix + "00270";
+
+    @LogMessageInfo(
+        message = "Servlet.destroy() for servlet {0} threw exception",
+        level = "WARNING"
+    )
+    public static final String DESTROY_SERVLET_EXCEPTION = prefix + "00271";
+
+    @LogMessageInfo(
+        message = "Servlet {0} threw unload() exception",
+        level = "WARNING"
+    )
+    public static final String SERVLET_UNLOAD_EXCEPTION = prefix + "00272";
+
+    @LogMessageInfo(
+            message = "Error loading {0} {1}",
+            level = "INFO"
+    )
+    public static final String ERROR_LOADING_INFO = prefix + "00273";
+
+    @LogMessageInfo(
+        message = "This application is not currently available",
+        level = "WARNING"
+    )
+    public static final String APP_UNAVAILABLE = prefix + "00274";
+
+    @LogMessageInfo(
+        message = "Servlet {0} is currently unavailable",
+        level = "WARNING"
+    )
+    public static final String SERVLET_UNAVAILABLE  = prefix + "00275";
+
+    @LogMessageInfo(
+        message = "Servlet {0} is not available",
+        level = "WARNING"
+    )
+    public static final String SERVLET_NOT_FOUND = prefix + "00276";
+
+    @LogMessageInfo(
+        message = "Allocate exception for servlet {0}",
+        level = "WARNING"
+    )
+    public static final String SERVLET_ALLOCATE_EXCEPTION = prefix + "00277";
+
+    @LogMessageInfo(
+        message = "Exception for sending acknowledgment of a request: {0}",
+        level = "WARNING"
+    )
+    public static final String SEND_ACKNOWLEDGEMENT_EXCEPTION = prefix + "00278";
+
+    @LogMessageInfo(
+        message = "Release filters exception for servlet {0}",
+        level = "WARNING"
+    )
+    public static final String RELEASE_FILTERS_EXCEPTION = prefix + "00280";
+
+    @LogMessageInfo(
+        message = "Deallocate exception for servlet {0}",
+        level = "WARNING"
+    )
+    public static final String DEALLOCATE_EXCEPTION = prefix + "00281";
+
+    @LogMessageInfo(
+        message = "StandardWrapperValve[{0}]: {1}",
+        level = "INFO"
+    )
+    public static final String STANDARD_WRAPPER_VALVE = prefix + "00283";
+
+    @LogMessageInfo(
+            message = "Failed to skip {0} bytes in the underlying buffer of MultipartStream on close().",
+            level = "WANING"
+    )
+    public static final String FAILED_SKIP_BYTES_MULTIPART_STREAM_CLOSE_EXCEPTION = prefix + "00284";
+
+    @LogMessageInfo(
+            message = "file data is empty.",
+            level = "INFO"
+    )
+    public static final String FILE_DATA_IS_EMPTY_INFO = prefix + "00285";
+
+    @LogMessageInfo(
+            message = "Unable to create Random source using class [{0}]",
+            level = "WARNING"
+    )
+    public static final String UNABLE_CREATE_RANDOM_SOURCE_EXCEPTION = prefix + "00286";
+
+    @LogMessageInfo(
+            message = "The property \"{0}\" is not defined for filters of type \"{1}\"",
+            level = "WARNING"
+    )
+    public static final String PROPERTY_NOT_DEFINED_EXCEPTION = prefix + "00287";
+
+    @LogMessageInfo(
+            message = "Error registering loader",
+            level = "SEVERE",
+            cause = "Could not register loader",
+            action = "Verify Object name"
+    )
+    public static final String REGISTERING_LOADER_EXCEPTION = prefix + "00288";
+
+    @LogMessageInfo(
+            message = "Error registering jndi stream handler",
+            level = "SEVERE",
+            cause = "Could not register jndi stream handler",
+            action = "Verify if the application has already set a factory, " +
+                     "if a security manager exists and its" +
+                     "checkSetFactory method doesn't allow" +
+                     "the operation"
+    )
+    public static final String REGISTERING_JNDI_STREAM_HANDLER_EXCEPTION = prefix + "00289";
+
+    @LogMessageInfo(
+            message = "Loader has already been started",
+            level = "WARNING"
+    )
+    public static final String LOADER_ALREADY_STARTED_EXCEPTION = prefix + "00290";
+
+    @LogMessageInfo(
+            message = "No resources for {0}",
+            level = "INFO"
+    )
+    public static final String NO_RESOURCE_INFO = prefix + "00291";
+
+    @LogMessageInfo(
+            message = "LifecycleException",
+            level = "SEVERE",
+            cause = "Could not construct a class loader",
+            action = "Verify if there is any lifecycle exception"
+    )
+    public static final String LIFECYCLE_EXCEPTION = prefix + "00292";
+
+    @LogMessageInfo(
+            message = "Loader has not yet been started",
+            level = "WARNING"
+    )
+    public static final String LOADER_NOT_STARTED_EXCEPTION = prefix + "00293";
+
+    @LogMessageInfo(
+            message = "Cannot set reloadable property to {0}",
+            level = "SEVERE",
+            cause = "Could not set reloadable property",
+            action = "Verify the value for the property"
+    )
+    public static final String SET_RELOADABLE_PROPERTY_EXCEPTION = prefix + "00294";
+
+    @LogMessageInfo(
+            message = "WebappLoader[{0}]: {1}",
+            level = "WARNING"
+    )
+    public static final String WEB_APP_LOADER_EXCEPTION = prefix + "00295";
+
+    @LogMessageInfo(
+            message = "No work dir for {0}",
+            level = "INFO"
+    )
+    public static final String NO_WORK_DIR_INFO = prefix + "00296";
+
+    @LogMessageInfo(
+            message = "Failed to create destination directory to copy resources",
+            level = "WARNING"
+    )
+    public static final String FAILED_CREATE_DEST_DIR = prefix + "00297";
+
+    @LogMessageInfo(
+            message = "Failed to copy resources",
+            level = "WARNING"
+    )
+    public static final String FAILED_COPY_RESOURCE = prefix + "00298";
+
+    @LogMessageInfo(
+            message = "Failed to create work directory to {0}",
+            level = "SEVERE",
+            cause = "Coud not create work directory",
+            action = "Verify the PATH "
+    )
+    public static final String FAILED_CREATE_WORK_DIR_EXCEPTION = prefix + "00299";
+
+    @LogMessageInfo(
+            message = "File Logger has already been started",
+            level = "WARNING"
+    )
+    public static final String FILE_LOGGER_STARTED = prefix + "00300";
+
+    @LogMessageInfo(
+            message = "File Logger has not yet been started",
+            level = "WARNING"
+    )
+    public static final String FILE_LOGGER_NOT_STARTED = prefix + "00301";
+
+     @LogMessageInfo(
+             message = "Unknown container {0}",
+             level = "SEVERE",
+             cause = "Unknown container for implementation of StandardEngine interface",
+             action = "Verify the current container"
+     )
+     public static final String UNKNOWN_CONTAINER_EXCEPTION = prefix + "00302";
+
+     @LogMessageInfo(
+             message = "Null engine !! {0}",
+             level = "SEVERE",
+             cause = "Could not get engine",
+             action = "Verify current container"
+     )
+     public static final String NULL_ENGINE_EXCEPTION = prefix + "00303";
+
+     @LogMessageInfo(
+             message = "Unable to create javax.management.ObjectName for Logger",
+             level = "WARNING"
+     )
+     public static final String UNABLE_CREATE_OBJECT_NAME_FOR_LOGGER_EXCEPTION = prefix + "00304";
+
+     @LogMessageInfo(
+             message = "Can't register logger {0}",
+             level = "SEVERE",
+             cause = "Could not register logger",
+             action = "Verify registration is called after configure()"
+     )
+     public static final String CANNOT_REGISTER_LOGGER_EXCEPTION = prefix + "00305";
+
+     @LogMessageInfo(
+             message = "Setting JAAS app name {0}",
+             level = "INFO"
+     )
+     public static final String SETTING_JAAS_INFO = prefix + "00306";
+
+     @LogMessageInfo(
+             message = "Login exception authenticating username {0}",
+             level = "FINE"
+     )
+     public static final String LOGIN_EXCEPTION_AUTHENTICATING_USERNAME = prefix + "00307";
+
+     @LogMessageInfo(
+             message = "Username {0} NOT authenticated due to failed login",
+             level = "FINE"
+     )
+     public static final String USERNAME_NOT_AUTHENTICATED_FAILED_LOGIN = prefix + "00308";
+
+     @LogMessageInfo(
+             message = "Username {0} NOT authenticated due to expired account",
+             level = "FINE"
+     )
+     public static final String USERNAME_NOT_AUTHENTICATED_EXPIRED_ACCOUNT = prefix + "00309";
+
+     @LogMessageInfo(
+             message = "Username {0} NOT authenticated due to expired credential",
+             level = "FINE"
+     )
+     public static final String USERNAME_NOT_AUTHENTICATED_EXPIRED_CREDENTIAL = prefix + "00310";
+
+     @LogMessageInfo(
+             message = "error ",
+             level = "SEVERE",
+             cause = "Could not authenticate by using the current username",
+             action = "Verify the username and credential"
+     )
+     public static final String AUTHENTICATION_ERROR = prefix + "00311";
+
+    @LogMessageInfo(
+            message = "Illegal digestEncoding: {0}",
+            level = "SEVERE",
+            cause = "Could not convert the char array to byte array with respect to given charset",
+            action = "Verify the current charset"
+    )
+    public static final String ILLEGAL_DIGEST_ENCODING_EXCEPTION = prefix + "00312";
+
+    @LogMessageInfo(
+            message = "Access to the requested resource has been denied",
+            level = "WARNING"
+    )
+    public static final String ACCESS_RESOURCE_DENIED = prefix + "00313";
+
+    @LogMessageInfo(
+            message = "Configuration error: Cannot perform access control without an authenticated principal",
+            level = "WARNING"
+    )
+    public static final String CONFIG_ERROR_NOT_AUTHENTICATED = prefix + "00314";
+
+    @LogMessageInfo(
+            message = "Username {0} has role {1}",
+            level = "FINE"
+    )
+    public static final String USERNAME_HAS_ROLE = prefix + "00315";
+
+    @LogMessageInfo(
+            message = "Username {0} does NOT have role {1}",
+            level = "FINE"
+    )
+    public static final String USERNAME_NOT_HAVE_ROLE = prefix + "00316";
+
+    @LogMessageInfo(
+            message = "This Realm has already been started",
+            level = "INFO"
+    )
+    public static final String REALM_BEEN_STARTED = prefix + "00317";
+
+    @LogMessageInfo(
+            message = "Invalid message digest algorithm {0} specified",
+            level = "WARNING"
+    )
+    public static final String INVALID_ALGORITHM_EXCEPTION = prefix + "00318";
+
+    @LogMessageInfo(
+            message = "This Realm has not yet been started",
+            level = "INFO"
+    )
+    public static final String REALM_NOT_BEEN_STARTED = prefix + "00319";
+
+    @LogMessageInfo(
+            message = "Error digesting user credentials",
+            level = "SEVERE",
+            cause = "Could not digest user credentials",
+            action = "Verify the current credential"
+    )
+    public static final String ERROR_DIGESTING_USER_CREDENTIAL_EXCEPTION = prefix + "00320";
+
+    @LogMessageInfo(
+            message = "Couldn't get MD5 digest",
+            level = "SEVERE",
+            cause = "Could not get instance of MessageDigest based on MD5",
+            action = "Verify if it supports a MessageDigestSpi implementation " +
+                     "for the specified algorithm"
+    )
+    public static final String CANNOT_GET_MD5_DIGEST_EXCEPTION = prefix + "00321";
+
+    @LogMessageInfo(
+            message = "An exception occurs when running the PrivilegedExceptionAction block.",
+            level = "FINE"
+    )
+    public static final String PRIVILEGE_ACTION_EXCEPTION = prefix + "00322";
+
+    @LogMessageInfo(
+            message = "Only skipped [{0}] bytes when [{1}] were requested",
+            level = "WARNING"
+    )
+    public static final String SKIP_BYTES_EXCEPTION = prefix + "00323";
+
+    @LogMessageInfo(
+            message = "Directory Listing For {0}",
+            level = "INFO"
+    )
+    public static final String DIR_TITLE_INFO = prefix + "00324";
+
+    @LogMessageInfo(
+            message = "Up To {0}",
+            level = "INFO"
+    )
+    public static final String DIR_PARENT_INFO = prefix + "00325";
+
+    @LogMessageInfo(
+            message = "Filename",
+            level = "INFO"
+    )
+    public static final String DIR_FILENAME_INFO = prefix + "00326";
+
+    @LogMessageInfo(
+            message = "Size",
+            level = "INFO"
+    )
+    public static final String DIR_SIZE_INFO = prefix + "00327";
+
+    @LogMessageInfo(
+            message = "Last Modified",
+            level = "INFO"
+    )
+    public static final String DIR_LAST_MODIFIED_INFO = prefix + "00328";
+
+    @LogMessageInfo(
+            message = "Container has not called setWrapper() for this servlet",
+            level = "WARNING"
+    )
+    public static final String SET_WRAPPER_NOT_CALLED_EXCEPTION = prefix + "00329";
+
+    @LogMessageInfo(
+            message = "Cannot call invoker servlet with a named dispatcher",
+            level = "WARNING"
+    )
+    public static final String CANNOT_CALL_INVOKER_SERVLET = prefix + "00330";
+
+    @LogMessageInfo(
+            message = "No servlet name or class was specified in path {0}",
+            level = "WARNING"
+    )
+    public static final String INVALID_PATH_EXCEPTION = prefix + "00331";
+
+    @LogMessageInfo(
+            message = "Cannot create servlet wrapper for path {0}",
+            level = "WARNING"
+    )
+    public static final String CANNOT_CREATE_SERVLET_WRAPPER_EXCEPTION = prefix + "00332";
+
+    @LogMessageInfo(
+            message = "Cannot allocate servlet instance for path {0}",
+            level = "WARNING"
+    )
+    public static final String CANNOT_ALLOCATE_SERVLET_INSTANCE_EXCEPTION = prefix + "00333";
+
+    @LogMessageInfo(
+            message = "Cannot deallocate servlet instance for path {0}",
+            level = "WARNING"
+    )
+    public static final String CANNOT_DEALLOCATE_SERVLET_INSTANCE_EXCEPTION = prefix + "00334";
+
+    @LogMessageInfo(
+            message = "JAXP initialization failed",
+            level = "WARNING"
+    )
+    public static final String JAXP_INTI_FAILED = prefix + "00335";
+
+    @LogMessageInfo(
+            message = "Ignored external entity, publicID: {0}, systemID: {1}",
+            level = "INFO"
+    )
+    public static final String IGNORED_EXTERNAL_ENTITY_INFO = prefix + "00336";
+
+    @LogMessageInfo(
+            message = "setAttribute: Session attribute with name {0} has value that is not of type String (required for cookie-based persistence)",
+            level = "WARNING"
+    )
+    public static final String SET_SESSION_ATTRIBUTE_EXCEPTION = prefix + "00337";
+
+    @LogMessageInfo(
+            message = "Loading Session {0} from file {1}",
+            level = "FINE"
+    )
+    public static final String LOADING_SESSION_FROM_FILE = prefix + "00338";
+
+    @LogMessageInfo(
+            message = "Removing Session {0} at file {1}",
+            level = "FINE"
+    )
+    public static final String REMOVING_SESSION_FROM_FILE = prefix + "00339";
+
+    @LogMessageInfo(
+            message = "Saving Session {0} to file {1}",
+            level = "FINE"
+    )
+    public static final String SAVING_SESSION_TO_FILE = prefix + "00340";
+
+    @LogMessageInfo(
+            message = "Unable to delete file [{0}] which is preventing the creation of the session storage location",
+            level = "WARNING"
+    )
+    public static final String UNABLE_DELETE_FILE_EXCEPTION = prefix + "00341";
+
+    @LogMessageInfo(
+            message = "Unable to create directory [{0}] for the storage of session data",
+            level = "WARNING"
+    )
+    public static final String UNABLE_CREATE_DIR_EXCEPTION = prefix + "00342";
+
+    @LogMessageInfo(
+            message = "SQL Error {0}",
+            level = "FINE"
+    )
+    public static final String SQL_ERROR = prefix + "00343";
+
+    @LogMessageInfo(
+            message = "Loading Session {0} from database {1}",
+            level = "FINE"
+    )
+    public static final String LOADING_SESSION_FROM_DATABASE = prefix + "00344";
+
+    @LogMessageInfo(
+            message = "Removing Session {0} at database {1}",
+            level = "FINE"
+    )
+    public static final String REMOVING_SESSION_FROM_DATABASE = prefix + "00345";
+
+    @LogMessageInfo(
+            message = "Saving Session {0} to database {1}",
+            level = "FINE"
+    )
+    public static final String SAVING_SESSION_TO_DATABASE = prefix + "00346";
+
+    @LogMessageInfo(
+            message = "The database connection is null or was found to be closed. Trying to re-open it.",
+            level = "FINE"
+    )
+    public static final String DATABASE_CONNECTION_CLOSED = prefix + "00347";
+
+    @LogMessageInfo(
+            message = "The re-open on the database failed. The database could be down.",
+            level = "FINE"
+    )
+    public static final String RE_OPEN_DATABASE_FAILED = prefix + "00348";
+
+    @LogMessageInfo(
+            message = "A SQL exception occurred {0}",
+            level = "FINE"
+    )
+    public static final String SQL_EXCEPTION = prefix + "00349";
+
+    @LogMessageInfo(
+            message = "JDBC driver class not found {0}",
+            level = "FINE"
+    )
+    public static final String JDBC_DRIVER_CLASS_NOT_FOUND = prefix + "00350";
+
+    @LogMessageInfo(
+            message = "Exception initializing random number generator of class {0}",
+            level = "SEVERE",
+            cause = "Could not construct and seed a new random number generator",
+            action = "Verify if the current random number generator class is "
+    )
+    public static final String INIT_RANDOM_NUMBER_GENERATOR_EXCEPTION = prefix + "00351";
+
+    @LogMessageInfo(
+            message = "Seeding random number generator class {0}",
+            level = "FINE"
+    )
+    public static final String SEEDING_RANDOM_NUMBER_GENERATOR_CLASS = prefix + "00352";
+
+    @LogMessageInfo(
+            message = "Failed to close randomIS.",
+            level = "WARNING"
+    )
+    public static final String FAILED_CLOSE_RANDOMIS_EXCEPTION = prefix + "00353";
+
+    @LogMessageInfo(
+            message = "Error registering ",
+            level = "SEVERE",
+            cause = "Could not construct an object name",
+            action = "Verify the format of domain, path, host. And make sure they are no null"
+    )
+    public static final String ERROR_REGISTERING_EXCEPTION_SEVERE = prefix + "00354";
+
+    @LogMessageInfo(
+            message = "setAttribute: Non-serializable attribute with name {0}",
+            level = "WARNING"
+    )
+    public static final String NON_SERIALIZABLE_ATTRIBUTE_EXCEPTION = prefix + "00355";
+
+    @LogMessageInfo(
+            message = "Session not found {0}",
+            level = "INFO"
+    )
+    public static final String SESSION_NOT_FOUND = prefix + "00356";
+
+    @LogMessageInfo(
+            message = "Checking isLoaded for id, {0}, {1}",
+            level = "SEVERE",
+            cause = "Could not find session associated with given ID",
+            action = "Verify the session ID"
+    )
+    public static final String CHECKING_IS_LOADED_EXCEPTION = prefix + "00357";
+
+    @LogMessageInfo(
+            message = "Exception clearing the Store",
+            level = "SEVERE",
+            cause = "Could not instantiate PrivilegedStoreClear()",
+            action = "Verify if specified action's run() could remove all sessions from store"
+    )
+    public static final String CLEARING_STORE_EXCEPTION = prefix + "00358";
+
+    @LogMessageInfo(
+            message = "createSession: Too many active sessions",
+            level = "WARNING"
+    )
+    public static final String CREATE_SESSION_EXCEPTION = prefix + "00359";
+
+    @LogMessageInfo(
+            message = "Exception in the Store during load",
+            level = "SEVERE",
+            cause = "Could not instantiate PrivilegedStoreKeys()",
+            action = "Verify if specified action's run() does not throw exception"
+    )
+    public static final String STORE_LOADING_EXCEPTION = prefix + "00360";
+
+    @LogMessageInfo(
+            message = "Loading {0} persisted sessions",
+            level = "FINE"
+    )
+    public static final String LOADING_PERSISTED_SESSIONS = prefix + "00361";
+
+    @LogMessageInfo(
+            message = "Failed load session from store",
+            level = "SEVERE",
+            cause = "Could not restore sessions from store to manager's list",
+            action = "Verify if the sessions are valid"
+    )
+    public static final String FAILED_LOAD_SESSION_EXCEPTION = prefix + "00362";
+
+    @LogMessageInfo(
+            message = "Can't load sessions from store",
+            level = "SEVERE",
+            cause = "Could not load sessions from store",
+            action = "Verify if there is no exception to get the array containing the session " +
+                     "identifiers of all Sessions currently saved in this Store"
+    )
+    public static final String CANNOT_LOAD_SESSION_EXCEPTION = prefix + "00363";
+
+    @LogMessageInfo(
+            message = "Exception in the Store during removeSession",
+            level = "SEVERE",
+            cause = "Could not instantiate PrivilegedStoreRemove()",
+            action = "Verify if the specified action's run() could remove the session with the " +
+                     "specified session identifier from this Store"
+    )
+    public static final String STORE_REMOVE_SESSION_EXCEPTION = prefix + "00364";
+
+    @LogMessageInfo(
+            message = "Exception removing session",
+            level = "SEVERE",
+            cause = "Could not remove specified session identifier from store",
+            action = "Verify if there is no I/O error occur"
+    )
+    public static final String REMOVING_SESSION_EXCEPTION = prefix + "00365";
+
+    @LogMessageInfo(
+            message = "Saving {0} persisted sessions",
+            level = "FINE"
+    )
+    public static final String SAVING_PERSISTED_SESSION = prefix + "00366";
+
+    @LogMessageInfo(
+            message = "Exception in the Store during swapIn",
+            level = "SEVERE",
+            cause = "Could not instantiate PrivilegedStoreLoad",
+            action = "Verify if action's run() can load and return the Session associated with the specified session " +
+                     "identifier from this Store, without removing it"
+    )
+    public static final String STORE_SWAP_IN_EXCEPTION = prefix + "00367";
+
+    @LogMessageInfo(
+            message = "Error deserializing Session {0}: {1}",
+            level = "SEVERE",
+            cause = "Deserialization error occur, and could not load and return the session " +
+                     "associated with the specified session identifier from this Store",
+            action = "Verify if ClassNotFoundException occur"
+    )
+    public static final String DESERILIZING_SESSION_EXCEPTION = prefix + "00368";
+
+    @LogMessageInfo(
+            message = "Session swapped in is invalid or expired",
+            level = "SEVERE",
+            cause = "Session swapped in is invalid or expired",
+            action = "Verify if current session is valid"
+    )
+    public static final String INVALID_EXPIRED_SESSION_EXCEPTION = prefix + "00369";
+
+    @LogMessageInfo(
+            message = "Swapping session {0} in from Store",
+            level = "FINE"
+    )
+    public static final String SWAPPING_SESSION_FROM_STORE = prefix + "00370";
+
+    @LogMessageInfo(
+            message = "Exception in the Store during writeSession",
+            level = "SEVERE",
+            cause = "Could not write the provided session to the Store",
+            action = "Verify if there are any I/O errors occur"
+    )
+    public static final String STORE_WRITE_SESSION_EXCEPTION = prefix + "00371";
+
+    @LogMessageInfo(
+            message = "Error serializing Session {0}: {1}",
+            level = "SEVERE",
+            cause = "Could not save the specified Session into this Store",
+            action = "Verify if there are any I/O errors occur"
+    )
+    public static final String SERIALIZING_SESSION_EXCEPTION = prefix + "00372";
+
+    @LogMessageInfo(
+            message = "Manager has already been started",
+            level = "INFO"
+    )
+    public static final String MANAGER_STARTED_INFO = prefix + "00373";
+
+    @LogMessageInfo(
+            message = "No Store configured, persistence disabled",
+            level = "SEVERE",
+            cause = "Could not prepare for the beginning of active use of the public methods of this component",
+            action = "Verify if Store has been configured"
+    )
+    public static final String NO_STORE_CONFIG_EXCEPTION = prefix + "00374";
+
+    @LogMessageInfo(
+            message = "Manager has not yet been started",
+            level = "INFO"
+    )
+    public static final String  MANAGER_NOT_STARTED_INFO = prefix + "00375";
+
+    @LogMessageInfo(
+            message = "Invalid session timeout setting {0}",
+            level = "SEVERE",
+            cause = "Could not set session timeout from given parameter",
+            action = "Verify the number format for session timeout setting"
+    )
+    public static final String INVALID_SESSION_TIMEOUT_SETTING_EXCEPTION = prefix + "00376";
+
+    @LogMessageInfo(
+            message = "Swapping session {0} to Store, idle for {1} seconds",
+            level = "FINE"
+    )
+    public static final String SWAPPING_SESSION_TO_STORE = prefix + "00377";
+
+    @LogMessageInfo(
+            message = "Too many active sessions, {0}, looking for idle sessions to swap out",
+            level = "FINE"
+    )
+    public static final String TOO_MANY_ACTIVE_SESSION = prefix + "00378";
+
+    @LogMessageInfo(
+            message = "Swapping out session {0}, idle for {1} seconds too many sessions active",
+            level = "FINE"
+    )
+    public static final String SWAP_OUT_SESSION = prefix + "00379";
+
+    @LogMessageInfo(
+            message = " Backing up session {0} to Store, idle for {1} seconds",
+            level = "FINE"
+    )
+    public static final String BACKUP_SESSION_TO_STORE = prefix + "00380";
+
+    @LogMessageInfo(
+            message = "createSession: Too many active sessions",
+            level = "WARNING"
+    )
+    public static final String TOO_MANY_ACTIVE_SESSION_EXCEPTION = prefix + "00381";
+
+    @LogMessageInfo(
+            message = " Loading persisted sessions from {0}",
+            level = "FINE"
+    )
+    public static final String LOADING_PERSISTED_SESSION = prefix + "00382";
+
+    @LogMessageInfo(
+            message = "IOException while loading persisted sessions: {0}",
+            level = "SEVERE",
+            cause = "Could not creates an ObjectInputStream",
+            action = "Verify if there are IO exceptions"
+    )
+    public static final String LOADING_PERSISTED_SESSION_IO_EXCEPTION = prefix + "00383";
+
+    @LogMessageInfo(
+            message = "ClassNotFoundException while loading persisted sessions: {0}",
+            level = "SEVERE",
+            cause = "Could not deserialize and create StandardSession instance ",
+            action = "Verify the class for an object being restored can be found"
+    )
+    public static final String CLASS_NOT_FOUND_EXCEPTION = prefix + "00384";
+
+    @LogMessageInfo(
+            message = "Saving persisted sessions to {0}",
+            level = "FINE"
+    )
+    public static final String SAVING_PERSISTED_SESSION_PATH = prefix + "00385";
+
+    @LogMessageInfo(
+            message = "IOException while saving persisted sessions: {0}",
+            level = "SEVERE",
+            cause = "Could not creates an ObjectOutputStream instance",
+            action = "Verify if there are any I/O exceptions"
+    )
+    public static final String SAVING_PERSISTED_SESSION_IO_EXCEPTION = prefix + "00386";
+
+    @LogMessageInfo(
+            message = "Exception loading sessions from persistent storage",
+            level = "SEVERE",
+            cause = "Could not load any currently active sessions",
+            action = "Verify if the serialized class is valid and if there are any I/O exceptions"
+    )
+    public static final String LOADING_SESSIONS_EXCEPTION = prefix + "00387";
+
+    @LogMessageInfo(
+            message = "Exception unloading sessions to persistent storage",
+            level = "SEVERE",
+            cause = "Could not save any currently active sessions",
+            action = "Verify if there are any I/O exceptions"
+    )
+    public static final String UNLOADING_SESSIONS_EXCEPTION = prefix + "00388";
+
+    @LogMessageInfo(
+            message = "Session id change event listener threw exception",
+            level = "WARNING"
+    )
+    public static final String SESSION_ID_CHANGE_EVENT_LISTENER_EXCEPTION = prefix + "00389";
+
+    @LogMessageInfo(
+            message = "Session event listener threw exception",
+            level = "WARNING"
+    )
+    public static final String SESSION_EVENT_LISTENER_EXCEPTION = prefix + "00390";
+
+    @LogMessageInfo(
+            message = "Session already invalidated",
+            level = "WARNING"
+    )
+    public static final String SESSION_INVALIDATED_EXCEPTION = prefix + "00391";
+
+    @LogMessageInfo(
+            message = "Session attribute event listener threw exception",
+            level = "WARNING"
+    )
+    public static final String SESSION_ATTRIBUTE_EVENT_LISTENER_EXCEPTION = prefix + "00392";
+
+    @LogMessageInfo(
+            message = "setAttribute: name parameter cannot be null",
+            level = "WARNING"
+    )
+    public static final String NAME_PARAMETER_CANNOT_BE_NULL_EXCEPTION = prefix + "00393";
+
+    @LogMessageInfo(
+            message = "Session binding event listener threw exception",
+            level = "WARNING"
+    )
+    public static final String SESSION_BINDING_EVENT_LISTENER_EXCEPTION = prefix + "00394";
+
+    @LogMessageInfo(
+            message = " Cannot serialize session attribute {0} for session {1}",
+            level = "WARNING"
+    )
+    public static final String CANNOT_SERIALIZE_SESSION_EXCEPTION = prefix + "00395";
+
+    @LogMessageInfo(
+            message = "StoreBase has already been started",
+            level = "WARNING"
+    )
+    public static final String STORE_BASE_STARTED_EXCEPTION = prefix + "00396";
+
+    @LogMessageInfo(
+            message = "StoreBase has not been started",
+            level = "WARNING"
+    )
+    public static final String STORE_BASE_NOT_STARTED_EXCEPTION = prefix + "00397";
+
+    @LogMessageInfo(
+            message = "Class loader creation threw exception",
+            level = "SEVERE",
+            cause = "Could not create class loader",
+            action = "Verify the availability of current repository "
+    )
+    public static final String CLASS_LOADER_CREATION_EXCEPTION = prefix + "00398";
+
+    @LogMessageInfo(
+            message = "Error processing command line arguments",
+            level = "WARNING"
+    )
+    public static final String ERROR_PROCESSING_COMMAND_LINE_EXCEPTION = prefix + "00399";
+
+    @LogMessageInfo(
+            message = "Catalina.stop: ",
+            level = "SEVERE",
+            cause = "Could not stop server",
+            action = "Verify if the input file exist or if there are any I/O exceptions, parsing exceptions"
+    )
+    public static final String CATALINA_STOP_EXCEPTION = prefix + "00400";
+
+    @LogMessageInfo(
+            message = "Can't load server.xml from {0}",
+            level = "WARNING"
+    )
+    public static final String CANNOT_LOAD_SERVER_XML_EXCEPTION = prefix + "00401";
+
+    @LogMessageInfo(
+            message = "Catalina.start: ",
+            level = "WARNING"
+    )
+    public static final String CATALINA_START_WARNING_EXCEPTION = prefix + "00402";
+
+    @LogMessageInfo(
+            message = "Catalina.start: ",
+            level = "SEVERE",
+            cause = "Could not initialize the server",
+            action = "Verify if the server has already been initialized"
+    )
+    public static final String CATALINA_START_SEVERE_EXCEPTION = prefix + "00403";
+
+    @LogMessageInfo(
+            message = "Initialization processed in {0} ms",
+            level = "INFO"
+    )
+    public static final String INIT_PROCESSED_EXCEPTION = prefix + "00404";
+
+    @LogMessageInfo(
+            message = "Error loading configuration",
+            level = "WARNING"
+    )
+    public static final String ERROR_LOADING_CONFIGURATION_EXCEPTION = prefix + "00405";
+
+    @LogMessageInfo(
+            message = "Server startup in {0} ms",
+            level = "WARNING"
+    )
+    public static final String SERVER_STARTUP_INFO = prefix + "00406";
+
+    @LogMessageInfo(
+            message = "Failed to load catalina.properties",
+            level = "WARNING"
+    )
+    public static final String FAILED_LOAD_CATALINA_PROPERTIES_EXCEPTION = prefix + "00407";
+
+    @LogMessageInfo(
+            message = "Lifecycle event data object {0} is not a Context",
+            level = "WARNING"
+    )
+    public static final String EVENT_DATA_IS_NOT_CONTEXT_EXCEPTION = prefix + "00408";
+
+    @LogMessageInfo(
+            message = "alt-dd file {0} not found",
+            level = "WARNING"
+    )
+    public static final String ALT_DD_FILE_NOT_FOUND_EXCEPTION = prefix + "00409";
+
+    @LogMessageInfo(
+            message = "Missing application web.xml, using defaults only {0}",
+            level = "FINE"
+    )
+    public static final String MISSING_APP_WEB_XML_FINE = prefix + "00410";
+
+    @LogMessageInfo(
+            message = "Parse error in application web.xml at line {0} and column {1}",
+            level = "WARNING"
+    )
+    public static final String PARSE_ERROR_IN_APP_WEB_XML_EXCEPTION = prefix + "00411";
+
+    @LogMessageInfo(
+            message = "Parse error in application web.xml",
+            level = "WARNING"
+    )
+    public static final String PARSE_ERROR_IN_APP_WEB_XML = prefix + "00412";
+
+    @LogMessageInfo(
+            message = "Error closing application web.xml",
+            level = "SEVERE",
+            cause = "Could not close this input stream and releases any system resources " +
+                    "associated with the stream.",
+            action = "Verify if any I/O errors occur"
+    )
+    public static final String ERROR_CLOSING_APP_WEB_XML_EXCEPTION = prefix + "00413";
+
+    @LogMessageInfo(
+            message = "No Realm has been configured to authenticate against",
+            level = "WARNING"
+    )
+    public static final String NO_REALM_BEEN_CONFIGURED_EXCEPTION = prefix + "00414";
+
+    @LogMessageInfo(
+            message = "Cannot configure an authenticator for method {0}",
+            level = "WARNING"
+    )
+    public static final String CANNOT_CONFIG_AUTHENTICATOR_EXCEPTION = prefix + "00415";
+
+    @LogMessageInfo(
+            message = "Cannot instantiate an authenticator of class {0}",
+            level = "WARNING"
+    )
+    public static final String CANNOT_INSTANTIATE_AUTHENTICATOR_EXCEPTION = prefix + "00416";
+
+    @LogMessageInfo(
+            message = "Configured an authenticator for method {0}",
+            level = "FINE"
+    )
+    public static final String CONFIGURED_AUTHENTICATOR_FINE = prefix + "00417";
+
+    @LogMessageInfo(
+            message = "No default web.xml",
+            level = "INFO"
+    )
+    public static final String NO_DEFAULT_WEB_XML_INFO = prefix + "00418";
+
+    @LogMessageInfo(
+            message = "Missing default web.xml, using application web.xml only {0} {1}",
+            level = "WARNING"
+    )
+    public static final String MISSING_DEFAULT_WEB_XML_EXCEPTION = prefix + "00419";
+
+    @LogMessageInfo(
+            message = "Parse error in default web.xml at line {0} and column {1}",
+            level = "SEVERE",
+            cause = "Could not parse the content of the specified input source using this Digester",
+            action = "Verify the input parameter, if any I/O errors occur"
+    )
+    public static final String PARSE_ERROR_IN_DEFAULT_WEB_XML_EXCEPTION = prefix + "00420";
+
+    @LogMessageInfo(
+            message = "Parse error in default web.xml",
+            level = "SEVERE",
+            cause = "Could not parse the content of the specified input source using this Digester",
+            action = "Verify the input parameter, if any I/O errors occur"
+    )
+    public static final String PARSE_ERROR_IN_DEFAULT_WEB_XML = prefix + "00421";
+
+    @LogMessageInfo(
+            message = "Error closing default web.xml",
+            level = "SEVERE",
+            cause = "Could not close this input stream and releases any system resources " +
+                    "associated with the stream.",
+            action = "Verify if any I/O errors occur"
+    )
+    public static final String ERROR_CLOSING_DEFAULT_WEB_XML_EXCEPTION = prefix + "00422";
+
+    @LogMessageInfo(
+            message = "ContextConfig: Initializing",
+            level = "FINE"
+    )
+    public static final String CONTEXT_CONFIG_INIT_FINE = prefix + "00423";
+
+    @LogMessageInfo(
+            message = "Exception fixing docBase",
+            level = "SEVERE",
+            cause = "Could not adjust docBase",
+            action = "Verify if any I/O errors occur"
+    )
+    public static final String FIXING_DOC_BASE_EXCEPTION = prefix + "00424";
+
+    @LogMessageInfo(
+            message = "ContextConfig: Processing START",
+            level = "FINEST"
+    )
+    public static final String PROCESSING_START_FINEST = prefix + "00425";
+
+    @LogMessageInfo(
+            message = "ContextConfig: Processing STOP",
+            level = "FINEST"
+    )
+    public static final String PROCESSING_STOP_FINEST = prefix + "00426";
+
+    @LogMessageInfo(
+            message = "Security role name {0} used in an <auth-constraint> without being defined in a <security-role> in context [{1}]",
+            level = "INFO"
+    )
+    public static final String SECURITY_ROLE_NAME_USED_IN_AUTH_WITHOUT_DEFINITION = prefix + "00427";
+
+    @LogMessageInfo(
+            message = "Security role name {0} used in a <run-as> without being defined in a <security-role> in context [{1}]",
+            level = "INFO"
+    )
+    public static final String SECURITY_ROLE_NAME_USED_IN_RUNAS_WITHOUT_DEFINITION = prefix + "00428";
+
+    @LogMessageInfo(
+            message = "Security role name {0} used in a <role-link> without being defined in a <security-role> in context [{1}]",
+            level = "INFO"
+    )
+    public static final String SECURITY_ROLE_NAME_USED_IN_LINK_WITHOUT_DEFINITION = prefix + "00429";
+
+    @LogMessageInfo(
+            message = "No web.xml, using defaults {0}",
+            level = "INFO"
+    )
+    public static final String NO_WEB_XML_INFO = prefix + "00430";
+
+    @LogMessageInfo(
+            message = "No engines have been defined yet",
+            level = "WARNING"
+    )
+    public static final String NO_ENGINES_DEFINED = prefix + "00431";
+
+    @LogMessageInfo(
+            message = "Engine.start exception",
+            level = "SEVERE",
+            cause = "Could not prepare for the beginning of active use of " +
+                    "the public methods of this component.",
+            action = "Verify if start() be called before any of the public " +
+                     "methods of this component are utilized"
+    )
+    public static final String ENGINE_START_EXCEPTION = prefix + "00432";
+
+    @LogMessageInfo(
+            message = "Couldn't load SSL server socket factory.",
+            level = "SEVERE",
+            cause = "Could not instantiate ServerSocketFactory",
+            action = "Verify access permission to this class"
+    )
+    public static final String COULD_NOT_LOAD_SSL_SERVER_SOCKET_FACTORY_EXCEPTION = prefix + "00433";
+
+    @LogMessageInfo(
+            message = "Couldn't create connector.",
+            level = "SEVERE",
+            cause = "Could not instantiate connector",
+            action = "Verify access permission to this class"
+    )
+    public static final String COULD_NOT_CREATE_CONNECTOR_EXCEPTION = prefix + "00434";
+
+    @LogMessageInfo(
+            message = "Connector.stop",
+            level = "SEVERE",
+            cause = "Could not remove the specified Connector from the set associated from this Service",
+            action = "Verify if connector has already been stopped or removed"
+    )
+    public static final String CONNECTOR_STOP_EXCEPTION = prefix + "00435";
+
+    @LogMessageInfo(
+            message = "Engine.stop exception",
+            level = "SEVERE",
+            cause = "Could not terminate the active use of the public methods of this component",
+            action = "Verify if stop() is the last one called on a given instance of this component"
+    )
+    public static final String  ENGINE_STOP_EXCEPTION = prefix + "00436";
+
+    @LogMessageInfo(
+            message = "Specified Authenticator is not a Valve",
+            level = "WARNING"
+    )
+    public static final String AUTH_IS_NOT_VALVE_EXCEPTION = prefix + "00437";
+
+    @LogMessageInfo(
+            message = "Embedded service has already been started",
+            level = "WARNING"
+    )
+    public static final String SERVICE_BEEN_STARTED_EXCEPTION = prefix + "00438";
+
+    @LogMessageInfo(
+            message = "Embedded service has not yet been started",
+            level = "WARNING"
+    )
+    public static final String SERVICE_NOT_BEEN_STARTED_EXCEPTION = prefix + "00439";
+
+    @LogMessageInfo(
+            message = "Lifecycle event data object {0} is not an Engine",
+            level = "WARNING"
+    )
+    public static final String LIFECYCLE_EVENT_DATA_IS_NOT_ENGINE_EXCEPTION = prefix + "00440";
+
+    @LogMessageInfo(
+            message = "EngineConfig: {0}",
+            level = "WARNING"
+    )
+    public static final String ENGINE_CONFIG = prefix + "00441";
+
+    @LogMessageInfo(
+            message = "EngineConfig: Processing START",
+            level = "INFO"
+    )
+    public static final String ENGINE_CONFIG_PROCESSING_START_INFO = prefix + "00442";
+
+    @LogMessageInfo(
+            message = "EngineConfig: Processing STOP",
+            level = "INFO"
+    )
+    public static final String ENGINE_CONFIG_PROCESSING_STOP_INFO = prefix + "00443";
+
+    @LogMessageInfo(
+            message = "Application base directory {0} does not exist",
+            level = "WARNING"
+    )
+    public static final String APP_NOT_EXIST_EXCEPTION = prefix + "00444";
+
+    @LogMessageInfo(
+            message = "Unable to create the directory [{0}]",
+            level = "WARNING"
+    )
+    public static final String UNABLE_CREATE_DIRECTORY_EXCEPTION = prefix + "00445";
+
+    @LogMessageInfo(
+            message = "The archive [{0}] is malformed and will be ignored: an entry contains an illegal path [{1}]",
+            level = "WARNING"
+    )
+    public static final String ARCHIVE_IS_MALFORMED_EXCEPTION = prefix + "00446";
+
+    @LogMessageInfo(
+            message = "Failed to set last-modified time of the file {0}",
+            level = "WARNING"
+    )
+    public static final String FAILED_SET_LAST_MODIFIED_TIME_EXCEPTION = prefix + "00447";
+
+    @LogMessageInfo(
+            message = "Error copying {0} to {1}",
+            level = "SEVERE",
+            cause = "Could not copy file",
+            action = "Verify if channel is not available for file transfer"
+    )
+    public static final String ERROR_COPYING_EXCEPTION = prefix + "00448";
+
+    @LogMessageInfo(
+            message = "[{0}] could not be completely deleted. The presence of the remaining files may cause problems",
+            level = "SEVERE",
+            cause = "Could not completely delete specified directory",
+            action = "Verify the access permission to specified directory"
+    )
+    public static final String DELETE_DIR_EXCEPTION = prefix + "00449";
+
+    @LogMessageInfo(
+            message = "Lifecycle event data object {0} is not a Host",
+            level = "SEVERE",
+            cause = "Could not process the START event for an associated Host",
+            action = "Verify Lifecycle event data object"
+    )
+    public static final String LIFECYCLE_OBJECT_NOT_HOST_EXCEPTION = prefix + "00450";
+
+    @LogMessageInfo(
+            message = "Deploying configuration descriptor {0}",
+            level = "FINE"
+    )
+    public static final String DEPLOYING_CONFIG_DESCRIPTOR = prefix + "00451";
+
+    @LogMessageInfo(
+            message = "Error deploying configuration descriptor {0}",
+            level = "SEVERE",
+            cause = "Could not deploy configuration descriptor",
+            action = "Verify the URL that points to context configuration file and the context path"
+    )
+    public static final String ERROR_DEPLOYING_CONFIG_DESCRIPTOR_EXCEPTION = prefix + "00452";
+
+    @LogMessageInfo(
+            message = "The war name [{0}] is invalid. The archive will be ignored.",
+            level = "SEVERE",
+            cause = "Could not deploy war file",
+            action = "Verify the name war file"
+    )
+    public static final String INVALID_WAR_NAME_EXCEPTION = prefix + "00453";
+
+    @LogMessageInfo(
+            message = "Expanding web application archive {0}",
+            level = "FINE"
+    )
+    public static final String EXPANDING_WEB_APP = prefix + "00454";
+
+    @LogMessageInfo(
+            message = "Exception while expanding web application archive {0}",
+            level = "WARNING"
+    )
+    public static final String EXPANDING_WEB_APP_EXCEPTION = prefix + "00455";
+
+    @LogMessageInfo(
+            message = "Exception while expanding web application archive {0}",
+            level = "SEVERE",
+            cause = "Could not expand web application archive",
+            action = "Verify the URL, and if any I/O errors orrur"
+    )
+    public static final String EXPANDING_WEB_APP_ARCHIVE_EXCEPTION = prefix + "00456";
+
+    @LogMessageInfo(
+            message = "Deploying web application archive {0}",
+            level = "INFO"
+    )
+    public static final String DEPLOYING_WEB_APP_ARCHIVE = prefix + "00457";
+
+    @LogMessageInfo(
+            message = "Error deploying web application archive {0}",
+            level = "SEVERE",
+            cause = "Could not deploy web application archive",
+            action = "Verify the context path and if specified context path " +
+                     "is already attached to an existing web application"
+    )
+    public static final String ERROR_DEPLOYING_WEB_APP_ARCHIVE_EXCEPTION = prefix + "00458";
+
+    @LogMessageInfo(
+            message = "Deploying web application directory {0}",
+            level = "FINE"
+    )
+    public static final String DEPLOYING_WEB_APP_DIR = prefix + "00459";
+
+    @LogMessageInfo(
+            message = "Error deploying web application directory {0}",
+            level = "SEVERE",
+            cause = "Could not deploy web application directory",
+            action = "Verify the context path and if specified context path " +
+                     "is already attached to an existing web application"
+    )
+    public static final String ERROR_DEPLOYING_WEB_APP_DIR = prefix + "00460";
+
+    @LogMessageInfo(
+            message = "Error undeploying Jar file {0}",
+            level = "SEVERE",
+            cause = "Could not remove an existing web application, attached to the specified context path",
+            action = "Verify the context path of the application"
+    )
+    public static final String ERROR_UNDEPLOYING_JAR_FILE_EXCEPTION = prefix + "00461";
+
+    @LogMessageInfo(
+            message = "HostConfig: restartContext [{0}]",
+            level = "INFO"
+    )
+    public static final String RESTART_CONTEXT_INFO = prefix + "00462";
+
+    @LogMessageInfo(
+            message = "Error during context [{0}] stop",
+            level = "WARNING"
+    )
+    public static final String ERROR_DURING_CONTEXT_STOP_EXCEPTION = prefix + "00463";
+
+    @LogMessageInfo(
+            message = "Error during context [{0}] restart",
+            level = "WARNING"
+    )
+    public static final String ERROR_DURING_CONTEXT_RESTART_EXCEPTION = prefix + "00464";
+
+    @LogMessageInfo(
+            message = "HostConfig: Processing START",
+            level = "FINE"
+    )
+    public static final String PROCESSING_START = prefix + "00465";
+
+    @LogMessageInfo(
+            message = "HostConfig: Processing STOP",
+            level = "FINE"
+    )
+    public static final String PROCESSING_STOP = prefix + "00466";
+
+    @LogMessageInfo(
+            message = "Undeploying deployed web applications",
+            level = "FINE"
+    )
+    public static final String UNDEPLOYING_WEB_APP = prefix + "00467";
+
+    @LogMessageInfo(
+            message = "Undeploying context [{0}]",
+            level = "FINE"
+    )
+    public static final String UNDEPLOYING_CONTEXT = prefix + "00468";
+
+    @LogMessageInfo(
+            message = "Error undeploying web application at context path {0}",
+            level = "SEVERE",
+            cause = "Could not remove an existing web application, attached to the specified context path",
+            action = "Verify the context path of the application"
+    )
+    public static final String ERROR_UNDEPLOYING_WEB_APP_EXCEPTION = prefix + "00469";
+
+    @LogMessageInfo(
+            message = "Must set 'catalina.home' system property",
+            level = "SEVERE",
+            cause = "Did not set 'catalina.home'",
+            action = "Verify that 'catalina.home' was passed"
+    )
+    public static final String MUST_SET_SYS_PROPERTY = prefix + "00470";
+
+    @LogMessageInfo(
+            message = "Exception creating instance of {0}",
+            level = "SEVERE",
+            cause = "Could not load application class",
+            action = "Verify the class name"
+    )
+    public static final String CREATING_INSTANCE_EXCEPTION = prefix + "00472";
+
+    @LogMessageInfo(
+            message = "Exception locating main() method",
+            level = "SEVERE",
+            cause = "Could not locate the static main() method of the application class",
+            action = "Verify the access permission"
+    )
+    public static final String LOCATING_MAIN_METHOD_EXCEPTION = prefix + "00473";
+
+    @LogMessageInfo(
+            message = "Exception calling main() method",
+            level = "SEVERE",
+            cause = "Could not invoke main() method",
+            action = "Verify the underlying method is inaccessible, and parameter values"
+    )
+    public static final String CALLING_MAIN_METHOD_EXCEPTION = prefix + "00474";
+
+    @LogMessageInfo(
+            message = "Usage:  java org.apache.catalina.startup.Tool [<options>] <class> [<arguments>]",
+            level = "INFO"
+    )
+    public static final String USAGE_INFO = prefix + "00475";
+
+    @LogMessageInfo(
+            message = "Deploying user web applications",
+            level = "INFO"
+    )
+    public static final String DEPLOYING_USER_WEB_APP_INFO = prefix + "00476";
+
+    @LogMessageInfo(
+            message = "Exception loading user database",
+            level = "WARNING"
+    )
+    public static final String LOADING_USER_DATABASE_EXCEPTION = prefix + "00477";
+
+    @LogMessageInfo(
+            message = "Deploying web application for user {0}",
+            level = "INFO"
+    )
+    public static final String DEPLOYING_WEB_APP_FOR_USER_INFO = prefix + "00478";
+
+    @LogMessageInfo(
+            message = "Error deploying web application for user {0}",
+            level = "WARNING"
+    )
+    public static final String DEPLOYING_WEB_APP_FOR_USER_EXCEPTION = prefix + "00479";
+
+    @LogMessageInfo(
+            message = "UserConfig[{0}]: {1}",
+            level = "INFO"
+    )
+    public static final String USER_CONFIG = prefix + "00480";
+
+    @LogMessageInfo(
+            message = "UserConfig[null]: {0}",
+            level = "INFO"
+    )
+    public static final String USER_CONFIG_NULL = prefix + "00481";
+
+    @LogMessageInfo(
+            message = "UserConfig: Processing START",
+            level = "INFO"
+    )
+    public static final String PROCESSING_START_INFO = prefix + "00482";
+
+    @LogMessageInfo(
+            message = "UserConfig: Processing STOP",
+            level = "INFO"
+    )
+    public static final String PROCESSING_STOP_INFO = prefix + "00483";
+
+    @LogMessageInfo(
+            message = "Failed to load manifest resources {0}",
+            level = "SEVERE",
+            cause = "Could not find MANIFEST from JAR file",
+            action = "Verify the JAR file"
+    )
+    public static final String FAILED_LOAD_MANIFEST_RESOURCES_EXCEPTION = prefix + "00484";
+
+    @LogMessageInfo(
+            message = "ExtensionValidator[{0}][{1}]: Required extension \"{2}\" not found.",
+            level = "INFO"
+    )
+    public static final String EXTENSION_NOT_FOUND_INFO = prefix + "00485";
+
+    @LogMessageInfo(
+            message = "ExtensionValidator[{0}]: Failure to find {1} required extension(s).",
+            level = "INFO"
+    )
+    public static final String FAILED_FIND_EXTENSION_INFO = prefix + "00486";
+
+    @LogMessageInfo(
+            message = "Odd number of hexadecimal digits",
+            level = "WARNING"
+    )
+    public static final String ODD_NUMBER_HEX_DIGITS_EXCEPTION = prefix + "00487";
+
+    @LogMessageInfo(
+            message = "Bad hexadecimal digit",
+            level = "WARNING"
+    )
+    public static final String BAD_HEX_DIGIT_EXCEPTION = prefix + "00488";
+
+    @LogMessageInfo(
+            message = "Map is currently locked",
+            level = "WARNING"
+    )
+    public static final String MAP_IS_LOCKED_EXCEPTION = prefix + "00489";
+
+    @LogMessageInfo(
+            message = "UTF8 not supported",
+            level = "WARNING"
+    )
+    public static final String UTF8_NOT_SUPPORTED_EXCEPTION = prefix + "00490";
+
+    @LogMessageInfo(
+            message = "Could not create a new directory: {0}",
+            level = "SEVERE",
+            cause = "Could not create a new directory",
+            action = "Verify if file is directory, and access permission"
+    )
+    public static final String CREATING_DIR_EXCEPTION = prefix + "00491";
+
+    @LogMessageInfo(
+            message = "status.setContentType",
+            level = "WARNING"
+    )
+    public static final String SET_CONTENT_TYPE_EXCEPTION = prefix + "00492";
+
+    @LogMessageInfo(
+            message = "Internal Error",
+            level = "SEVERE",
+            cause = "Error during invoke the servlet application",
+            action = "Trying to invoke the servlet application"
+    )
+    public static final String INTERNAL_ERROR = prefix + "00493";
+
+    @LogMessageInfo(
+            message = "Failed to initialize the interceptor",
+            level = "SEVERE",
+            cause = "Error in initializing the servlet application",
+            action = "initialize the servlet interceptor"
+    )
+    public static final String FAILED_TO_INITIALIZE_THE_INTERCEPTOR = prefix + "00494";
+
+    @LogMessageInfo(
+            message = "Failed to rename log file to {0} for rotate logs",
+            level = "SEVERE",
+            cause = "Could not rename log file",
+            action = "Verify access permission and new file name"
+    )
+    public static final String FAILED_RENAME_LOG_FILE = prefix + "00503";
+
+    @LogMessageInfo(
+            message = "at least this wasn't swallowed",
+            level = "INFO"
+    )
+    public static final String NOT_SWALLOWED_INFO = prefix + "00504";
+
+    @LogMessageInfo(
+            message = "Failed to create directory {0}",
+            level = "SEVERE",
+            cause = "Could not create directory",
+            action = "Verify access permission"
+    )
+    public static final String FAILED_CREATE_DIR = prefix + "00505";
+
+    @LogMessageInfo(
+            message = "fields was just empty or whitespace",
+            level = "INFO"
+    )
+    public static final String FIELD_EMPTY_INFO = prefix + "00506";
+
+    @LogMessageInfo(
+            message = "unable to decode with rest of chars being: {0}",
+            level = "SEVERE",
+            cause = "Could not decode rest of chars",
+            action = "Verify the current pattern"
+    )
+    public static final String UNABLE_DECODE_REST_CHARS = prefix + "00507";
+
+    @LogMessageInfo(
+            message = "No closing ) found for in decode",
+            level = "SEVERE",
+            cause = "could not find closing bracket",
+            action = "Verify if the parameter includes closing bracket"
+    )
+    public static final String NO_CLOSING_BRACKET_FOUND = prefix + "00508";
+
+    @LogMessageInfo(
+            message = "The next characters couldn't be decoded: {0}",
+            level = "SEVERE",
+            cause = "Could not decode characters",
+            action = "Verify the pattern"
+    )
+    public static final String CHARACTER_CANNOT_DECODED = prefix + "00509";
+
+    @LogMessageInfo(
+            message = "End of line reached before decoding x- param",
+            level = "SEVERE",
+            cause = "Could not decode, since end of line reached",
+            action = "Verify the String index"
+    )
+    public static final String END_LINE_REACHED = prefix + "00510";
+
+    @LogMessageInfo(
+            message = "x param in wrong format. Needs to be 'x-#(...)' read the docs!",
+            level = "SEVERE",
+            cause = "Could not decode, since x param in wrong format",
+            action = "Verify the format of parameter"
+    )
+    public static final String WRONG_X_PARAM_FORMAT = prefix + "00511";
+
+    @LogMessageInfo(
+            message = "x param in wrong format. No closing ')'!",
+            level = "SEVERE",
+            cause = "Could not decode, since x param has no closing bracket",
+            action = "Verify the format of parameter"
+    )
+    public static final String X_PARAM_NO_CLOSING_BRACKET = prefix + "00512";
+
+    @LogMessageInfo(
+            message = "x param for servlet request, couldn't decode value: {0}",
+            level = "SEVERE",
+            cause = "Could not decode value, since no x param type matched",
+            action = "Verify the current fieldInfo"
+    )
+    public static final String X_PARAM_CANNOT_DECODE_VALUE = prefix + "00513";
+
+    @LogMessageInfo(
+            message = "No Context configured to process this request",
+            level = "WARNING"
+    )
+    public static final String NO_CONTEXT_CONFIGURED = prefix + "00514";
+
+    @LogMessageInfo(
+            message = "Syntax error in request filter pattern {0}",
+            level = "WARNING"
+    )
+    public static final String SYNTAX_ERROR = prefix + "00515";
+
+    @LogMessageInfo(
+            message = "Cannot process the error page: {0}",
+            level = "INFO"
+    )
+    public static final String CANNOT_PROCESS_ERROR_PAGE_INFO = prefix + "00516";
+
+    @LogMessageInfo(
+            message = "Digester.getParser: ",
+            level = "SEVERE",
+            cause = "Could not create new SAXParser",
+            action = "Verify the parser configuration and if SAXParser is supported"
+    )
+    public static final String GET_PARRSER_EXCEPTION = prefix + "00517";
+
+    @LogMessageInfo(
+            message = "Cannot get XMLReader",
+            level = "SEVERE",
+            cause = "Could not get XML Reader",
+            action = "Verify if there are XML Readers can be instantiated"
+    )
+    public static final String CANNOT_GET_XML_READER_EXCEPTION = prefix + "00518";
+
+    @LogMessageInfo(
+            message = "Finish event threw exception",
+            level = "SEVERE",
+            cause = "Rules could not remove data",
+            action = "Verify if finish() is called after all parsing methods have been called"
+    )
+    public static final String FINISH_EVENT_EXCEPTION = prefix + "00519";
+
+    @LogMessageInfo(
+            message = "Finish event threw error",
+            level = "SEVERE",
+            cause = "Rules could not remove data",
+            action = "Verify if finish() is called after all parsing methods have been called"
+    )
+    public static final String FINISH_EVENT_ERROR = prefix + "00520";
+
+    @LogMessageInfo(
+            message = "Body event threw exception",
+            level = "SEVERE",
+            cause = "Could not fire body()",
+            action = "Verify if the current rule has body"
+    )
+    public static final String BODY_EVENT_EXCEPTION = prefix + "00521";
+
+    @LogMessageInfo(
+            message = "Body event threw error",
+            level = "SEVERE",
+            cause = "Could not fire body()",
+            action = "Verify if the current rule has body"
+    )
+    public static final String BODY_EVENT_ERROR = prefix + "00522";
+
+    @LogMessageInfo(
+            message = "No rules found matching {0}.",
+            level = "WARNING"
+    )
+    public static final String NO_RULES_FOUND_MATCHING_EXCEPTION = prefix + "00523";
+
+    @LogMessageInfo(
+            message = "End event threw exception",
+            level = "SEVERE",
+            cause = "Could not call end()",
+            action = "Verify if this method is called when the end of a matching XML element " +
+                     "is encountered"
+    )
+    public static final String END_EVENT_EXCEPTION = prefix + "00524";
+
+    @LogMessageInfo(
+            message = "End event threw error",
+            level = "SEVERE",
+            cause = "Could not call end()",
+            action = "Verify if this method is called when the end of a matching XML element " +
+                    "is encountered"
+    )
+    public static final String END_EVENT_ERROR = prefix + "00525";
+
+    @LogMessageInfo(
+            message = "Begin event threw exception",
+            level = "SEVERE",
+            cause = "Could not call begin()",
+            action = "Verify if this method is called when the beginning of a matching XML element " +
+                    "is encountered"
+    )
+    public static final String BEGIN_EVENT_EXCEPTION = prefix + "00526";
+
+    @LogMessageInfo(
+            message = "Begin event threw error",
+            level = "SEVERE",
+            cause = "Could not call begin()",
+            action = "Verify if this method is called when the beginning of a matching XML element " +
+                    "is encountered"
+    )
+    public static final String BEGIN_EVENT_ERROR = prefix + "00527";
+
+    @LogMessageInfo(
+            message = "Parse Error at line {0} column {1}: {2}",
+            level = "SEVERE",
+            cause = "Parsing error occurs",
+            action = "Verify if there are any parsing errors occur"
+    )
+    public static final String PARSE_ERROR = prefix + "00528";
+
+    @LogMessageInfo(
+            message = "Parse Fatal Error at line {0} column {1}: {2}",
+            level = "SEVERE",
+            cause = "Parsing error occurs",
+            action = "Verify if there are any parsing errors occur"
+    )
+    public static final String PARSE_FATAL_ERROR = prefix + "00529";
+
+    @LogMessageInfo(
+            message = "Parse Warning Error at line {0} column {1}: {2}",
+            level = "SEVERE",
+            cause = "Parsing error occurs",
+            action = "Verify if there are any parsing errors occur"
+    )
+    public static final String PARSE_WARNING_ERROR = prefix + "00530";
+
+    @LogMessageInfo(
+            message = "Empty stack (returning null)",
+            level = "WARNING"
+    )
+    public static final String EMPTY_STACK_EXCEPTION = prefix + "00531";
+
+    @LogMessageInfo(
+            message = "No Locator!",
+            level = "SEVERE",
+            cause = "There is no document locator",
+            action = "Verify if document locator has been set"
+    )
+    public static final String NO_LOCATOR_EXCEPTION = prefix + "00532";
+
+    @LogMessageInfo(
+            message = "[SetPropertiesRule]{0} Setting property {1} to {2} did not find a matching property.",
+            level = "WARNING"
+    )
+    public static final String PROPERTIES_RULE_NOT_FIND_MATCHING_PROPERTY = prefix + "00533";
+
+    @LogMessageInfo(
+            message = "[SetPropertyRule]{0} Setting property {1} to {2} did not find a matching property.",
+            level = "WARNING"
+    )
+    public static final String PROPERTY_RULE_NOT_FIND_MATCHING_PROPERTY = prefix + "00534";
+
+    @LogMessageInfo(
+            message = "Login failed",
+            level = "WARNING"
+    )
+
+    public static final String LOGIN_FAIL = prefix + "00535";
+
+    @LogMessageInfo(
+            message = "This is request has already been authenticated",
+            level = "WARNING"
+    )
+    public static final String ALREADY_AUTHENTICATED = prefix + "00536";
+
+    @LogMessageInfo(
+            message = "No authenticator",
+            level = "WARNING"
+    )
+    public static final String NO_AUTHENTICATOR = prefix + "00537";
+
+    @LogMessageInfo(
+            message = "Invalid call to login while pluggable authentication method is configured",
+            level = "WARNING"
+    )
+    public static final String LOGIN_WITH_AUTH_CONFIG = prefix + "00538";
+
+    @LogMessageInfo(
+            message = "Internal logout error",
+            level = "WARNING")
+    public static final String INTERNAL_LOGOUT_ERROR = prefix + "00539";
+
+    @LogMessageInfo(
+            message = "Blocked access to external entity with publicId [{0}] and systemId [{0}]",
+            level = "WARNING"
+    )
+    public static final String BLOCK_EXTERNAL_ENTITY = prefix + "00540";
+
+    @LogMessageInfo(
+            message = "Blocked access to external entity with name [{0}], publicId [{1}], baseURI [{2}] and systemId [{3}]",
+            level = "WARNING"
+    )
+    public static final String BLOCK_EXTERNAL_ENTITY2 = prefix + "00541";
+
+    @LogMessageInfo(
+            message = "Blocked access to external subset with name [{0}] and baseURI [{1}]",
+            level = "WARNING"
+    )
+    public static final String BLOCK_EXTERNAL_SUBSET = prefix + "00542";
+
+    @LogMessageInfo(
+            message = "Fail to read file [{0}]",
+            level = "WARNING"
+    )
+    public static final String READ_FILE_EXCEPTION = prefix + "00543";
+
+    @LogMessageInfo(
+            message = "Unable to find the underlying Coyote request object (which is required to create a push request) from the request of type [{0}]",
+            level = "WARNING"
+    )
+    public static final String NO_PUSH_COYOTE_REQUEST_EXCEPTION = prefix + "00544";
+
+    @LogMessageInfo(
+            message = "It is illegal to call push() before setting a path",
+            level = "WARNING"
+    )                           
+    public static final String NO_PUSH_PATH_EXCEPTION = prefix + "00545";
+
+    @LogMessageInfo(
+            message = "The push method should not be null",
+            level = "WARNING"
+    )
+    public static final String NULL_PUSH_METHOD_EXCEPTION = prefix + "00546";
+
+    @LogMessageInfo(
+            message = "The push method should not be an empty String",
+            level = "WARNING"
+    )
+    public static final String EMPTY_PUSH_METHOD_EXCEPTION = prefix + "00547";
+
+    @LogMessageInfo(
+            message = "The push method ''{0}'' is not cacheable or safe",
+            level = "WARNING"
+    )
+    public static final String NONCACHEABLE_UNSAFE_PUSH_METHOD_EXCEPTION = prefix + "00548";
+
+    @LogMessageInfo(
+            message = "The repository '{0}' is not a directory",
+            level = "WARNING"
+    )
+    public static final String REPOSITORY_IS_NOT_A_DIRECTORY = prefix + "00549";
+
+    @LogMessageInfo(
+            message = "The repository '{0}' contains a null character",
+            level = "WARNING"
+    )
+    public static final String REPOSITORY_PATH_CONTAIN_NULL_CHARACTER = prefix + "00550";
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Logger.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Logger.java
new file mode 100644
index 0000000..4c4fe5c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Logger.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import java.beans.PropertyChangeListener;
+
+
+/**
+ * A <b>Logger</b> is a generic interface for the message and exception
+ * logging methods of the ServletContext interface.  Loggers can be
+ * attached at any Container level, but will typically only be attached
+ * to a Context, or higher level, Container.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:18 $
+ */
+
+public interface Logger {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * Verbosity level constants for log messages that may be filtered
+     * by the underlying logger.
+     */
+
+    public static final int FATAL = Integer.MIN_VALUE;
+
+    public static final int ERROR = 1;
+
+    public static final int WARNING = 2;
+
+    public static final int INFORMATION = 3;
+
+    public static final int DEBUG = 4;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container with which this Logger has been associated.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the Container with which this Logger has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container);
+
+
+    /**
+     * Return descriptive information about this Logger implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the verbosity level of this logger.  Messages logged with a
+     * higher verbosity than this level will be silently ignored.
+     */
+    public int getVerbosity();
+
+
+    /**
+     * Set the verbosity level of this logger.  Messages logged with a
+     * higher verbosity than this level will be silently ignored.
+     *
+     * @param verbosity The new verbosity level
+     */
+    public void setVerbosity(int verbosity);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Writes the specified message to a servlet log file, usually an event
+     * log.  The name and type of the servlet log is specific to the
+     * servlet container.  This message will be logged unconditionally.
+     *
+     * @param message A <code>String</code> specifying the message to be
+     *  written to the log file
+     */
+    public void log(String message);
+
+
+    /**
+     * Writes the specified exception, and message, to a servlet log file.
+     * The implementation of this method should call
+     * <code>log(msg, exception)</code> instead.  This method is deprecated
+     * in the ServletContext interface, but not deprecated here to avoid
+     * many useless compiler warnings.  This message will be logged
+     * unconditionally.
+     *
+     * @param exception An <code>Exception</code> to be reported
+     * @param msg The associated message string
+     */
+    public void log(Exception exception, String msg);
+
+
+    /**
+     * Writes an explanatory message and a stack trace for a given
+     * <code>Throwable</code> exception to the servlet log file.  The name
+     * and type of the servlet log file is specific to the servlet container,
+     * usually an event log.  This message will be logged unconditionally.
+     *
+     * @param message A <code>String</code> that describes the error or
+     *  exception
+     * @param throwable The <code>Throwable</code> error or exception
+     */
+    public void log(String message, Throwable throwable);
+
+
+    /**
+     * Writes the specified message to the servlet log file, usually an event
+     * log, if the logger is set to a verbosity level equal to or higher than
+     * the specified value for this message.
+     *
+     * @param message A <code>String</code> specifying the message to be
+     *  written to the log file
+     * @param verbosity Verbosity level of this message
+     */
+    public void log(String message, int verbosity);
+
+
+    /**
+     * Writes the specified message and exception to the servlet log file,
+     * usually an event log, if the logger is set to a verbosity level equal
+     * to or higher than the specified value for this message.
+     *
+     * @param message A <code>String</code> that describes the error or
+     *  exception
+     * @param throwable The <code>Throwable</code> error or exception
+     * @param verbosity Verbosity level of this message
+     */
+    public void log(String message, Throwable throwable, int verbosity);
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Manager.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Manager.java
new file mode 100644
index 0000000..124c109
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Manager.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.*;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+//END OF 6364900
+
+/**
+ * A <b>Manager</b> manages the pool of Sessions that are associated with a
+ * particular Container.  Different Manager implementations may support
+ * value-added features such as the persistent storage of session data,
+ * as well as migrating sessions for distributable web applications.
+ * <p>
+ * In order for a <code>Manager</code> implementation to successfully operate
+ * with a <code>Context</code> implementation that implements reloading, it
+ * must obey the following constraints:
+ * <ul>
+ * <li>Must implement <code>Lifecycle</code> so that the Context can indicate
+ *     that a restart is required.
+ * <li>Must allow a call to <code>stop()</code> to be followed by a call to
+ *     <code>start()</code> on the same <code>Manager</code> instance.
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.6 $ $Date: 2006/11/17 23:06:36 $
+ */
+
+public interface Manager {
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the Container with which this Manager is associated.
+     */
+    public Container getContainer();
+
+    /**
+     * Set the Container with which this Manager is associated.
+     *
+     * @param container The newly associated Container
+     */
+    public void setContainer(Container container);
+
+    /**
+     * Return the distributable flag for the sessions supported by
+     * this Manager.
+     */
+    public boolean getDistributable();
+
+    /**
+     * Set the distributable flag for the sessions supported by this
+     * Manager.  If this flag is set, all user data objects added to
+     * sessions associated with this manager must implement Serializable.
+     *
+     * @param distributable The new distributable flag
+     */
+    public void setDistributable(boolean distributable);
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+    /**
+     * Same as getMaxInactiveIntervalSeconds
+     */
+    public int getMaxInactiveInterval();
+
+    /**
+     * Return the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     */
+    public int getMaxInactiveIntervalSeconds();
+
+    /**
+     * Same as setMaxInactiveIntervalSeconds
+     */
+    public void setMaxInactiveInterval(int interval);
+
+    /**
+     * Set the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     *
+     * @param interval The new default value
+     */
+    public void setMaxInactiveIntervalSeconds(int interval);
+
+    /**
+     * Gets the session id length (in bytes) of Sessions created by
+     * this Manager.
+     *
+     * @return The session id length
+     */
+    public int getSessionIdLength();
+
+    /**
+     * Sets the session id length (in bytes) for Sessions created by this
+     * Manager.
+     *
+     * @param length The session id length
+     */
+    public void setSessionIdLength(int length);
+
+    /** 
+     * Same as getSessionCount
+     */
+    public int getSessionCounter();
+
+    /** 
+     * Returns the total number of sessions created by this manager.
+     *
+     * @return Total number of sessions created by this manager.
+     */
+    public int getSessionCount();
+
+    /** 
+     * Same as setSessionCount
+     */
+    public void setSessionCounter(int sessionCounter);
+
+    /** 
+     * Sets the total number of sessions created by this manager.
+     *
+     * @param sessionCounter Total number of sessions created by this manager.
+     */
+    public void setSessionCount(int sessionCounter);
+
+    /**
+     * Gets the maximum number of sessions that have been active at the same
+     * time.
+     *
+     * @return Maximum number of sessions that have been active at the same
+     * time
+     */
+    public int getMaxActive();
+
+    /**
+     * (Re)sets the maximum number of sessions that have been active at the
+     * same time.
+     *
+     * @param maxActive Maximum number of sessions that have been active at
+     * the same time.
+     */
+    public void setMaxActive(int maxActive);
+
+    /** 
+     * Gets the number of currently active sessions.
+     *
+     * @return Number of currently active sessions
+     */
+    public int getActiveSessions();
+
+    /**
+     * Gets the number of sessions that have expired.
+     *
+     * @return Number of sessions that have expired
+     */
+    public int getExpiredSessions();
+
+    /**
+     * Sets the number of sessions that have expired.
+     *
+     * @param expiredSessions Number of sessions that have expired
+     */
+    public void setExpiredSessions(int expiredSessions);
+
+    /**
+     * Gets the number of sessions that were not created because the maximum
+     * number of active sessions was reached.
+     *
+     * @return Number of rejected sessions
+     */
+    public int getRejectedSessions();
+
+    /**
+     * Sets the number of sessions that were not created because the maximum
+     * number of active sessions was reached.
+     *
+     * @param rejectedSessions Number of rejected sessions
+     */
+    public void setRejectedSessions(int rejectedSessions);
+
+    /**
+     * Same as getSessionMaxAliveTimeSeconds
+     */
+    public int getSessionMaxAliveTime();
+
+    /**
+     * Gets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @return Longest time (in seconds) that an expired session had been
+     * alive.
+     */
+    public int getSessionMaxAliveTimeSeconds();
+
+    /**
+     * Same as setSessionMaxAliveTimeSeconds
+     */
+    public void setSessionMaxAliveTime(int sessionMaxAliveTime);
+
+    /**
+     * Sets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @param sessionMaxAliveTime Longest time (in seconds) that an expired
+     * session had been alive.
+     */
+    public void setSessionMaxAliveTimeSeconds(int sessionMaxAliveTime);
+
+    /**
+     * Same as getSessionAverageAliveTimeSeconds
+     */
+    public int getSessionAverageAliveTime();
+
+    /**
+     * Gets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @return Average time (in seconds) that expired sessions had been
+     * alive.
+     */
+    public int getSessionAverageAliveTimeSeconds();
+
+    /**
+     * Same as setSessionAverageAliveTimeSeconds
+     */
+    public void setSessionAverageAliveTime(int sessionAverageAliveTime);
+
+    /**
+     * Sets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @param sessionAverageAliveTime Average time (in seconds) that expired
+     * sessions had been alive.
+     */
+    public void setSessionAverageAliveTimeSeconds(int sessionAverageAliveTime);
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Add this Session to the set of active Sessions for this Manager.
+     *
+     * @param session Session to be added
+     */
+    public void add(Session session);
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+    /**
+     * Change the session ID of the current session to a new randomly generated
+     * session ID.
+     * 
+     * @param session   The session to change the session ID for
+     */
+    public void changeSessionId(Session session);
+
+    /**
+     * Get a session from the recycled ones or create a new empty one.
+     * The PersistentManager manager does not need to create session data
+     * because it reads it from the Store.
+     */                                                                         
+    public Session createEmptySession();
+
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id will be assigned by this method, and available via the getId()
+     * method of the returned session.  If a new session cannot be created
+     * for any reason, return <code>null</code>.
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    public Session createSession();
+
+    // START S1AS8PE 4817642
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties, using the specified
+     * session id.
+     *
+     * @param sessionId the session id to assign to the new session
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     *
+     * @return the new session, or <code>null</code> if a session with the
+     * requested id already exists
+     */
+    public Session createSession(String sessionId);
+    // END S1AS8PE 4817642
+
+    /**
+     * Return the active Session, associated with this Manager, with the
+     * specified session id (if any); otherwise return <code>null</code>.
+     *
+     * @param id The session id for the session to be returned
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    public Session findSession(String id) throws IOException;
+
+    /**
+     * Finds and returns the session with the given id that also satisfies
+     * the given version requirement.
+     *
+     * This overloaded version of findSession() will be invoked only if
+     * isSessionVersioningSupported() returns true. By default, this method
+     * delegates to the version of findSession() that does not take any
+     * session version number.
+     *
+     * @param id The session id to match
+     * @param version The session version requirement to satisfy
+     *
+     * @return The session that matches the given id and also satisfies the
+     * given version requirement, or null if no such session could be found
+     * by this session manager
+     *
+     * @exception IOException if an IO error occurred
+     */
+    public Session findSession(String id, String version) throws IOException;
+
+    /**
+     * Gets the session with the given id from the given request.
+     *
+     * @param id the session id
+     * @param request the request containing the requested session information
+     * @return the requested session, or null if not found
+     * @throws IOException
+     */
+    public Session findSession(String id, HttpServletRequest request) throws IOException;
+
+    /**
+     * Returns true if this session manager supports session versioning, false
+     * otherwise.
+     *
+     * @return true if this session manager supports session versioning, false
+     * otherwise.
+     */
+    public boolean isSessionVersioningSupported();
+
+    /**
+     * Return the set of active Sessions associated with this Manager.
+     * If this Manager has no active Sessions, a zero-length array is returned.
+     */
+    public Session[] findSessions();
+
+    /**
+     * Load any currently active sessions that were previously unloaded
+     * to the appropriate persistence mechanism, if any.  If persistence is not
+     * supported, this method returns without doing anything.
+     *
+     * @exception ClassNotFoundException if a serialized class cannot be
+     *  found during the reload
+     * @exception IOException if an input/output error occurs
+     */
+    public void load() throws ClassNotFoundException, IOException;
+
+    /**
+     * Remove this Session from the active Sessions for this Manager.
+     *
+     * @param session Session to be removed
+     */
+    public void remove(Session session);
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+    /**
+     * Save any currently active sessions in the appropriate persistence
+     * mechanism, if any.  If persistence is not supported, this method
+     * returns without doing anything.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void unload() throws IOException;
+
+    //PWC Extension
+    //START OF RIMOD# 4820359 -- Support for iWS6.0 session managers
+    /**
+     * Perform any operations when the request is finished.
+     */
+    public void update(HttpSession session) throws Exception;
+    //END OF RIMOD# 4820359
+
+    //START OF 6364900
+    public boolean lockSession(ServletRequest request) throws ServletException;
+    public void unlockSession(ServletRequest request);
+    public void preRequestDispatcherProcess(ServletRequest request, ServletResponse response);
+    public void postRequestDispatcherProcess(ServletRequest request, ServletResponse response);
+    //END OF 6364900
+
+    /**
+     * Converts the given session into a cookie as a way of persisting it.
+     *
+     * @param session the session to convert
+     * @return the cookie representation of the given session
+     * @throws IOException
+     */
+    public Cookie toCookie(Session session) throws IOException;
+
+    /**
+     * Checks the given session attribute name and value to make sure they comply with any
+     * restrictions set forth by this session manager.
+     *
+     * For example, in the case of cookie-based persistence, session attribute values must be
+     * of type String.
+     *
+     * @param name the session attribute name
+     * @param value the session attribute value
+     * @throws IllegalArgumentException if the given session attribute name or value violate
+     * any restrictions set forth by this session manager
+     */
+    public void checkSessionAttribute(String name, Object value) throws IllegalArgumentException;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Pipeline.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Pipeline.java
new file mode 100644
index 0000000..ad3c871
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Pipeline.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import org.glassfish.web.valve.GlassFishValve;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+/**
+ * <p>Interface describing a collection of Valves that should be executed
+ * in sequence when the <code>invoke()</code> method is invoked.  It is
+ * required that a Valve somewhere in the pipeline (usually the last one)
+ * must process the request and create the corresponding response, rather
+ * than trying to pass the request on.</p>
+ *
+ * <p>There is generally a single Pipeline instance associated with each
+ * Container.  The container's normal request processing functionality is
+ * generally encapsulated in a container-specific Valve, which should always
+ * be executed at the end of a pipeline.  To facilitate this, the
+ * <code>setBasic()</code> method is provided to set the Valve instance that
+ * will always be executed last.  Other Valves will be executed in the order
+ * that they were added, before the basic Valve is executed.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Peter Donald
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:18 $
+ */
+
+public interface Pipeline {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * <p>Return the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).
+     */
+    public GlassFishValve getBasic();
+
+
+    /**
+     * <p>Set the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).  Prior to setting the basic Valve,
+     * the Valve's <code>setContainer()</code> will be called, if it
+     * implements <code>Contained</code>, with the owning Container as an
+     * argument.  The method may throw an <code>IllegalArgumentException</code>
+     * if this Valve chooses not to be associated with this Container, or
+     * <code>IllegalStateException</code> if it is already associated with
+     * a different Container.</p>
+     *
+     * @param valve Valve to be distinguished as the basic Valve
+     */
+    public void setBasic(GlassFishValve valve);
+
+
+    /**
+     * @return true if this pipeline has any non basic valves, false
+     * otherwise
+     */
+    public boolean hasNonBasicValves();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Add a new Valve to the end of the pipeline associated with this
+     * Container.  Prior to adding the Valve, the Valve's
+     * <code>setContainer()</code> method will be called, if it implements
+     * <code>Contained</code>, with the owning Container as an argument.
+     * The method may throw an
+     * <code>IllegalArgumentException</code> if this Valve chooses not to
+     * be associated with this Container, or <code>IllegalStateException</code>
+     * if it is already associated with a different Container.</p>
+     *
+     * @param valve Valve to be added
+     *
+     * @exception IllegalArgumentException if this Container refused to
+     *  accept the specified Valve
+     * @exception IllegalArgumentException if the specified Valve refuses to be
+     *  associated with this Container
+     * @exception IllegalStateException if the specified Valve is already
+     *  associated with a different Container
+     */
+    public void addValve(GlassFishValve valve);
+
+
+    /**
+     * Add Tomcat-style valve.
+     *
+     * @param valve the Tomcat-style valve to be added
+     */
+    public void addValve(Valve valve);
+
+
+    /**
+     * Return the set of Valves in the pipeline associated with this
+     * Container, including the basic Valve (if any).  If there are no
+     * such Valves, a zero-length array is returned.
+     */
+    public GlassFishValve[] getValves();
+
+
+    /**
+     * Cause the specified request and response to be processed by the Valves
+     * associated with this pipeline, until one of these valves causes the
+     * response to be created and returned.  The implementation must ensure
+     * that multiple simultaneous requests (on different threads) can be
+     * processed through the same Pipeline without interfering with each
+     * other's control flow.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception is thrown
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+    /**
+     * Remove the specified Valve from the pipeline associated with this
+     * Container, if it is found; otherwise, do nothing.  If the Valve is
+     * found and removed, the Valve's <code>setContainer(null)</code> method
+     * will be called if it implements <code>Contained</code>.
+     *
+     * @param valve Valve to be removed
+     */
+    public void removeValve(GlassFishValve valve);
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Realm.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Realm.java
new file mode 100644
index 0000000..7b6aa48
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Realm.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.jvnet.hk2.annotations.Contract;
+
+import org.glassfish.hk2.api.PerLookup;
+
+import javax.servlet.http.HttpServletRequest;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletResponse;
+/**
+ * A <b>Realm</b> is a read-only facade for an underlying security realm
+ * used to authenticate individual users, and identify the security roles
+ * associated with those users.  Realms can be attached at any Container
+ * level, but will typically only be attached to a Context, or higher level,
+ * Container.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.6 $ $Date: 2007/04/18 17:27:22 $
+ */
+
+@Contract
+@PerLookup
+public interface Realm {
+
+    // ------------------------------------------------------------- Constants
+
+    //START SJSAS 6202703
+    /**
+     * Flag indicating authentication is needed for current request.  Used by
+     * preAuthenticateCheck method.
+     */
+    public static final int AUTHENTICATE_NEEDED = 1;
+    
+    /**
+     * Flag indicating authentication is not needed for current request. Used
+     * by preAuthenticateCheck method.
+     */
+    public static final int AUTHENTICATE_NOT_NEEDED = 0;
+    
+    /**
+     * Flag indicating the user has been authenticated but been denied access
+     * to the requested resource.
+     */
+    public static final int AUTHENTICATED_NOT_AUTHORIZED = -1;
+
+    //END SJSAS 6202703
+    
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container with which this Realm has been associated.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the Container with which this Realm has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container);
+
+
+    /**
+     * Return descriptive information about this Realm implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    // --------------------------------------------------------- Public Methods
+
+    
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, char[] credentials);
+
+
+    /**
+     * Return the Principal associated with the specified username, which
+     * matches the digest calculated using the given parameters using the
+     * method described in RFC 2069; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param digest Digest which has been submitted by the client
+     * @param nonce Unique (or supposedly unique) token which has been used
+     * for this request
+     * @param realm Realm name
+     * @param md5a2 Second MD5 digest used to calculate the digest :
+     * MD5(Method + ":" + uri)
+     */
+    public Principal authenticate(String username, char[] digest,
+                                  String nonce, String nc, String cnonce,
+                                  String qop, String realm,
+                                  char[] md5a2);
+
+
+    /**
+     * Return the Principal associated with the specified chain of X509
+     * client certificates.  If there is none, return <code>null</code>.
+     *
+     * @param certs Array of client certificates, with the first one in
+     *  the array being the certificate of the client itself.
+     */
+    public Principal authenticate(X509Certificate certs[]);
+    
+    /**
+     * Return the SecurityConstraints configured to guard the request URI for
+     * this request, or <code>null</code> if there is no such constraint.
+     *
+     * @param request Request we are processing
+     */
+    public SecurityConstraint[] findSecurityConstraints(HttpRequest request,
+                                                        Context context);
+
+    /**
+     * Gets the security constraints configured by the given context
+     * for the given request URI and method.
+     *
+     * @param uri the request URI
+     * @param method the request method
+     * @param context the context
+     *
+     * @return the security constraints configured by the given context
+     * for the given request URI and method, or null
+     */
+    public SecurityConstraint[] findSecurityConstraints(String uri, 
+                                                        String method,
+                                                        Context context);
+
+    /**
+     * Perform access control based on the specified authorization constraint.
+     * Return <code>true</code> if this constraint is satisfied and processing
+     * should continue, or <code>false</code> otherwise.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraint Security constraint we are enforcing
+     * @param context Context to which client of this class is attached.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean hasResourcePermission(HttpRequest request,
+                                         HttpResponse response,
+                                         SecurityConstraint[] constraint,
+                                         Context context)
+        throws IOException;
+    
+    
+    /**
+     * Return <code>true</code> if the specified Principal has the specified
+     * security role, within the context of this Realm; otherwise return
+     * <code>false</code>.
+     *
+     * @param principal Principal for whom the role is to be checked
+     * @param role Security role to be checked
+     */
+    public boolean hasRole(Principal principal, String role);
+
+    //START SJSAS 6232464
+    /**
+     * Return <code>true</code> if the specified Principal has the specified
+     * security role, within the context of this Realm; otherwise return
+     * <code>false</code>.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param principal Principal for whom the role is to be checked
+     * @param role Security role to be checked
+     */
+    public boolean hasRole(HttpRequest request, 
+                           HttpResponse response, 
+                           Principal principal, 
+                           String role);
+    //END SJSAS 6232464
+    
+    //START SJSAS 6202703
+    /**
+     * Checks whether or not authentication is needed.
+     * Returns an int, one of AUTHENTICATE_NOT_NEEDED, AUTHENTICATE_NEEDED,
+     * or AUTHENTICATED_NOT_AUTHORIZED.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraints Security constraint we are enforcing
+     * @param disableProxyCaching whether or not to disable proxy caching for
+     *        protected resources.
+     * @param securePagesWithPragma true if we add headers which 
+     * are incompatible with downloading office documents in IE under SSL but
+     * which fix a caching problem in Mozill
+     * @param ssoEnabled true if sso is enabled
+     * @exception IOException if an input/output error occurs
+     */
+    public int preAuthenticateCheck(HttpRequest request,
+                                    HttpResponse response,
+                                    SecurityConstraint[] constraints,
+                                    boolean disableProxyCaching,
+                                    boolean securePagesWithPragma,
+                                    boolean ssoEnabled)
+        throws IOException;    
+    
+    
+    /**
+     * Authenticates the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * requirements have been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param context The Context to which client of this class is attached.
+     * @param authenticator the current authenticator.
+     * @param calledFromAuthenticate true if the call originates from
+     *                               HttpServletRequest.authenticate
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean invokeAuthenticateDelegate(HttpRequest request,
+                                              HttpResponse response,
+                                              Context context,
+                                              Authenticator authenticator,
+                                              boolean calledFromAuthenticate)
+          throws IOException;
+
+    /**
+     * Post authentication for given request and response.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param context The Context to which client of this class is attached.
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean invokePostAuthenticateDelegate(HttpRequest request,
+                                              HttpResponse response,
+                                              Context context)
+           throws IOException;
+    
+    //END SJSAS 6202703
+
+        /**
+     * Enforce any user data constraint required by the security constraint
+     * guarding this request URI.  Return <code>true</code> if this constraint
+     * was not violated and processing should continue, or <code>false</code>
+     * if we have created a response already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraint Security constraint being checked
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean hasUserDataPermission(HttpRequest request,
+        HttpResponse response, SecurityConstraint[] constraint)
+            throws IOException;
+    
+    /**
+     * Checks if the given request URI and method are the target of any
+     * user-data-constraint with a transport-guarantee of CONFIDENTIAL,
+     * and whether any such constraint is already satisfied.
+     * 
+     * If <tt>uri</tt> and <tt>method</tt> are null, then the URI and method
+     * of the given <tt>request</tt> are checked.
+     *
+     * If a user-data-constraint exists that is not satisfied, then the 
+     * given <tt>request</tt> will be redirected to HTTPS.
+     *
+     * @param request the request that may be redirected
+     * @param response the response that may be redirected
+     * @param constraints the security constraints to check against
+     * @param uri the request URI (minus the context path) to check
+     * @param method the request method to check
+     *
+     * @return true if the request URI and method are not the target of any
+     * unsatisfied user-data-constraint with a transport-guarantee of
+     * CONFIDENTIAL, and false if they are (in which case the given request
+     * will have been redirected to HTTPS)
+     */
+    public boolean hasUserDataPermission(HttpRequest request,
+                                         HttpResponse response,
+                                         SecurityConstraint[] constraints,
+                                         String uri, String method)
+        throws IOException;
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+    // BEGIN IASRI 4808401, 4934562
+    /**
+     * Return an alternate principal from the request if available.
+     *
+     * @param req The request object.
+     * @return Alternate principal or null.
+     */
+    public Principal getAlternatePrincipal(HttpRequest req);
+
+
+    /**
+     * Return an alternate auth type from the request if available.
+     *
+     * @param req The request object.
+     * @return Alternate auth type or null.
+     */
+    public String getAlternateAuthType(HttpRequest req);
+    // END IASRI 4808401
+
+
+    // BEGIN IASRI 4856062,4918627,4874504
+    /**
+     * Set the name of the associated realm.
+     *
+     * @param name the name of the realm.
+     */
+    public void setRealmName(String name, String authMethod);
+ 
+ 
+    /**
+     * Returns the name of the associated realm.
+     *
+     * @return realm name or null if not set.
+     */
+    public String getRealmName();
+    // END IASRI 4856062,4918627,4874504
+    /**
+     * Does digest authentication and returns the Principal associated with the username in the 
+     * HTTP header.
+     *
+     * @param hreq HTTP servlet request.
+     */
+    public Principal authenticate(HttpServletRequest hreq);
+    
+    /**
+     * Returns whether the specified ServletContext indicates that security
+     * extension is enabled.
+     * 
+     * @param servletContext the ServletContext
+     * @return true if security extension is enabled; false otherwise
+     */
+    public boolean isSecurityExtensionEnabled(ServletContext servletContext);
+
+    /**
+     * Logs out.
+     * 
+     * @param hreq the HttpRequest
+     */
+    public void logout(HttpRequest hreq);
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Request.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Request.java
new file mode 100644
index 0000000..a5a5e3f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Request.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.util.Iterator;
+
+/**
+ * A <b>Request</b> is the Catalina-internal facade for a
+ * <code>ServletRequest</code> that is to be processed, in order to
+ * produce the corresponding <code>Response</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.4 $ $Date: 2006/04/24 16:36:12 $
+ */
+
+public interface Request {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the authorization credentials sent with this request.
+     */
+    public String getAuthorization();
+
+
+    /**
+     * Return the Connector through which this Request was received.
+     */
+    public Connector getConnector();
+
+
+    /**
+     * Set the Connector through which this Request was received.
+     *
+     * @param connector The new connector
+     */
+    public void setConnector(Connector connector);
+
+
+    /**
+     * Return the Context within which this Request is being processed.
+     */
+    public Context getContext();
+
+
+    /**
+     * 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);
+
+
+    /**
+     * Get filter chain associated with the request.
+     */
+    public FilterChain getFilterChain();
+
+
+    /**
+     * Set filter chain associated with the request.
+     * 
+     * @param filterChain new filter chain
+     */
+    public void setFilterChain(FilterChain filterChain);
+
+
+    /**
+     * Return the Host within which this Request is being processed.
+     */
+    public Host getHost();
+
+
+    /**
+     * Set the Host within which this Request is being processed.  This
+     * must be called as soon as the appropriate Host is identified, and
+     * before the Request is passed to a context.
+     *
+     * @param host The newly associated Host
+     */
+    public void setHost(Host host);
+
+
+    /**
+     * 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 the <code>ServletRequest</code> for which this object
+     * is the facade.
+     */
+    public ServletRequest getRequest();
+
+
+    /**
+     * Return the <code>ServletRequest</code> for which this object
+     * is the facade.
+     *
+     * @param maskDefaultContextMapping true if the fact that a request
+     * received at the root context was mapped to a default-web-module will
+     * be masked, false otherwise
+     */
+    public ServletRequest getRequest(boolean maskDefaultContextMapping);
+
+
+    /**
+     * Return the Response with which this Request is associated.
+     */
+    public Response getResponse();
+
+
+    /**
+     * Set the Response with which this Request is associated.
+     *
+     * @param response The new associated response
+     */
+    public void setResponse(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();
+
+
+    /**
+     * 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);
+
+
+    /**
+     * Return the input stream associated with this Request.
+     */
+    public InputStream getStream();
+
+
+    /**
+     * Set the input stream associated with this Request.
+     *
+     * @param stream The new input stream
+     */
+    public void setStream(InputStream stream);
+
+
+    /**
+     * Return the Wrapper within which this Request is being processed.
+     */
+    public Wrapper getWrapper();
+
+
+    /**
+     * 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);
+
+
+    // --------------------------------------------------------- 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;
+
+
+    /**
+     * 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;
+
+
+    /**
+     * 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 an Iterator containing the String names of all notes bindings
+     * that exist for this request.
+     */
+    public Iterator getNoteNames();
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle();
+
+
+    /**
+     * 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);
+
+
+    /**
+     * Set the content length associated with this Request.
+     *
+     * @param length The new content length
+     */
+    public void setContentLength(int length);
+
+
+    /**
+     * 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);
+
+
+    /**
+     * 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);
+
+
+    /**
+     * Set the protocol name and version associated with this Request.
+     *
+     * @param protocol Protocol name and version
+     */
+    public void setProtocol(String protocol);
+
+
+    /**
+     * Set the remote IP address associated with this Request.  NOTE:  This
+     * value will be used to resolve the value for <code>getRemoteHost()</code>
+     * if that method is called.
+     *
+     * @param remote The remote IP address
+     */
+    public void setRemoteAddr(String remote);
+
+
+    /**
+     * Set the value to be returned by <code>isSecure()</code>
+     * for this Request.
+     *
+     * @param secure The new isSecure value
+     */
+    public void setSecure(boolean secure);
+
+
+    /**
+     * Set the name of the server (virtual host) to process this request.
+     *
+     * @param name The server name
+     */
+    public void setServerName(String name);
+
+
+    /**
+     * Set the port number of the server to process this request.
+     *
+     * @param port The server port
+     */
+    public void setServerPort(int port);
+
+
+    // START CR 6415120
+    /**
+     * Set whether or not access to resources under WEB-INF or META-INF
+     * needs to be checked.
+     */
+    public void setCheckRestrictedResources(boolean check);
+
+
+    /**
+     * Return whether or not access to resources under WEB-INF or META-INF
+     * needs to be checked.
+     */
+    public boolean getCheckRestrictedResources();
+    // END CR 6415120
+
+
+    // START SJSAS 6346226
+    /**
+     * Gets the jroute id of this request, which may have been 
+     * sent as a separate <code>JROUTE</code> cookie or appended to the
+     * session identifier encoded in the URI (if cookies have been disabled).
+     * 
+     * @return The jroute id of this request, or null if this request does not
+     * carry any jroute id
+     */
+    public String getJrouteId();
+    // END SJSAS 6346226
+
+
+    /**
+     * Generate and return a new session ID.
+     *
+     * This hook allows connectors to provide their own scalable session
+     * ID generators.
+     */
+    public String generateSessionId();
+
+
+    /**
+     * Disables async support on this request.
+     */
+    public void disableAsyncSupport();
+
+
+    /**
+     * Sets the requested session cookie path, see IT 7426
+     */
+    public void setRequestedSessionCookiePath(String cookiePath);
+
+
+    /**
+     * Gets the session associated with this Request, creating one
+     * if necessary and requested.
+     *
+     * @param create true if a new session is to be created if one does not
+     * already exist, false otherwise
+     */
+    public Session getSessionInternal(boolean create);
+
+
+    /**
+     * Change the ID of the session that this request is associated with. There
+     * are several things that may trigger an ID change. These include moving
+     * between nodes in a cluster and session fixation prevention during the
+     * authentication process.
+     */
+    public String changeSessionId();
+
+    public Session lockSession();
+
+    public void unlockSession();
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Response.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Response.java
new file mode 100644
index 0000000..c9810cf
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Response.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+
+/**
+ * A <b>Response</b> is the Catalina-internal facade for a
+ * <code>ServletResponse</code> that is to be produced,
+ * based on the processing of a corresponding <code>Request</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:19 $
+ */
+
+public interface Response {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Connector through which this Response is returned.
+     */
+    public Connector getConnector();
+
+
+    /**
+     * Set the Connector through which this Response is returned.
+     *
+     * @param connector The new connector
+     */
+    public void setConnector(Connector connector);
+
+
+    /**
+     * Return the number of bytes actually written to the output stream.
+     */
+    public int getContentCount();
+
+
+    /**
+     * Return the Context with which this Response is associated.
+     */
+    public Context getContext();
+
+
+    /**
+     * Set the Context with which this Response is associated.  This should
+     * be called as soon as the appropriate Context is identified.
+     *
+     * @param context The associated Context
+     */
+    public void setContext(Context context);
+
+
+    /**
+     * Set the application commit flag.
+     * 
+     * @param appCommitted The new application committed flag value
+     */
+    public void setAppCommitted(boolean appCommitted);
+
+
+    /**
+     * Application commit flag accessor.
+     */
+    public boolean isAppCommitted();
+
+
+    /**
+     * Return the "processing inside an include" flag.
+     */
+    public boolean getIncluded();
+
+
+    /**
+     * 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);
+
+
+    /**
+     * 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 the Request with which this Response is associated.
+     */
+    public Request getRequest();
+
+
+    /**
+     * Set the Request with which this Response is associated.
+     *
+     * @param request The new associated request
+     */
+    public void setRequest(Request request);
+
+
+    /**
+     * Return the <code>ServletResponse</code> for which this object
+     * is the facade.
+     */
+    public ServletResponse getResponse();
+
+
+    /**
+     * Return the output stream associated with this Response.
+     */
+    public OutputStream getStream();
+
+
+    /**
+     * Set the output stream associated with this Response.
+     *
+     * @param stream The new output stream
+     */
+    public void setStream(OutputStream stream);
+
+
+    /**
+     * Set the suspended flag.
+     * 
+     * @param suspended The new suspended flag value
+     */
+    public void setSuspended(boolean suspended);
+
+
+    /**
+     * Suspended flag accessor.
+     */
+    public boolean isSuspended();
+
+
+    /**
+     * Set the error flag.
+     */
+    public void setError();
+
+
+    /**
+     * Error flag accessor.
+     */
+    public boolean isError();
+
+
+    // BEGIN S1AS 4878272
+    /**
+     * Sets detail error message.
+     *
+     * @param message detail error message
+     */
+    public void setDetailMessage(String message);
+
+
+    /**
+     * Gets detail error message.
+     *
+     * @return the detail error message
+     */
+    public String getDetailMessage();
+    // END S1AS 4878272
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * 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;
+
+
+    /**
+     * 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;
+
+
+    /**
+     * Return the content length that was set or calculated for this Response.
+     */
+    public int 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 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.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public PrintWriter getReporter() throws IOException;
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle();
+
+
+    /**
+     * Reset the data buffer but not any status or header information.
+     */
+    public void resetBuffer();
+
+    
+    /**
+     * Reset the data buffer and the using Writer/Stream flags but not any
+     * status or header information.
+     */
+    public void resetBuffer(boolean resetWriterStreamFlags);
+    
+    
+    /**
+     * Send an acknowledgment of a request.
+     * 
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendAcknowledgement()
+        throws IOException;
+
+
+    /**
+     * Apply URL Encoding to the given URL without adding session identifier
+     * et al associated to this response.
+     *
+     * @param url URL to be encoded
+     */
+    public String encode(String url);
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Server.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Server.java
new file mode 100644
index 0000000..2f26748
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Server.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import org.apache.catalina.deploy.NamingResources;
+
+/**
+ * A <code>Server</code> element represents the entire Catalina
+ * servlet container.  Its attributes represent the characteristics of
+ * the servlet container as a whole.  A <code>Server</code> may contain
+ * one or more <code>Services</code>, and the top level set of naming
+ * resources.
+ * <p>
+ * Normally, an implementation of this interface will also implement
+ * <code>Lifecycle</code>, such that when the <code>start()</code> and
+ * <code>stop()</code> methods are called, all of the defined
+ * <code>Services</code> are also started or stopped.
+ * <p>
+ * In between, the implementation must open a server socket on the port number
+ * specified by the <code>port</code> property.  When a connection is accepted,
+ * the first line is read and compared with the specified shutdown command.
+ * If the command matches, shutdown of the server is initiated.
+ * <p>
+ * <strong>NOTE</strong> - The concrete implementation of this class should
+ * register the (singleton) instance with the <code>ServerFactory</code>
+ * class in its constructor(s).
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:19 $
+ */
+
+public interface Server {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Server implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the global naming resources.
+     */
+    public NamingResources getGlobalNamingResources();
+
+
+    /**
+     * Set the global naming resources.
+     * 
+     * @param globalNamingResources The new global naming resources
+     */
+    public void setGlobalNamingResources
+        (NamingResources globalNamingResources);
+
+
+    /**
+     * Return the port number we listen to for shutdown commands.
+     */
+    public int getPort();
+
+
+    /**
+     * Set the port number we listen to for shutdown commands.
+     *
+     * @param port The new port number
+     */
+    public void setPort(int port);
+
+
+    /**
+     * Return the shutdown command string we are waiting for.
+     */
+    public String getShutdown();
+
+
+    /**
+     * Set the shutdown command we are waiting for.
+     *
+     * @param shutdown The new shutdown command
+     */
+    public void setShutdown(String shutdown);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new Service to the set of defined Services.
+     *
+     * @param service The Service to be added
+     */
+    public void addService(Service service);
+
+
+    /**
+     * Wait until a proper shutdown command is received, then return.
+     */
+    public void await();
+
+
+    /**
+     * Return the specified Service (if it exists); otherwise return
+     * <code>null</code>.
+     *
+     * @param name Name of the Service to be returned
+     */
+    public Service findService(String name);
+
+
+    /**
+     * Return the set of Services defined within this Server.
+     */
+    public Service[] findServices();
+
+
+    /**
+     * Remove the specified Service from the set associated from this
+     * Server.
+     *
+     * @param service The Service to be removed
+     */
+    public void removeService(Service service);
+
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     *
+     * @exception LifecycleException If this server was already initialized.
+     */
+    public void initialize()
+    throws LifecycleException;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ServerFactory.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ServerFactory.java
new file mode 100644
index 0000000..f19f37b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ServerFactory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import org.apache.catalina.core.StandardServer;
+
+
+/**
+ * <p><strong>ServerFactory</strong> allows the registration of the
+ * (singleton) <code>Server</code> instance for this JVM, so that it
+ * can be accessed independently of any existing reference to the
+ * component hierarchy.  This is important for administration tools
+ * that are built around the internal component implementation classes.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:20 $
+ */
+
+public class ServerFactory {
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The singleton <code>Server</code> instance for this JVM.
+     */
+    private static Server server = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the singleton <code>Server</code> instance for this JVM.
+     */
+    public static synchronized Server getServer() {
+        if( server==null )
+            server=new StandardServer();
+        return (server);
+
+    }
+
+
+    /**
+     * Set the singleton <code>Server</code> instance for this JVM.  This
+     * method must <strong>only</strong> be called from a constructor of
+     * the (singleton) <code>Server</code> instance that is created for
+     * this execution of Catalina.
+     *
+     * @param theServer The new singleton instance
+     */
+    public static synchronized void setServer(Server theServer) {
+
+        if (server == null)
+            server = theServer;
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Service.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Service.java
new file mode 100644
index 0000000..14fa5af
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Service.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import javax.management.NotificationBroadcasterSupport;
+
+/**
+ * A <strong>Service</strong> is a group of one or more
+ * <strong>Connectors</strong> that share a single <strong>Container</strong>
+ * to process their incoming requests.  This arrangement allows, for example,
+ * a non-SSL and SSL connector to share the same population of web apps.
+ * <p>
+ * A given JVM can contain any number of Service instances; however, they are
+ * completely independent of each other and share only the basic JVM facilities
+ * and classes on the system class path.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:20 $
+ */
+
+public interface Service {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the <code>Container</code> that handles requests for all
+     * <code>Connectors</code> associated with this Service.
+     */
+    public Container getContainer();
+
+
+    /**
+     * Set the <code>Container</code> that handles requests for all
+     * <code>Connectors</code> associated with this Service.
+     *
+     * @param container The new Container
+     */
+    public void setContainer(Container container);
+
+
+    /**
+     * Return descriptive information about this Service implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the name of this Service.
+     */
+    public String getName();
+
+
+    /**
+     * Set the name of this Service.
+     *
+     * @param name The new service name
+     */
+    public void setName(String name);
+
+
+    /**
+     * Return the <code>Server</code> with which we are associated (if any).
+     */
+    public Server getServer();
+
+
+    /**
+     * Set the <code>Server</code> with which we are associated (if any).
+     *
+     * @param server The server that owns this Service
+     */
+    public void setServer(Server server);
+
+
+    /**
+     * Return the <code>NotificationBroadcasterSupport</code> that sends notification for this Service.
+     */
+    public NotificationBroadcasterSupport getBroadcaster();
+
+
+    /**
+     * Set the <code>NotificationBroadcasterSupport</code> that sends notification for this Service
+     *
+     * @param broadcaster The new NotificationBroadcasterSupport
+     */
+    public void setBroadcaster(NotificationBroadcasterSupport broadcaster);
+
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new Connector to the set of defined Connectors, and associate it
+     * with this Service's Container.
+     *
+     * @param connector The Connector to be added
+     */
+    public void addConnector(Connector connector);
+
+
+    /**
+     * Find and return the set of Connectors associated with this Service.
+     */
+    public Connector[] findConnectors();
+
+
+    /**
+     * Remove the specified Connector from the set associated from this
+     * Service.  The removed Connector will also be disassociated from our
+     * Container.
+     *
+     * @param connector The Connector to be removed
+     */
+    // START SJSAS 6231069
+    //public void removeConnector(Connector connector);
+    public void removeConnector(Connector connector) throws LifecycleException;
+     // END SJSAS 6231069
+
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     *
+     * @exception LifecycleException If this server was already initialized.
+     */
+    public void initialize()
+    throws LifecycleException;
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Session.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Session.java
new file mode 100644
index 0000000..f8cf0d1
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Session.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import javax.servlet.http.HttpSession;
+import java.security.Principal;
+import java.util.*;
+
+
+/**
+ * A <b>Session</b> is the Catalina-internal facade for an
+ * <code>HttpSession</code> that is used to maintain state information
+ * between requests for a particular user of a web application.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:27:20 $
+ */
+
+public interface Session {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The SessionEvent event type when a session is created.
+     */
+    public static final String SESSION_CREATED_EVENT = "createSession";
+
+
+    /**
+     * The SessionEvent event type when a session is destroyed.
+     */
+    public static final String SESSION_DESTROYED_EVENT = "destroySession";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the authentication type used to authenticate our cached
+     * Principal, if any.
+     */
+    public String getAuthType();
+
+
+    /**
+     * Set the authentication type used to authenticate our cached
+     * Principal, if any.
+     *
+     * @param authType The new cached authentication type
+     */
+    public void setAuthType(String authType);
+
+
+    /**
+     * Return the creation time for this session.
+     */
+    public long getCreationTime();
+
+
+    /**
+     * Set the creation time for this session.  This method is called by the
+     * Manager when an existing Session instance is reused.
+     *
+     * @param time The new creation time
+     */
+    public void setCreationTime(long time);
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getId();
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getIdInternal();
+
+
+    /**
+     * Set the session identifier for this session.
+     *
+     * @param id The new session identifier
+     */
+    public void setId(String id);
+
+
+    /**
+     * Return descriptive information about this Session implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the last time the client sent a request associated with this
+     * session, as the number of milliseconds since midnight, January 1, 1970
+     * GMT.  Actions that your application takes, such as getting or setting
+     * a value associated with the session, do not affect the access time.
+     */
+    public long getLastAccessedTime();
+
+
+    /**
+     * Return the Manager within which this Session is valid.
+     */
+    public Manager getManager();
+
+
+    /**
+     * Set the Manager within which this Session is valid.
+     *
+     * @param manager The new Manager
+     */
+    public void setManager(Manager manager);
+
+
+    /**
+     * Return the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session.  A negative
+     * time indicates that the session should never time out.
+     */
+    public int getMaxInactiveInterval();
+
+
+    /**
+     * Set the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session.  A negative
+     * time indicates that the session should never time out.
+     *
+     * @param interval The new maximum interval
+     */
+    public void setMaxInactiveInterval(int interval);
+
+
+    /**
+     * Set the <code>isNew</code> flag for this session.
+     *
+     * @param isNew The new value for the <code>isNew</code> flag
+     */
+    public void setNew(boolean isNew);
+
+
+    /**
+     * Return the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.  If there
+     * is no current associated Principal, return <code>null</code>.
+     */
+    public Principal getPrincipal();
+
+
+    /**
+     * Set the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.
+     *
+     * @param principal The new Principal, or <code>null</code> if none
+     */
+    public void setPrincipal(Principal principal);
+
+
+    /**
+     * Return the <code>HttpSession</code> for which this object
+     * is the facade.
+     */
+    public HttpSession getSession();
+
+
+    /**
+     * Set the <code>isValid</code> flag for this session.
+     *
+     * @param isValid The new value for the <code>isValid</code> flag
+     */
+    public void setValid(boolean isValid);
+
+
+    /**
+     * Expire the expired session if necessary and
+     * return the <code>isValid</code> flag for this session.
+     */
+    public boolean isValid();
+
+
+    /**
+     * Return the <code>isValid</code> flag for this session.
+     */
+    public boolean getIsValid();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Update the accessed time information for this session.  This method
+     * should be called by the context when a request comes in for a particular
+     * session, even if the application does not reference it.
+     */
+    public void access();
+
+
+    /**
+     * Add a session event listener to this component.
+     */
+    public void addSessionListener(SessionListener listener);
+
+
+    /**
+     * End access to the session.
+     */
+    public void endAccess();
+
+
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     */
+    public void expire();
+
+
+    /**
+     * Return the object bound with the specified name to the internal notes
+     * for this session, or <code>null</code> if no such binding exists.
+     *
+     * @param name Name of the note to be returned
+     */
+    public Object getNote(String name);
+
+
+    /**
+     * Return an Iterator containing the String names of all notes bindings
+     * that exist for this session.
+     */
+    public Iterator getNoteNames();
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle();
+
+
+    /**
+     * Remove any object bound to the specified name in the internal notes
+     * for this session.
+     *
+     * @param name Name of the note to be removed
+     */
+    public void removeNote(String name);
+
+
+    /**
+     * Remove a session event listener from this component.
+     */
+    public void removeSessionListener(SessionListener listener);
+
+
+    /**
+     * Bind an object to a specified name in the internal notes associated
+     * with this session, 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);
+
+
+    // START SJSAS 6329289
+    /**
+     * Checks whether this Session has expired.
+     *
+     * @return true if this Session has expired, false otherwise
+     */
+    public boolean hasExpired();
+    // END SJSAS 6329289
+
+
+    /** 
+     * Gets the version number of this Session
+     */    
+    public long getVersion();
+
+    /**
+     * Gets the attributes of this session.
+     *
+     * @return the attributes of this session
+     */
+    public Map<String, Object> getAttributes();
+
+
+    /**
+     * Return the single sign on id.
+     * It is null if there is no SSO.
+     */
+    public String getSsoId();
+
+
+    /**
+     * Set the single sign on id.
+     */
+    public void setSsoId(String ssoId);
+
+
+    /**
+     * Return the single sign on version.
+     */
+    public long getSsoVersion();
+
+
+    /**
+     * Set the single sign on version.
+     */
+    public void setSsoVersion(long ssoVersion);
+
+
+    /**
+     * lock the session for background
+     * returns true if successful; false if unsuccessful
+     */     
+    public boolean lockForeground();
+    
+
+    /**
+     * unlock the session from foreground
+     */      
+    public void unlockForeground(); 
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/SessionEvent.java b/appserver/web/web-core/src/main/java/org/apache/catalina/SessionEvent.java
new file mode 100644
index 0000000..eef5b6d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/SessionEvent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import java.util.EventObject;
+
+
+/**
+ * General event for notifying listeners of significant changes on a Session.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:20 $
+ */
+
+public final class SessionEvent
+    extends EventObject {
+
+
+    /**
+     * The event data associated with this event.
+     */
+    private Object data = null;
+
+
+    /**
+     * The Session on which this event occurred.
+     */
+    private Session session = null;
+
+
+    /**
+     * The event type this instance represents.
+     */
+    private String type = null;
+
+
+    /**
+     * Construct a new SessionEvent with the specified parameters.
+     *
+     * @param session Session on which this event occurred
+     * @param type Event type
+     * @param data Event data
+     */
+    public SessionEvent(Session session, String type, Object data) {
+
+        super(session);
+        this.session = session;
+        this.type = type;
+        this.data = data;
+
+    }
+
+
+    /**
+     * Return the event data of this event.
+     */
+    public Object getData() {
+
+        return (this.data);
+
+    }
+
+
+    /**
+     * Return the Session on which this event occurred.
+     */
+    public Session getSession() {
+
+        return (this.session);
+
+    }
+
+
+    /**
+     * Return the event type of this event.
+     */
+    public String getType() {
+
+        return (this.type);
+
+    }
+
+
+    /**
+     * Return a string representation of this event.
+     */
+    public String toString() {
+
+        return ("SessionEvent['" + getSession() + "','" +
+                getType() + "']");
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/SessionListener.java b/appserver/web/web-core/src/main/java/org/apache/catalina/SessionListener.java
new file mode 100644
index 0000000..d687313
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/SessionListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+/**
+ * Interface defining a listener for significant Session generated events.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:20 $
+ */
+
+public interface SessionListener {
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event SessionEvent that has occurred
+     */
+    public void sessionEvent(SessionEvent event);
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/SessionLocker.java b/appserver/web/web-core/src/main/java/org/apache/catalina/SessionLocker.java
new file mode 100755
index 0000000..a84eb77
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/SessionLocker.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * SessionLocker.java
+ *
+ * Created on January 18, 2006, 4:39 PM
+ */
+
+package org.apache.catalina;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+
+/**
+ *
+ * @author  Administrator
+ */
+public interface SessionLocker {
+    
+    public void init(Context context);
+    
+    public boolean lockSession(ServletRequest req) throws ServletException;
+    
+    public void unlockSession(ServletRequest req);
+    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Store.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Store.java
new file mode 100644
index 0000000..f10af04
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Store.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+
+
+/**
+ * A <b>Store</b> is the abstraction of a Catalina component that provides
+ * persistent storage and loading of Sessions and their associated user data.
+ * Implementations are free to save and load the Sessions to any media they
+ * wish, but it is assumed that saved Sessions are persistent across
+ * server or context restarts.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:21 $
+ */
+
+public interface Store {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Store implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the Manager instance associated with this Store.
+     */
+    public Manager getManager();
+
+
+    /**
+     * Set the Manager associated with this Store.
+     *
+     * @param manager The Manager which will use this Store.
+     */
+    public void setManager(Manager manager);
+
+
+    /**
+     * Return the number of Sessions present in this Store.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public int getSize() throws IOException;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Return an array containing the session identifiers of all Sessions
+     * currently saved in this Store.  If there are no such Sessions, a
+     * zero-length array is returned.
+     *
+     * @exception IOException if an input/output error occurred
+     */
+    public String[] keys() throws IOException;
+
+
+    /**
+     * Load and return the Session associated with the specified session
+     * identifier from this Store, without removing it.  If there is no
+     * such stored Session, return <code>null</code>.
+     *
+     * @param id Session identifier of the session to load
+     *
+     * @exception ClassNotFoundException if a deserialization error occurs
+     * @exception IOException if an input/output error occurs
+     */
+    public Session load(String id)
+        throws ClassNotFoundException, IOException;
+
+
+    /**
+     * Remove the Session with the specified session identifier from
+     * this Store, if present.  If no such Session is present, this method
+     * takes no action.
+     *
+     * @param id Session identifier of the Session to be removed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void remove(String id) throws IOException;
+
+
+    /**
+     * Remove all Sessions from this Store.
+     */
+    public void clear() throws IOException;
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener);
+
+
+    /**
+     * Save the specified Session into this Store.  Any previously saved
+     * information for the associated session identifier is replaced.
+     *
+     * @param session Session to be saved
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void save(Session session) throws IOException;
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Valve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Valve.java
new file mode 100644
index 0000000..20ebc67
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Valve.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+/**
+ * <p>A <b>Valve</b> is a request processing component associated with a
+ * particular Container.  A series of Valves are generally associated with
+ * each other into a Pipeline.  The detailed contract for a Valve is included
+ * in the description of the <code>invoke()</code> method below.</p>
+ *
+ * <b>HISTORICAL NOTE</b>:  The "Valve" name was assigned to this concept
+ * because a valve is what you use in a real world pipeline to control and/or
+ * modify flows through it.
+ *
+ * @author Craig R. McClanahan
+ * @author Gunnar Rjnning
+ * @author Peter Donald
+ * @version $Revision: 303352 $ $Date: 2004-10-05 19:12:52 +0200 (mar., 05 oct. 2004) $
+ */
+
+public interface Valve {
+
+
+    //-------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo();
+
+
+    /**
+     * Return the next Valve in the pipeline containing this Valve, if any.
+     */
+    public Valve getNext();
+
+
+    /**
+     * Set the next Valve in the pipeline containing this Valve.
+     *
+     * @param valve The new next valve, or <code>null</code> if none
+     */
+    public void setNext(Valve valve);
+
+
+    //---------------------------------------------------------- Public Methods
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess();
+
+
+    /**
+     * <p>Perform request processing as required by this Valve.</p>
+     *
+     * <p>An individual Valve <b>MAY</b> perform the following actions, in
+     * the specified order:</p>
+     * <ul>
+     * <li>Examine and/or modify the properties of the specified Request and
+     *     Response.
+     * <li>Examine the properties of the specified Request, completely generate
+     *     the corresponding Response, and return control to the caller.
+     * <li>Examine the properties of the specified Request and Response, wrap
+     *     either or both of these objects to supplement their functionality,
+     *     and pass them on.
+     * <li>If the corresponding Response was not generated (and control was not
+     *     returned, call the next Valve in the pipeline (if there is one) by
+     *     executing <code>context.invokeNext()</code>.
+     * <li>Examine, but not modify, the properties of the resulting Response
+     *     (which was created by a subsequently invoked Valve or Container).
+     * </ul>
+     *
+     * <p>A Valve <b>MUST NOT</b> do any of the following things:</p>
+     * <ul>
+     * <li>Change request properties that have already been used to direct
+     *     the flow of processing control for this request (for instance,
+     *     trying to change the virtual host to which a Request should be
+     *     sent from a pipeline attached to a Host or Context in the
+     *     standard implementation).
+     * <li>Create a completed Response <strong>AND</strong> pass this
+     *     Request and Response on to the next Valve in the pipeline.
+     * <li>Consume bytes from the input stream associated with the Request,
+     *     unless it is completely generating the response, or wrapping the
+     *     request before passing it on.
+     * <li>Modify the HTTP headers included with the Response after the
+     *     <code>invokeNext()</code> method has returned.
+     * <li>Perform any actions on the output stream associated with the
+     *     specified Response after the <code>invokeNext()</code> method has
+     *     returned.
+     * </ul>
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     * @exception ServletException if a servlet error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+    
+    /**
+     * Process a Comet event.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     * @exception ServletException if a servlet error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     */
+    public void event(Request request, Response response, CometEvent event)
+        throws IOException, ServletException;
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/Wrapper.java b/appserver/web/web-core/src/main/java/org/apache/catalina/Wrapper.java
new file mode 100644
index 0000000..d8dff50
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/Wrapper.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina;
+
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+
+
+/**
+ * A <b>Wrapper</b> is a Container that represents an individual servlet
+ * definition from the deployment descriptor of the web application.  It
+ * provides a convenient mechanism to use Interceptors that see every single
+ * request to the servlet represented by this definition.
+ * <p>
+ * Implementations of Wrapper are responsible for managing the servlet life
+ * cycle for their underlying servlet class, including calling init() and
+ * destroy() at appropriate times, as well as respecting the existence of
+ * the SingleThreadModel declaration on the servlet class itself.
+ * <p>
+ * The parent Container attached to a Wrapper will generally be an
+ * implementation of Context, representing the servlet context (and
+ * therefore the web application) within which this servlet executes.
+ * <p>
+ * Child Containers are not allowed on Wrapper implementations, so the
+ * <code>addChild()</code> method should throw an
+ * <code>IllegalArgumentException</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3.6.1 $ $Date: 2008/04/17 18:37:01 $
+ */
+
+public interface Wrapper extends Container {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the available date/time for this servlet, in milliseconds since
+     * the epoch.  If this date/time is in the future, any request for this
+     * servlet will return an SC_SERVICE_UNAVAILABLE error.  If it is zero,
+     * the servlet is currently available.  A value equal to Long.MAX_VALUE
+     * is considered to mean that unavailability is permanent.
+     */
+    public long getAvailable();
+
+
+    /**
+     * Set the available date/time for this servlet, in milliseconds since the
+     * epoch.  If this date/time is in the future, any request for this servlet
+     * will return an SC_SERVICE_UNAVAILABLE error.  A value equal to
+     * Long.MAX_VALUE is considered to mean that unavailability is permanent.
+     *
+     * @param available The new available date/time
+     */
+    public void setAvailable(long available);
+
+
+    /**
+     * Return the context-relative URI of the JSP file for this servlet.
+     */
+    public String getJspFile();
+
+
+    /**
+     * Set the context-relative URI of the JSP file for this servlet.
+     *
+     * @param jspFile JSP file URI
+     */
+    public void setJspFile(String jspFile);
+
+
+    /**
+     * Return the load-on-startup order value (negative value means
+     * load on first call).
+     */
+    public int getLoadOnStartup();
+
+
+    /**
+     * Set the load-on-startup order value (negative value means
+     * load on first call).
+     *
+     * @param value New load-on-startup value
+     */
+    public void setLoadOnStartup(int value);
+
+
+    /**
+     * Return the run-as identity for this servlet.
+     */
+    public String getRunAs();
+
+
+    /**
+     * Set the run-as identity for this servlet.
+     *
+     * @param runAs New run-as identity value
+     */
+    public void setRunAs(String runAs);
+
+
+    /**
+     * Return the fully qualified servlet class name for this servlet.
+     */
+    public String getServletClassName();
+
+
+    /**
+     * Gets the name of the wrapped servler.
+     */
+    public String getServletName();
+
+
+    /**
+     * Set the fully qualified servlet class name for this servlet.
+     *
+     * @param className Servlet class name
+     */
+    public void setServletClassName(String className);
+
+
+    /**
+     * Sets the class object from which this servlet will be instantiated.
+     *
+     * @param servletClass the class object from which the servlet will be
+     * instantiated
+     */
+    public void setServletClass(Class <? extends Servlet> servletClass);
+
+
+    /**
+     * Gets the names of the methods supported by the underlying servlet.
+     *
+     * This is the same set of methods included in the Allow response header
+     * in response to an OPTIONS request method processed by the underlying
+     * servlet.
+     *
+     * @return Array of names of the methods supported by the underlying
+     * servlet
+     */
+    public String[] getServletMethods() throws ServletException;
+
+
+    /**
+     * Is this servlet currently unavailable?
+     */
+    public boolean isUnavailable();
+
+
+    /**
+     * Sets the description of this servlet.
+     */
+    public void setDescription(String description);
+
+
+    /**
+     * Gets the description of this servlet.
+     */
+    public String getDescription();
+
+
+    /**
+     * Sets the multipart location
+     */
+    public void setMultipartLocation(String location);
+
+
+    /**
+     * Gets the multipart location
+     */
+    public String getMultipartLocation();
+
+
+    /**
+     * Sets the multipart max-file-size
+     */
+    public void setMultipartMaxFileSize(long maxFileSize);
+
+
+    /**
+     * Gets the multipart max-file-size
+     */
+    public long getMultipartMaxFileSize();
+
+
+    /**
+     * Sets the multipart max-request-size
+     */
+    public void setMultipartMaxRequestSize(long maxRequestSize);
+
+
+    /**
+     * Gets the multipart max-request-Size
+     */
+    public long getMultipartMaxRequestSize();
+
+
+    /**
+     * Sets the multipart file-size-threshold
+     */
+    public void setMultipartFileSizeThreshold(int fileSizeThreshold);
+
+
+    /**
+     * Gets the multipart file-size-threshol
+     */
+    public int getMultipartFileSizeThreshold();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new servlet initialization parameter for this servlet.
+     *
+     * @param name Name of this initialization parameter to add
+     * @param value Value of this initialization parameter to add
+     */
+    public void addInitParameter(String name, String value);
+
+
+    /**
+     * Add a new listener interested in InstanceEvents.
+     *
+     * @param listener The new listener
+     */
+    public void addInstanceListener(InstanceListener listener);
+
+
+    /**
+     * Add a mapping associated with the Wrapper.
+     * 
+     * @param mapping The new wrapper mapping
+     */
+    public void addMapping(String mapping);
+
+
+    /**
+     * Add a new security role reference record to the set of records for
+     * this servlet.
+     *
+     * @param name Role name used within this servlet
+     * @param link Role name used within the web application
+     */
+    public void addSecurityReference(String name, String link);
+
+
+    /**
+     * Allocate an initialized instance of this Servlet that is ready to have
+     * its <code>service()</code> method called.  If the servlet class does
+     * not implement <code>SingleThreadModel</code>, the (only) initialized
+     * instance may be returned immediately.  If the servlet class implements
+     * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
+     * that this instance is not allocated again until it is deallocated by a
+     * call to <code>deallocate()</code>.
+     *
+     * @exception ServletException if the servlet init() method threw
+     *  an exception
+     * @exception ServletException if a loading error occurs
+     */
+    public Servlet allocate() throws ServletException;
+
+
+    /**
+     * Return this previously allocated servlet to the pool of available
+     * instances.  If this servlet class does not implement SingleThreadModel,
+     * no action is actually required.
+     *
+     * @param servlet The servlet to be returned
+     *
+     * @exception ServletException if a deallocation error occurs
+     */
+    public void deallocate(Servlet servlet) throws ServletException;
+
+
+    /**
+     * Return the value for the specified initialization parameter name,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the requested initialization parameter
+     */
+    public String findInitParameter(String name);
+
+
+    /**
+     * Return the names of all defined initialization parameters for this
+     * servlet.
+     */
+    public String[] findInitParameters();
+
+
+    /**
+     * Return the mappings associated with this wrapper.
+     */
+    public String[] findMappings();
+
+
+    /**
+     * Return the security role link for the specified security role
+     * reference name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Security role reference used within this servlet
+     */
+    public String findSecurityReference(String name);
+
+
+    /**
+     * Return the set of security role reference names associated with
+     * this servlet, if any; otherwise return a zero-length array.
+     */
+    public String[] findSecurityReferences();
+
+
+    /**
+     * Load and initialize an instance of this servlet, if there is not already
+     * at least one initialized instance.  This can be used, for example, to
+     * load servlets that are marked in the deployment descriptor to be loaded
+     * at server startup time.
+     *
+     * @exception ServletException if the servlet init() method threw
+     *  an exception
+     * @exception ServletException if some other loading problem occurs
+     */
+    public void load() throws ServletException;
+
+
+    /**
+     * Remove the specified initialization parameter from this servlet.
+     *
+     * @param name Name of the initialization parameter to remove
+     */
+    public void removeInitParameter(String name);
+
+
+    /**
+     * Remove a listener no longer interested in InstanceEvents.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeInstanceListener(InstanceListener listener);
+
+
+    /**
+     * Remove a mapping associated with the wrapper.
+     *
+     * @param mapping The pattern to remove
+     */
+    public void removeMapping(String mapping);
+
+
+    /**
+     * Remove any security role reference for the specified role name.
+     *
+     * @param name Security role used within this servlet to be removed
+     */
+    public void removeSecurityReference(String name);
+
+
+    /**
+     * Process an UnavailableException, marking this servlet as unavailable
+     * for the specified amount of time.
+     *
+     * @param unavailable The exception that occurred, or <code>null</code>
+     *  to mark this servlet as permanently unavailable
+     */
+    public void unavailable(UnavailableException unavailable);
+
+
+    /**
+     * Unload all initialized instances of this servlet, after calling the
+     * <code>destroy()</code> method for each instance.  This can be used,
+     * for example, prior to shutting down the entire servlet engine, or
+     * prior to reloading all of the classes from the Loader associated with
+     * our Loader's repository.
+     *
+     * @exception ServletException if an unload error occurs
+     */
+    public void unload() throws ServletException;
+
+
+    /**
+     * Configures the wrapped servlet as either supporting or not supporting
+     * asynchronous operations.
+     *
+     * @param isAsyncSupported true if the wrapped servlet supports
+     * asynchronous operations, false otherwise
+     */
+    public void setIsAsyncSupported(boolean isAsyncSupported);
+
+
+    /**
+     * Checks if the wrapped servlet has been annotated or flagged in the
+     * deployment descriptor as being able to support asynchronous operations.
+     *
+     * @return true if the wrapped servlet supports async operations, and
+     * false otherwise
+     */
+    public boolean isAsyncSupported();
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/AuthenticatorBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/AuthenticatorBase.java
new file mode 100644
index 0000000..6b20a10
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/AuthenticatorBase.java
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.*;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.valves.ValveBase;
+import org.glassfish.web.valve.GlassFishValve;
+import java.security.SecureRandom;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.security.Principal;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Basic implementation of the <b>Valve</b> interface that enforces the
+ * <code>&lt;security-constraint&gt;</code> elements in the web application
+ * deployment descriptor.  This functionality is implemented as a Valve
+ * so that it can be ommitted in environments that do not require these
+ * features.  Individual implementations of each supported authentication
+ * method can subclass this base class as required.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  When this class is utilized, the Context to
+ * which it is attached (or a parent Container in a hierarchy) must have an
+ * associated Realm that can be used for authenticating users and enumerating
+ * the roles to which they have been assigned.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  This Valve is only useful when processing HTTP
+ * requests.  Requests of any other type will simply be passed through.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.17.6.3 $ $Date: 2008/04/17 18:37:04 $
+ */
+
+
+public abstract class AuthenticatorBase
+    extends ValveBase
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    implements Authenticator, Lifecycle {
+    */
+    // START CR 6411114
+    implements Authenticator {
+    // END CR 6411114    
+    
+    // ----------------------------------------------------- Static Variables
+
+    protected static final Logger log = LogFacade.getLogger();
+    protected static final ResourceBundle rb = log.getResourceBundle();
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.AuthenticatorBase/1.0";
+
+    /**
+     * The number of random bytes to include when generating a
+     * session identifier.
+     */
+    protected static final int SESSION_ID_BYTES = 16;
+    
+    /**
+     * Authentication header
+     */
+    protected static final String AUTH_HEADER_NAME = "WWW-Authenticate";
+    
+
+    /**
+     * Default authentication realm name.
+     */
+    protected static final String REALM_NAME = "Authentication required";
+
+
+    // ----------------------------------------------------- Instance Variables
+    /**
+     * Should a session always be used once a user is authenticated? This may
+     * offer some performance benefits since the session can then be used to
+     * cache the authenticated Principal, hence removing the need to
+     * authenticate the user via the Realm on every request. This may be of help
+     * for combinations such as BASIC authentication used with the JNDIRealm or
+     * DataSourceRealms. However there will also be the performance cost of
+     * creating and GC'ing the session. By default, a session will not be
+     * created.
+     */
+    protected boolean alwaysUseSession = false;
+
+    /**
+     * Should we cache authenticated Principals if the request is part of
+     * an HTTP session?
+     */
+    protected boolean cache = true;
+    
+    /**
+     * Should the session ID, if any, be changed upon a successful
+     * authentication to prevent a session fixation attack?
+     */
+    protected boolean changeSessionIdOnAuthentication = true;
+
+
+    /**
+     * The Context to which this Valve is attached.
+     */
+    protected Context context = null;
+    
+    
+    /**
+     * A String initialization parameter used to increase the entropy of
+     * the initialization of our random number generator.
+     */
+    protected String entropy = null;
+        
+    /**
+     * Flag to determine if we disable proxy caching, or leave the issue
+     * up to the webapp developer.
+     */
+    protected boolean disableProxyCaching = true;
+    
+    /**
+     * The lifecycle event support for this component.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+    */
+    
+    /**
+     * A random number generator to use when generating session identifiers.
+     */
+    protected SecureRandom random = null;
+    
+    /**
+     * The Java class name of the random number generator class to be used
+     * when generating session identifiers.
+     */
+    protected String randomClass = SecureRandom.class.getName();
+        
+    /**
+     * The SingleSignOn implementation in our request processing chain,
+     * if there is one.
+     */
+    protected SingleSignOn sso = null;
+    
+    /**
+     * Has this component been started?
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    protected boolean started = false;
+    */
+    
+    /**
+     * "Expires" header always set to Date(1), so generate once only
+     */
+    //START SJSAS 6202703
+    /*
+    private static final String DATE_ONE =
+            (new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER,
+            Locale.US)).format(new Date(1));
+    */
+    //END SJSAS 6202703
+
+    /**
+     * Flag to determine if we disable proxy caching with headers incompatible
+     * with IE 
+     */
+    protected boolean securePagesWithPragma = true;
+    
+
+    // ------------------------------------------------------------- Properties
+    public boolean getAlwaysUseSession() {
+        return alwaysUseSession;
+    }
+
+
+    public void setAlwaysUseSession(boolean alwaysUseSession) {
+        this.alwaysUseSession = alwaysUseSession;
+    }
+
+
+    /**
+     * Return the cache authenticated Principals flag.
+     */
+    public boolean getCache() {
+        return (this.cache);
+    }
+    
+    
+    /**
+     * Set the cache authenticated Principals flag.
+     *
+     * @param cache The new cache flag
+     */
+    public void setCache(boolean cache) {
+        this.cache = cache;
+    }
+    
+    
+    /**
+     * Return the Container to which this Valve is attached.
+     */
+    public Container getContainer() {
+        return (this.context);
+    }
+    
+    
+    /**
+     * Set the Container to which this Valve is attached.
+     *
+     * @param container The container to which we are attached
+     */
+    public void setContainer(Container container) {
+        
+        if (!(container instanceof Context))
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.CONFIG_ERROR_MUST_ATTACH_TO_CONTEXT));
+        
+        super.setContainer(container);
+        this.context = (Context) container;
+        this.securePagesWithPragma = context.isSecurePagesWithPragma(); 
+    }
+    
+    
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+        return (this.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 entropy increaser value, or compute a semi-useful value
+     * if this String has not yet been set.
+     */
+    public String getEntropy() {
+        
+        // Calculate a semi-useful value if this has not been set
+        if (this.entropy == null)
+            setEntropy(this.toString());
+        
+        return (this.entropy);        
+    }
+    
+    
+    /**
+     * Set the entropy increaser value.
+     *
+     * @param entropy The new entropy increaser value
+     */
+    public void setEntropy(String entropy) {
+        this.entropy = entropy;
+    }
+    
+    
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    @Override
+    public String getInfo() {
+        return (this.info);
+    }
+    
+    
+    /**
+     * Return the random number generator class name.
+     */
+    public String getRandomClass() {
+        return (this.randomClass);
+    }
+    
+    
+    /**
+     * Set the random number generator class name.
+     *
+     * @param randomClass The new random number generator class name
+     */
+    public void setRandomClass(String randomClass) {
+        this.randomClass = randomClass;
+    }
+    
+    /**
+     * Return the flag that states if we add headers to disable caching by
+     * proxies.
+     */
+    public boolean getDisableProxyCaching() {
+        return disableProxyCaching;
+    }
+    
+    /**
+     * Set the value of the flag that states if we add headers to disable
+     * caching by proxies.
+     * @param nocache <code>true</code> if we add headers to disable proxy
+     *              caching, <code>false</code> if we leave the headers alone.
+     */
+    public void setDisableProxyCaching(boolean nocache) {
+        disableProxyCaching = nocache;
+    }
+    
+
+    /**
+     * Return the flag that states, if proxy caching is disabled, what headers
+     * we add to disable the caching.
+     */
+    public boolean isSecurePagesWithPragma() {
+        return securePagesWithPragma;
+    }
+
+
+    /**
+     * Set the value of the flag that states what headers we add to disable
+     * proxy caching.
+     * @param securePagesWithPragma <code>true</code> if we add headers which 
+     * are incompatible with downloading office documents in IE under SSL but
+     * which fix a caching problem in Mozilla.
+     */
+    public void setSecurePagesWithPragma(boolean securePagesWithPragma) {
+        this.securePagesWithPragma = securePagesWithPragma;
+    }    
+
+    /**
+     * Return the flag that states if we should change the session ID of an
+     * existing session upon successful authentication.
+     * 
+     * @return <code>true</code> to change session ID upon successful
+     *         authentication, <code>false</code> to do not perform the change.
+     */
+    public boolean isChangeSessionIdOnAuthentication() {
+        return changeSessionIdOnAuthentication;
+    }
+
+    /**
+     * Set the value of the flag that states if we should change the session ID
+     * of an existing session upon successful authentication.
+     * 
+     * @param changeSessionIdOnAuthentication
+     *            <code>true</code> to change session ID upon successful
+     *            authentication, <code>false</code> to do not perform the
+     *            change.
+     */
+    public void setChangeSessionIdOnAuthentication(
+            boolean changeSessionIdOnAuthentication) {
+        this.changeSessionIdOnAuthentication = changeSessionIdOnAuthentication;
+    }
+
+    public SingleSignOn getSingleSignOn() {
+        return sso;
+    }
+
+    public void setSingleSignOn(SingleSignOn sso) {
+        this.sso = sso;
+    }
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * Enforce the security restrictions in the web application deployment
+     * descriptor of our associated Context.
+     *
+     * @param request Request to be processed
+     * @param response Response to be processed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if thrown by a processing element
+     */
+    @Override
+    public int invoke(Request request, Response response)
+    throws IOException, ServletException {
+                
+        // START GlassFish 247
+        if (!context.getAvailable()) {
+            try {    
+                ((HttpServletResponse) response.getResponse())
+                    .sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+            } catch (IllegalStateException e) {
+                ;
+            } catch (IOException e) {
+                ;
+            }
+            return END_PIPELINE;
+        }       
+        // END GlassFish 247
+        
+        /* GlassFish 6386229
+        // If this is not an HTTP request, do nothing
+        if (!(request instanceof HttpRequest) ||
+                !(response instanceof HttpResponse)) {
+            return INVOKE_NEXT;
+        }
+        if (!(request.getRequest() instanceof HttpServletRequest) ||
+                !(response.getResponse() instanceof HttpServletResponse)) {
+            return INVOKE_NEXT;
+        }
+        */
+        
+        HttpRequest hrequest = (HttpRequest) request;
+        HttpResponse hresponse = (HttpResponse) response;
+        if (log.isLoggable(Level.FINE)) {
+            String msg = "Security checking request " +
+                         ((HttpServletRequest) request.getRequest()).getMethod() + " " +
+                         ((HttpServletRequest) request.getRequest()).getRequestURI();
+
+            log.log(Level.FINE, neutralizeForLog(msg));
+        }
+        LoginConfig config = this.context.getLoginConfig();
+        
+        // Have we got a cached authenticated Principal to record?
+        if (cache) {
+            Principal principal =
+                    ((HttpServletRequest) request.getRequest()).getUserPrincipal();
+            if (principal == null) {
+                Session session = getSession(hrequest);
+                if (session != null) {
+                    principal = session.getPrincipal();
+                    if (principal != null) {
+                        if (log.isLoggable(Level.FINE)) {
+                            String msg = "We have cached auth type " +
+                                         session.getAuthType() +
+                                         " for principal " +
+                                         session.getPrincipal();
+                            log.log(Level.FINE, neutralizeForLog(msg));
+                        }
+                        hrequest.setAuthType(session.getAuthType());
+                        hrequest.setUserPrincipal(principal);
+                    }
+                }
+            }
+        }
+        
+        Realm realm = this.context.getRealm();
+        // Is this request URI subject to a security constraint?
+        SecurityConstraint [] constraints = realm.
+                findSecurityConstraints(hrequest, this.context);
+        
+        if ((constraints == null) /* &&
+            (!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, " Not subject to any constraint");
+            return processSecurityCheck(hrequest,hresponse,config);
+        }
+        
+        // Make sure that constrained resources are not cached by web proxies
+        // or browsers as caching can provide a security hole
+        //START SJSAS 6202703
+        //Moved to org.apache.catalina.realm.RealmBase
+        /*
+        HttpServletRequest hsrequest = (HttpServletRequest)hrequest.getRequest();
+        if (disableProxyCaching &&
+                !hsrequest.isSecure() &&
+                !"POST".equalsIgnoreCase(hsrequest.getMethod())) {
+            HttpServletResponse sresponse =
+                    (HttpServletResponse) response.getResponse();
+            sresponse.setHeader("Pragma", "No-cache");
+            sresponse.setHeader("Cache-Control", "no-cache");
+            sresponse.setHeader("Expires", DATE_ONE);
+        }
+        */
+        //END SJSAS 6202703
+        
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, " Calling hasUserDataPermission()");
+
+        if (!realm.hasUserDataPermission(hrequest, hresponse, constraints)) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, " Failed hasUserDataPermission() test");
+            // ASSERT: Authenticator already set the appropriate
+            // HTTP status code, so we do not have to do anything special
+            return END_PIPELINE;
+        }
+        
+        //START SJSAS 6202703
+        /*
+        for(int i=0; i < constraints.length; i++) {
+            // Authenticate based upon the specified login configuration
+            if (constraints[i].getAuthConstraint()) {
+                if (log.isDebugEnabled())
+                    log.debug(" Calling authenticate()");
+         
+                if (!authenticate(hrequest, hresponse, config)) {
+                    if (log.isDebugEnabled())
+                        log.debug(" Failed authenticate() test");
+                    //ASSERT: Authenticator already set the appropriate
+                    //HTTP status code, so we do not have to do anything special
+                    return END_PIPELINE;
+                } else {
+                    break;
+                }
+            }
+        }
+         */
+        //END SJSAS 6202703
+        //START SJSAS 6202703
+        int preAuthenticateCheckResult = realm.preAuthenticateCheck(
+                hrequest, hresponse, constraints, disableProxyCaching,
+                securePagesWithPragma, (sso != null));
+        
+        if(preAuthenticateCheckResult == Realm.AUTHENTICATE_NOT_NEEDED) {
+            return processSecurityCheck(hrequest,hresponse,config);
+        } else if(preAuthenticateCheckResult == Realm.AUTHENTICATE_NEEDED) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, " Calling authenticate()");
+            }
+            boolean authenticateResult = realm.invokeAuthenticateDelegate(
+                    hrequest, hresponse, context, this, false);
+            if(!authenticateResult) {
+                if(log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, " Failed authenticate() test");
+                }
+                return END_PIPELINE;
+            }
+        } else if(preAuthenticateCheckResult == Realm.AUTHENTICATED_NOT_AUTHORIZED) {
+            return END_PIPELINE;
+        }
+        //END SJSAS 6202703
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, " Calling accessControl()");
+        }
+
+        if (!realm.hasResourcePermission(hrequest, hresponse,
+                constraints,
+                this.context)) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, " Failed accessControl() test");
+            }
+
+            // START IASRI 4823322
+            Auditor[] auditors = this.context.getAuditors();
+            if (auditors != null) {
+                for (int j=0; j<auditors.length; j++) {
+                    auditors[j].webInvocation(hrequest, false);
+                }
+            }
+            // END IASRI 4823322
+
+            /*
+             * ASSERT: AccessControl method has already set the
+             * appropriate HTTP status code, so we do not have to do
+             * anything special
+             */
+            return END_PIPELINE;
+        }
+        
+        // START IASRI 4823322
+        Auditor[] auditors = this.context.getAuditors();
+        if (auditors != null) {
+            boolean success=true;
+            for (int j=0; j<auditors.length; j++) {
+                try {
+                    auditors[j].webInvocation(hrequest, true);
+                } catch (Exception e) {
+                    success=false;
+                }
+            }
+            if (!success) {     // fail authorization if auditor blew up
+                return END_PIPELINE;
+            }
+        }
+        // END IASRI 4823322
+        
+        // Any and all specified constraints have been satisfied
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Successfully passed all security constraints");
+        return INVOKE_NEXT;
+    }
+
+    /**
+     * A post-request processing implementation that does nothing.
+     *
+     * Very few Valves override this behaviour as most Valve logic
+     * is used for request processing.
+     */
+    @Override
+    public void postInvoke(Request request, Response response) 
+            throws IOException, ServletException {
+        Realm realm = this.context.getRealm();
+        HttpRequest hrequest = (HttpRequest) request;
+        HttpResponse hresponse = (HttpResponse) response;
+        /*
+         * Check realm for null since app may have been undeployed by the
+         * time its pipeline is invoked on the way out, in which case its
+         * realm will have been set to null. See IT 6801
+         */
+        if (realm != null) {
+            realm.invokePostAuthenticateDelegate(hrequest, hresponse, context);
+        }
+    }
+    
+    // ------------------------------------------------------ Protected Methods
+    
+    
+    
+    
+    /**
+     * Associate the specified single sign on identifier with the
+     * specified Session.
+     *
+     * @param ssoId Single sign on identifier
+     * @param ssoVersion Single sign on version
+     * @param session Session to be associated
+     */
+    protected void associate(String ssoId, long ssoVersion,
+            Session session) {
+        
+        if (sso == null)
+            return;
+        sso.associate(ssoId, ssoVersion, session);
+        
+    }
+    
+    
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config Login configuration describing how authentication
+     * should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    //START SJSAS 6202703
+    /*
+    protected abstract boolean authenticate(HttpRequest request,
+                                            HttpResponse response,
+                                            LoginConfig config)
+        throws IOException;
+     */
+    public abstract boolean authenticate(HttpRequest request,
+                                         HttpResponse response,
+                                         LoginConfig config)
+            throws IOException;
+    //END SJSAS 6202703
+    
+    
+    /**
+     * Generate and return a new session identifier for the cookie that
+     * identifies an SSO principal.
+     */
+    protected synchronized String generateSessionId() {
+        
+        // Generate a byte array containing a session identifier
+        byte bytes[] = new byte[SESSION_ID_BYTES];
+        getRandom().nextBytes(bytes);
+        
+        // Render the result as a String of hexadecimal digits
+        StringBuilder result = new StringBuilder();
+        for (int i = 0; i < bytes.length; i++) {
+            byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
+            byte b2 = (byte) (bytes[i] & 0x0f);
+            if (b1 < 10)
+                result.append((char) ('0' + b1));
+            else
+                result.append((char) ('A' + (b1 - 10)));
+            if (b2 < 10)
+                result.append((char) ('0' + b2));
+            else
+                result.append((char) ('A' + (b2 - 10)));
+        }
+        return (result.toString());
+        
+    }
+    
+    
+    /**
+     * Return the random number generator instance we should use for
+     * generating session identifiers.  If there is no such generator
+     * currently defined, construct and seed a new one.
+     */
+    protected synchronized SecureRandom getRandom() {
+        
+        if (this.random == null) {
+            try {
+                Class clazz = Class.forName(randomClass);
+                this.random = (SecureRandom) clazz.newInstance();
+                long seed = System.currentTimeMillis();
+                char entropy[] = getEntropy().toCharArray();
+                for (int i = 0; i < entropy.length; i++) {
+                    long update = ((byte) entropy[i]) << ((i % 8) * 8);
+                    seed ^= update;
+                }
+                this.random.setSeed(seed);
+            } catch (Exception e) {
+                this.random = new SecureRandom();
+            }
+        }
+        
+        return (this.random);
+        
+    }
+    
+    
+    /**
+     * Return the internal Session that is associated with this HttpRequest,
+     * or <code>null</code> if there is no such Session.
+     *
+     * @param request The HttpRequest we are processing
+     */
+    protected Session getSession(HttpRequest request) {
+        
+        return (getSession(request, false));
+        
+    }
+    
+    
+    /**
+     * Return the internal Session that is associated with this HttpRequest,
+     * possibly creating a new one if necessary, or <code>null</code> if
+     * there is no such session and we did not create one.
+     *
+     * @param request The HttpRequest we are processing
+     * @param create Should we create a session if needed?
+     */
+    protected Session getSession(HttpRequest request, boolean create) {
+        
+        return request.getSessionInternal(create);
+        
+    }
+    
+    
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+        message = neutralizeForLog(message);
+        org.apache.catalina.Logger logger = context.getLogger();
+        if (logger != null) {
+            logger.log("Authenticator[" + context.getPath() + "]: " +
+                    message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.AUTHENTICATOR_INFO, new Object[] {context.getPath(), message});
+            }
+        }
+    }
+    
+    
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    protected void log(String message, Throwable t) {
+        message = neutralizeForLog(message);
+        org.apache.catalina.Logger logger = context.getLogger();
+        if (logger != null) {
+            logger.log("Authenticator[" + context.getPath() + "]: " +
+                message, t, org.apache.catalina.Logger.WARNING);
+        } else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.AUTHENTICATOR_INFO),
+                                              new Object[] {context.getPath(), message});
+            log.log(Level.WARNING, msg, t);
+        }
+    }
+    
+    
+    /**
+     * Register an authenticated Principal and authentication type in our
+     * request, in the current session (if there is one), and with our
+     * SingleSignOn valve, if there is one.  Set the appropriate cookie
+     * to be returned.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are generating
+     * @param principal The authenticated Principal to be registered
+     * @param authType The authentication type to be registered
+     * @param username Username used to authenticate (if any)
+     * @param password Password used to authenticate (if any)
+     */
+    protected void register(HttpRequest request, HttpResponse response,
+            Principal principal, String authType,
+            String username, char[] password) {
+        
+        if (log.isLoggable(Level.FINE)) {
+            String pname = ((principal != null) ? principal.getName() : "[null principal]");
+            String msg = "Authenticated '" + pname + "' with type '"
+                         + authType + "'";
+            log.log(Level.FINE, neutralizeForLog(msg));
+        }
+        // Cache the authentication information in our request
+        request.setAuthType(authType);
+        request.setUserPrincipal(principal);
+
+        Session session = getSession(request, false);
+        if (session != null && changeSessionIdOnAuthentication) {
+            request.changeSessionId();
+        } else if (alwaysUseSession) {
+            session = getSession(request, true);
+        }
+        
+        // Cache the authentication information in our session, if any
+        if (cache) {
+            if (session != null) {
+                session.setAuthType(authType);
+                session.setPrincipal(principal);
+                if (username != null)
+                    session.setNote(Constants.SESS_USERNAME_NOTE, username);
+                else
+                    session.removeNote(Constants.SESS_USERNAME_NOTE);
+                if (password != null)
+                    session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+                else
+                    session.removeNote(Constants.SESS_PASSWORD_NOTE);
+            }
+        }
+        
+        // Construct a cookie to be returned to the client
+        if (sso == null)
+            return;
+        HttpServletRequest hreq =
+                (HttpServletRequest) request.getRequest();
+        HttpServletResponse hres =
+                (HttpServletResponse) response.getResponse();
+
+        // Use the connector's random number generator (if any) for
+        // generating the session ID. If none, then fall back to the default
+        // session ID generator.
+        String value = request.generateSessionId();
+        if (value == null) {
+            value = generateSessionId();
+        }
+
+        Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE, value);
+        cookie.setMaxAge(-1);
+        cookie.setPath("/");
+        StandardHost host = (StandardHost) context.getParent();
+        if (host != null) {
+            host.configureSingleSignOnCookieSecure(cookie, hreq);
+            host.configureSingleSignOnCookieHttpOnly(cookie);
+        } else {
+            cookie.setSecure(hreq.isSecure());
+        }
+        hres.addCookie(cookie);
+
+        // Register this principal with our SSO valve
+        /* BEGIN S1AS8 PE 4856080,4918627
+        sso.register(value, principal, authType, username, password);
+         */
+        // BEGIN S1AS8 PE 4856080,4918627
+        String realm = context.getRealm().getRealmName();
+        // being here, an authentication just occurred using the realm
+        assert(realm != null);
+        sso.register(value, principal, authType, username, password, realm);
+        // END S1AS8 PE 4856080,4918627
+        
+        request.setNote(Constants.REQ_SSOID_NOTE, value);
+        if (sso.isVersioningSupported()) {
+            request.setNote(Constants.REQ_SSO_VERSION_NOTE, Long.valueOf(0));
+        }
+        
+    }
+
+    @Override
+    public void login(String username, char[] password, HttpRequest request)
+            throws ServletException {
+        Principal principal = doLogin(request, username, password);
+        register(request, (HttpResponse)request.getResponse(), principal,
+                getAuthMethod(), username, password);
+    }
+
+    protected abstract String getAuthMethod();
+
+    /**
+     * Process the login request.
+     *
+     * @param request   Associated request
+     * @param username  The user
+     * @param password  The password
+     * @return          The authenticated Principal
+     * @throws ServletException
+     */
+    protected Principal doLogin(HttpRequest request, String username,
+                                char[] password) throws ServletException {
+        Principal p = context.getRealm().authenticate(username, password);
+        if (p == null) {
+            throw new ServletException(rb.getString(LogFacade.LOGIN_FAIL));
+        }
+        return p;
+    }
+
+    @Override
+    public void logout(HttpRequest request) throws ServletException {
+        Session session = getSession(request);
+        if (session != null) {
+            session.setPrincipal(null);
+            session.setAuthType(null);
+        }
+
+        // principal and authType set to null in the following
+        register(request, (HttpResponse)request.getResponse(), null,
+                null, null, null);
+    }
+    
+    // ------------------------------------------------------ Private Methods
+
+    private int processSecurityCheck(HttpRequest hrequest,
+				     HttpResponse hresponse,
+				     LoginConfig config) 
+	throws IOException {
+
+        // Special handling for form-based logins to deal with the case
+        // where the login form (and therefore the "j_security_check" URI
+        // to which it submits) might be outside the secured area
+        String contextPath = this.context.getPath();
+        String requestURI = hrequest.getDecodedRequestURI();
+        if (requestURI.startsWith(contextPath) &&
+                requestURI.endsWith(Constants.FORM_ACTION)) {
+            if (!authenticate(hrequest, hresponse, config)) {
+                if (log.isLoggable(Level.FINE)) {
+                    String msg = " Failed authenticate() test ??" + requestURI;
+                    log.log(Level.FINE, neutralizeForLog(msg));
+                }
+                return END_PIPELINE;
+            }
+        }
+	return INVOKE_NEXT;
+    }
+        
+    // ------------------------------------------------------ Lifecycle Methods
+    
+    
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    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.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public LifecycleListener[] findLifecycleListeners() {
+        
+        return lifecycle.findLifecycleListeners();
+        
+    }
+    */
+
+    
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public void removeLifecycleListener(LifecycleListener listener) {
+        
+        lifecycle.removeLifecycleListener(listener);
+        
+    }
+    */
+
+    
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+        // START CR 6411114
+        if (started)            // Ignore multiple starts
+            return;
+        super.start();
+        // END CR 6411114
+        if ("org.apache.catalina.core.StandardContext".equals
+                (context.getClass().getName())) {
+            try {
+                // XXX What is this ???
+                Class paramTypes[] = new Class[0];
+                Object paramValues[] = new Object[0];
+                Method method =
+                        context.getClass().getMethod("getDebug", paramTypes);
+                Integer result = (Integer) method.invoke(context, paramValues);
+                setDebug(result.intValue());
+            } catch (Exception e) {
+                log.log(Level.SEVERE, LogFacade.GETTING_DEBUG_VALUE_EXCEPTION, e);
+            }
+        }
+        /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+        started = true;
+        */
+
+        // Look up the SingleSignOn implementation in our request processing
+        // path, if there is one
+        Container parent = context.getParent();
+        while ((sso == null) && (parent != null)) {
+            if (!(parent instanceof Pipeline)) {
+                parent = parent.getParent();
+                continue;
+            }
+            GlassFishValve valves[] = ((Pipeline) parent).getValves();
+            for (int i = 0; i < valves.length; i++) {
+                if (valves[i] instanceof SingleSignOn) {
+                    sso = (SingleSignOn) valves[i];
+                    break;
+                }
+            }
+            if (sso == null)
+                parent = parent.getParent();
+        }
+        if (log.isLoggable(Level.FINE)) {
+            if (sso != null)
+                log.log(Level.FINE, "Found SingleSignOn Valve at " + sso);
+            else
+                log.log(Level.FINE, "No SingleSignOn Valve is present");
+        }
+        
+    }
+    
+    
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    @Override
+    public void stop() throws LifecycleException {
+        // START CR 6411114
+        if (!started)       // Ignore stop if not started
+            return;
+        // END CR 6411114
+
+        sso = null;
+        // START CR 6411114
+        super.stop();
+        // END CR 6411114
+        
+    }
+    // BEGIN S1AS8 PE 4856062,4918627
+    /**
+     * Set the name of the associated realm. This method does nothing by
+     * default.
+     *
+     * @param name the name of the realm.
+     */
+    public void setRealmName(String name) {
+        
+    }
+    
+    
+    /**
+     * Returns the name of the associated realm. Always returns null unless
+     * subclass overrides behavior.
+     *
+     * @return realm name or null if not set.
+     */
+    public String getRealmName() {
+        return null;
+    }
+    // END S1AS8 PE 4856062,4918627
+    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/BasicAuthenticator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/BasicAuthenticator.java
new file mode 100644
index 0000000..f4d9c33
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/BasicAuthenticator.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.util.Base64;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.security.Principal;
+import java.util.Locale;
+import java.util.logging.Level;
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation of HTTP BASIC
+ * Authentication, as outlined in RFC 2617:  "HTTP Authentication: Basic
+ * and Digest Access Authentication."
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.7 $ $Date: 2007/05/05 05:31:52 $
+ */
+
+public class BasicAuthenticator
+    extends AuthenticatorBase {
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.BasicAuthenticator/1.0";
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    @Override
+    public String getInfo() {
+        return (this.info);
+    }
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config Login configuration describing how authentication
+     * should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    @Override
+    public boolean authenticate(HttpRequest request,
+                                HttpResponse response,
+                                LoginConfig config)
+        throws IOException {
+
+        // Have we already authenticated someone?
+        Principal principal =
+            ((HttpServletRequest) request.getRequest()).getUserPrincipal();
+        if (principal != null) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "Already authenticated '" + principal.getName() + "'");
+            return (true);
+        }
+
+        // Validate any credentials already included with this request
+        HttpServletResponse hres =
+            (HttpServletResponse) response.getResponse();
+        String authorization = request.getAuthorization();
+
+        /* IASRI 4868073 
+        String username = parseUsername(authorization);
+        String password = parsePassword(authorization);
+        principal = context.getRealm().authenticate(username, password);
+        if (principal != null) {
+            register(request, response, principal, Constants.BASIC_METHOD,
+                     username, password);
+            return (true);
+        }
+        */
+        // BEGIN IASRI 4868073
+        // Only attempt to parse and validate the authorization if one was
+        // sent by the client. No reason to attempt to login with null
+        // authorization which must fail anyway. With basic auth this
+        // scenario always occurs first so this is a common case. This
+        // will also prevent logging the audit message for failure to
+        // authenticate null user (since login failures are always logged
+        // per psarc req).
+
+        if (authorization != null) {
+            String username = parseUsername(authorization);
+            char[] password = parsePassword(authorization);
+            principal = context.getRealm().authenticate(username, password);
+            if (principal != null) {
+                register(request, response, principal, Constants.BASIC_METHOD,
+                         username, password);
+                String ssoId = (String) request.getNote(
+                    Constants.REQ_SSOID_NOTE);
+                if (ssoId != null) {
+                    getSession(request, true);
+                }
+                return (true);
+            }
+        }
+        // END IASRI 4868073
+
+        // Send an "unauthorized" response and an appropriate challenge
+        String realmName = config.getRealmName();
+        if (realmName == null)
+            realmName = REALM_NAME;
+    //        if (debug >= 1)
+    //            log("Challenging for realm '" + realmName + "'");
+        hres.setHeader(AUTH_HEADER_NAME,
+                       "Basic realm=\"" + realmName + "\"");
+        hres.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+        //      hres.flushBuffer();
+        return (false);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parse the username from the specified authorization credentials.
+     * If none can be found, return <code>null</code>.
+     *
+     * @param authorization Authorization credentials from this request
+     */
+    protected String parseUsername(String authorization) {
+
+        if (authorization == null)
+            return (null);
+        if (!authorization.toLowerCase(Locale.ENGLISH).startsWith("basic "))
+            return (null);
+        authorization = authorization.substring(6).trim();
+
+        // Decode and parse the authorization credentials
+        String unencoded =
+          new String(Base64.decode(authorization.getBytes(Charset.defaultCharset())));
+        int colon = unencoded.indexOf(':');
+        if (colon < 0)
+            return (null);
+        String username = unencoded.substring(0, colon);
+        //        String password = unencoded.substring(colon + 1).trim();
+        return (username);
+
+    }
+
+
+    /**
+     * Parse the password from the specified authorization credentials.
+     * If none can be found, return <code>null</code>.
+     *
+     * @param authorization Authorization credentials from this request
+     */
+    protected char[] parsePassword(String authorization) {
+
+        if (authorization == null)
+            return (null);
+        if (!authorization.toLowerCase(Locale.ENGLISH).startsWith("basic "))
+            return (null);
+        authorization = authorization.substring(6).trim();
+
+        // Decode and parse the authorization credentials
+        String unencoded =
+          new String(Base64.decode(authorization.getBytes(Charset.defaultCharset())));
+        int colon = unencoded.indexOf(':');
+        if (colon < 0)
+            return (null);
+        //        String username = unencoded.substring(0, colon).trim();
+        char[] password = unencoded.substring(colon + 1).toCharArray();
+        return (password);
+
+    }
+
+    @Override
+    protected String getAuthMethod() {
+        return HttpServletRequest.BASIC_AUTH;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/Constants.java
new file mode 100644
index 0000000..a3f2a04
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/Constants.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.authenticator";
+
+    // Authentication methods for login configuration
+    public static final String BASIC_METHOD = "BASIC";
+    public static final String CERT_METHOD = "CLIENT_CERT";
+    public static final String DIGEST_METHOD = "DIGEST";
+    public static final String FORM_METHOD = "FORM";
+
+    // User data constraints for transport guarantee
+    public static final String NONE_TRANSPORT = "NONE";
+    public static final String INTEGRAL_TRANSPORT = "INTEGRAL";
+    public static final String CONFIDENTIAL_TRANSPORT = "CONFIDENTIAL";
+
+    // Form based authentication constants
+    public static final String FORM_ACTION = "/j_security_check";
+    public static final String FORM_PASSWORD = "j_password";
+    public static final String FORM_USERNAME = "j_username";
+
+    // Cookie name for single sign on support
+    public static final String SINGLE_SIGN_ON_COOKIE = "JSESSIONIDSSO";
+    public static final String SINGLE_SIGN_ON_VERSION_COOKIE = "JSESSIONIDSSOVERSION";
+
+
+    // --------------------------------------------------------- Request Notes
+
+
+    /**
+     * <p>If a user has been authenticated by the web layer, by means of a
+     * login method other than CLIENT_CERT, the username and password
+     * used to authenticate the user will be attached to the request as
+     * Notes for use by other server components.  A server component can
+     * also call several existing methods on Request to determine whether
+     * or not any user has been authenticated:</p>
+     * <ul>
+     * <li><strong>((HttpServletRequest) getRequest()).getAuthType()</strong>
+     *     will return BASIC, CLIENT_CERT, DIGEST, FORM, or <code>null</code>
+     *     if there is no authenticated user.</li>
+     * <li><strong>((HttpServletRequest) getRequest()).getUserPrincipal()</strong>
+     *     will return the authenticated <code>Principal</code> returned by the
+     *     <code>Realm</code> that authenticated this user.</li>
+     * </ul>
+     * <p>If CLIENT_CERT authentication was performed, the certificate chain
+     * will be available as a request attribute, as defined in the
+     * servlet specification.</p>
+     */
+
+
+    /**
+     * The notes key for the password used to authenticate this user.
+     */
+    public static final String REQ_PASSWORD_NOTE =
+      "org.apache.catalina.request.PASSWORD";
+
+
+    /**
+     * The notes key for the username used to authenticate this user.
+     */
+    public static final String REQ_USERNAME_NOTE =
+      "org.apache.catalina.request.USERNAME";
+
+
+    /**
+     * The notes key to track the single-sign-on identity with which this
+     * request is associated.
+     */
+    public static final String REQ_SSOID_NOTE =
+      "org.apache.catalina.request.SSOID";
+
+    /**
+     * The notes key to track the single-sign-on version with which this
+     * request is associated.
+     */
+    public static final String REQ_SSO_VERSION_NOTE =
+      "org.apache.catalina.request.SSOVersion";
+
+    // ---------------------------------------------------------- Session Notes
+
+
+    /**
+     * If the <code>cache</code> property of our authenticator is set, and
+     * the current request is part of a session, authentication information
+     * will be cached to avoid the need for repeated calls to
+     * <code>Realm.authenticate()</code>, under the following keys:
+     */
+
+
+    /**
+     * The notes key for the password used to authenticate this user.
+     */
+    public static final String SESS_PASSWORD_NOTE =
+      "org.apache.catalina.session.PASSWORD";
+
+
+    /**
+     * The notes key for the username used to authenticate this user.
+     */
+    public static final String SESS_USERNAME_NOTE =
+      "org.apache.catalina.session.USERNAME";
+
+
+    /**
+     * The following note keys are used during form login processing to
+     * cache required information prior to the completion of authentication.
+     */
+
+
+    /**
+     * The previously authenticated principal (if caching is disabled).
+     */
+    public static final String FORM_PRINCIPAL_NOTE =
+        "org.apache.catalina.authenticator.PRINCIPAL";
+
+
+    /**
+     * The original request information, to which the user will be
+     * redirected if authentication succeeds.
+     */
+    public static final String FORM_REQUEST_NOTE =
+        "org.apache.catalina.authenticator.REQUEST";
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/DigestAuthenticator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/DigestAuthenticator.java
new file mode 100644
index 0000000..01ca344
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/DigestAuthenticator.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.util.DigestEncoder;
+import org.apache.catalina.LifecycleException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+
+
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation of HTTP DIGEST
+ * Authentication (see RFC 2069).
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.6 $ $Date: 2007/04/17 21:33:22 $
+ */
+
+public class DigestAuthenticator
+    extends AuthenticatorBase {
+
+    // -------------------------------------------------------------- Constants
+
+    /**
+     * The MD5 helper object for this class.
+     */
+    protected static final DigestEncoder digestEncoder = new DigestEncoder();
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.DigestAuthenticator/1.0";
+
+    /**
+     *  DIGEST implementation only supports auth quality of protection.
+     */
+    protected static final String QOP = "auth";
+
+    /**
+     * The default message digest algorithm to use if we cannot use
+     * the requested one.
+     */
+    protected static final String DEFAULT_ALGORITHM = "MD5";
+
+    private static final String EMPTY_STRING = "";
+
+
+    // ----------------------------------------------------------- Constructors
+
+    public DigestAuthenticator() {
+        super();
+    }
+
+
+    // ----------------------------------------------------- Static Variables
+
+    /**
+     * The message digest algorithm to be used when generating session
+     * identifiers. This must be an algorithm supported by the
+     * <code>java.security.MessageDigest</code> class on your platform.
+     */
+    protected static volatile String algorithm = DEFAULT_ALGORITHM;
+
+    /**
+     * MD5 message digest provider.
+     */
+    protected volatile static MessageDigest messageDigest;
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * List of client nonce values currently being tracked
+     
+    protected Map<String,NonceInfo> cnonces;
+    */
+
+    /**
+     * Maximum number of client nonces to keep in the cache. If not specified,
+     * the default value of 1000 is used.
+     */
+    protected int cnonceCacheSize = 1000;
+
+
+    /**
+     * Private key.
+     */
+    protected String key = null;
+
+
+    /**
+     * How long server nonces are valid for in milliseconds. Defaults to 5
+     * minutes.
+     */
+    protected long nonceValidity = 5 * 60 * 1000;
+
+
+    /**
+     * Opaque string.
+     */
+    protected String opaque;
+
+
+    /**
+     * Should the URI be validated as required by RFC2617? Can be disabled in
+     * reverse proxies where the proxy has modified the URI.
+     */
+    protected boolean validateUri = true;
+
+
+
+    
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the message digest algorithm for this Manager.
+     */
+    public static String getAlgorithm() {
+        return algorithm;
+    }
+
+
+    /**
+     * Set the message digest algorithm for this Manager.
+     *
+     * @param alg The new message digest algorithm
+     */
+    public static synchronized void setAlgorithm(String alg) {
+        algorithm = alg;
+        // reset the messageDigest
+        messageDigest = null;
+    }
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    @Override
+    public String getInfo() {
+        return (info);
+    }
+
+    public int getCnonceCacheSize() {
+        return cnonceCacheSize;
+    }
+
+
+    public void setCnonceCacheSize(int cnonceCacheSize) {
+        this.cnonceCacheSize = cnonceCacheSize;
+    }
+
+
+    public String getKey() {
+        return key;
+    }
+
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+
+    public long getNonceValidity() {
+        return nonceValidity;
+    }
+
+
+    public void setNonceValidity(long nonceValidity) {
+        this.nonceValidity = nonceValidity;
+    }
+
+
+    public String getOpaque() {
+        return opaque;
+    }
+
+
+    public void setOpaque(String opaque) {
+        this.opaque = opaque;
+    }
+
+
+    public boolean isValidateUri() {
+        return validateUri;
+    }
+
+
+    public void setValidateUri(boolean validateUri) {
+        this.validateUri = validateUri;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config Login configuration describing how authentication
+     * should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    @Override
+    public boolean authenticate(HttpRequest request,
+                                HttpResponse response,
+                                LoginConfig config)
+        throws IOException {
+
+        // Have we already authenticated someone?
+        Principal principal =
+            ((HttpServletRequest) request.getRequest()).getUserPrincipal();
+        if (principal != null)
+            return (true);
+
+        // Validate any credentials already included with this request
+        HttpServletRequest hreq =
+            (HttpServletRequest) request.getRequest();
+        HttpServletResponse hres =
+            (HttpServletResponse) response.getResponse();
+        String authorization = request.getAuthorization();
+        DigestInfo digestInfo = new DigestInfo(getOpaque(), getNonceValidity(),
+                getKey(), /*cnonces,*/ isValidateUri());
+
+        if (authorization != null) {
+            boolean validRequest = digestInfo.validate(hreq, authorization, config);
+            if (validRequest) {
+                principal = context.getRealm().authenticate(hreq);
+                if (principal != null) {
+                    String username = parseUsername(authorization);
+                    register(request, response, principal,
+                            Constants.DIGEST_METHOD,
+                            username, null);
+                    String ssoId = (String) request.getNote(
+                            Constants.REQ_SSOID_NOTE);
+                    if (ssoId != null) {
+                        getSession(request, true);
+                    }
+                    return (true);
+                }
+            }
+        }
+
+        // Send an "unauthorized" response and an appropriate challenge
+
+        // Next, generate a nOnce token (that is a token which is supposed
+        // to be unique).
+        String nonce = generateNonce(hreq);
+
+        setAuthenticateHeader(hreq, hres, config, nonce,
+                digestInfo.isNonceStale());
+        hres.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+        //      hres.flushBuffer();
+        return (false);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+
+
+    /**
+     * Parse the username from the specified authorization string.  If none
+     * can be identified, return <code>null</code>
+     *
+     * @param authorization Authorization string to be parsed
+     */
+    protected String parseUsername(String authorization) {
+
+        //System.out.println("Authorization token : " + authorization);
+        // Validate the authorization credentials format
+        if (authorization == null)
+            return (null);
+        if (!authorization.startsWith("Digest "))
+            return (null);
+        authorization = authorization.substring(7).trim();
+
+        StringTokenizer commaTokenizer =
+            new StringTokenizer(authorization, ",");
+
+        while (commaTokenizer.hasMoreTokens()) {
+            String currentToken = commaTokenizer.nextToken();
+            int equalSign = currentToken.indexOf('=');
+            if (equalSign < 0)
+                return null;
+            String currentTokenName =
+                currentToken.substring(0, equalSign).trim();
+            String currentTokenValue =
+                currentToken.substring(equalSign + 1).trim();
+            if ("username".equals(currentTokenName))
+                return (removeQuotes(currentTokenValue));
+        }
+
+        return (null);
+
+    }
+
+    @Override
+    protected String getAuthMethod() {
+        return HttpServletRequest.DIGEST_AUTH;
+    }
+
+    /**
+     * Removes the quotes on a string.
+     */
+    protected static String removeQuotes(String quotedString,
+                                         boolean quotesRequired) {
+        //support both quoted and non-quoted
+        if (quotedString.length() > 0 && quotedString.charAt(0) != '"' &&
+                !quotesRequired) {
+            return quotedString;
+        } else if (quotedString.length() > 2) {
+            return quotedString.substring(1, quotedString.length() - 1);
+        } else {
+            return EMPTY_STRING;
+        }
+    }
+
+    /**
+     * Removes the quotes on a string.
+     */
+    protected static String removeQuotes(String quotedString) {
+        return removeQuotes(quotedString, false);
+    }
+
+
+    /**
+     * Generate a unique token. The token is generated according to the
+     * following pattern. NOnceToken = Base64 ( MD5 ( client-IP ":"
+     * time-stamp ":" private-key ) ).
+     *
+     * @param request HTTP Servlet request
+     */
+    protected String generateNonce(HttpServletRequest request) {
+
+        long currentTime = System.currentTimeMillis();
+
+        String ipTimeKey =
+            request.getRemoteAddr() + ":" + currentTime + ":" + getKey();
+
+        byte[] buffer = digest(ipTimeKey.getBytes(Charset.defaultCharset()));
+        
+        return currentTime + ":" + new String (digestEncoder.encode(buffer));
+    }
+
+
+    /**
+     * Generates the WWW-Authenticate header.
+     * <p>
+     * The header MUST follow this template :
+     * <pre>
+     *      WWW-Authenticate    = "WWW-Authenticate" ":" "Digest"
+     *                            digest-challenge
+     *
+     *      digest-challenge    = 1#( realm | [ domain ] | nOnce |
+     *                  [ digest-opaque ] |[ stale ] | [ algorithm ] )
+     *
+     *      realm               = "realm" "=" realm-value
+     *      realm-value         = quoted-string
+     *      domain              = "domain" "=" <"> 1#URI <">
+     *      nonce               = "nonce" "=" nonce-value
+     *      nonce-value         = quoted-string
+     *      opaque              = "opaque" "=" quoted-string
+     *      stale               = "stale" "=" ( "true" | "false" )
+     *      algorithm           = "algorithm" "=" ( "MD5" | token )
+     * </pre>
+     *
+     * @param request HTTP Servlet request
+     * @param response HTTP Servlet response
+     * @param config    Login configuration describing how authentication
+     *              should be performed
+     * @param nOnce nonce token
+     */
+    protected void setAuthenticateHeader(HttpServletRequest request,
+                                         HttpServletResponse response,
+                                         LoginConfig config,
+                                         String nOnce,
+                                         boolean isNonceStale) {
+
+        // Get the realm name
+        String realmName = config.getRealmName();
+        if (realmName == null)
+            realmName = REALM_NAME;
+
+        String authenticateHeader;
+        if (isNonceStale) {
+            authenticateHeader = "Digest realm=\"" + realmName + "\", " +
+            "qop=\"" + QOP + "\", nonce=\"" + nOnce + "\", " + "opaque=\"" +
+            getOpaque() + "\", stale=true";
+        } else {
+            authenticateHeader = "Digest realm=\"" + realmName + "\", " +
+            "qop=\"" + QOP + "\", nonce=\"" + nOnce + "\", " + "opaque=\"" +
+            getOpaque() + "\"";
+        }
+
+        response.setHeader(AUTH_HEADER_NAME, authenticateHeader);
+
+    }
+
+    protected static synchronized MessageDigest getMessageDigest() {
+        if (messageDigest == null) {
+            try {
+                messageDigest = MessageDigest.getInstance(algorithm);
+            } catch(NoSuchAlgorithmException e) {
+                throw new IllegalStateException(
+                        algorithm + " digest algorithm not available", e);
+            }
+        }
+
+        return messageDigest;
+    }
+
+    protected static byte[] digest(byte[] data) {
+        byte[] buffer = null;
+
+        MessageDigest md = getMessageDigest();
+        synchronized(md) {
+            buffer = md.digest(data);
+        }
+
+        return buffer;
+    }
+
+        // ------------------------------------------------------- Lifecycle Methods
+
+    @Override
+    public synchronized void start() throws LifecycleException {
+        super.start();
+
+        // Generate a random secret key
+        if (getKey() == null) {
+            setKey(generateSessionId());
+        }
+
+        // Generate the opaque string the same way
+        if (getOpaque() == null) {
+            setOpaque(generateSessionId());
+        }
+    }
+
+    private static class DigestInfo {
+
+        private String opaque;
+        private long nonceValidity;
+        private String key;
+        private boolean validateUri = true;
+
+        private String userName = null;
+        private String uri = null;
+        private String response = null;
+        private String nonce = null;
+        private String nc = null;
+        private String cnonce = null;
+        private String realmName = null;
+        private String qop = null;
+
+        private boolean nonceStale = false;
+
+
+        public DigestInfo(String opaque, long nonceValidity, String key,
+               boolean validateUri) {
+            this.opaque = opaque;
+            this.nonceValidity = nonceValidity;
+            this.key = key;
+            this.validateUri = validateUri;
+        }
+
+        public boolean validate(HttpServletRequest request, String authorization,
+                LoginConfig config) {
+            // Validate the authorization credentials format
+            if (authorization == null) {
+                return false;
+            }
+            if (!authorization.startsWith("Digest ")) {
+                return false;
+            }
+            authorization = authorization.substring(7).trim();
+
+            // Bugzilla 37132: http://issues.apache.org/bugzilla/show_bug.cgi?id=37132
+            String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
+
+            String opaque_client = null;
+
+            for (int i = 0; i < tokens.length; i++) {
+                String currentToken = tokens[i];
+                if (currentToken.length() == 0)
+                    continue;
+
+                int equalSign = currentToken.indexOf('=');
+                if (equalSign < 0) {
+                    return false;
+                }
+                String currentTokenName =
+                    currentToken.substring(0, equalSign).trim();
+                String currentTokenValue =
+                    currentToken.substring(equalSign + 1).trim();
+                if ("username".equals(currentTokenName))
+                    userName = removeQuotes(currentTokenValue);
+                if ("realm".equals(currentTokenName))
+                    realmName = removeQuotes(currentTokenValue, true);
+                if ("nonce".equals(currentTokenName))
+                    nonce = removeQuotes(currentTokenValue);
+                if ("nc".equals(currentTokenName))
+                    nc = removeQuotes(currentTokenValue);
+                if ("cnonce".equals(currentTokenName))
+                    cnonce = removeQuotes(currentTokenValue);
+                if ("qop".equals(currentTokenName))
+                    qop = removeQuotes(currentTokenValue);
+                if ("uri".equals(currentTokenName))
+                    uri = removeQuotes(currentTokenValue);
+                if ("response".equals(currentTokenName))
+                    response = removeQuotes(currentTokenValue);
+                if ("opaque".equals(currentTokenName))
+                    opaque_client = removeQuotes(currentTokenValue);
+            }
+
+            if ( (userName == null) || (realmName == null) || (nonce == null)
+                 || (uri == null) || (response == null) ) {
+                return false;
+            }
+
+            // Validate the URI - should match the request line sent by client
+            if (validateUri) {
+                String uriQuery;
+                String query = request.getQueryString();
+                if (query == null) {
+                    uriQuery = request.getRequestURI();
+                } else {
+                    uriQuery = request.getRequestURI() + "?" + query;
+                }
+                if (!uri.equals(uriQuery)) {
+                    return false;
+                }
+            }
+
+            // Validate the Realm name
+            String lcRealm = config.getRealmName();
+            if (lcRealm == null) {
+                lcRealm = REALM_NAME;
+            }
+            if (!lcRealm.equals(realmName)) {
+                return false;
+            }
+
+            // Validate the opaque string
+            if (!this.opaque.equals(opaque_client)) {
+                return false;
+            }
+
+            // Validate nonce
+            int i = nonce.indexOf(":");
+            if (i < 0 || (i + 1) == nonce.length()) {
+                return false;
+            }
+            long nOnceTime;
+            try {
+                nOnceTime = Long.parseLong(nonce.substring(0, i));
+            } catch (NumberFormatException nfe) {
+                return false;
+            }
+            String md5clientIpTimeKey = nonce.substring(i + 1);
+            long currentTime = System.currentTimeMillis();
+            if ((currentTime - nOnceTime) > nonceValidity) {
+                nonceStale = true;
+                return false;
+            }
+            String serverIpTimeKey =
+                request.getRemoteAddr() + ":" + nOnceTime + ":" + key;
+            byte[] buffer = digest(serverIpTimeKey.getBytes(Charset.defaultCharset()));
+
+            String md5ServerIpTimeKey = new String(digestEncoder.encode(buffer));
+            if (!md5ServerIpTimeKey.equals(md5clientIpTimeKey)) {
+                return false;
+            }
+
+            // Validate qop
+            if (qop != null && !QOP.equals(qop)) {
+                return false;
+            }
+
+            // Validate cnonce and nc
+            // Check if presence of nc and nonce is consistent with presence of qop
+            if (qop == null) {
+                if (cnonce != null || nc != null) {
+                    return false;
+                }
+            } else {
+                if (cnonce == null || nc == null) {
+                    return false;
+                }
+                if (nc.length() != 8) {
+                    return false;
+                }
+                try {
+                    Long.parseLong(nc, 16);
+                } catch (NumberFormatException nfe) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public boolean isNonceStale() {
+            return nonceStale;
+        }
+
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/FormAuthenticator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/FormAuthenticator.java
new file mode 100644
index 0000000..57ae517
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -0,0 +1,624 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.SecurityConstraint;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.*;
+import java.util.logging.Level;
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.glassfish.grizzly.http.util.ByteChunk;
+import org.glassfish.grizzly.http.util.CharChunk;
+import org.glassfish.grizzly.http.util.MessageBytes;
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation of FORM BASED
+ * Authentication, as described in the Servlet API Specification, Version 2.2.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.8.2.2 $ $Date: 2008/04/17 18:37:04 $
+ */
+
+public class FormAuthenticator
+    extends AuthenticatorBase {
+
+
+
+    // -------------------------------------------------- Instance Variables
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.FormAuthenticator/1.0";
+
+
+    // ---------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    @Override
+    public String getInfo() {
+        return (this.info);
+    }
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config Login configuration describing how authentication
+     * should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    @Override
+    public boolean authenticate(HttpRequest request,
+                                HttpResponse response,
+                                LoginConfig config)
+        throws IOException {
+
+        // References to objects we will need later
+        HttpServletRequest hreq =
+          (HttpServletRequest) request.getRequest();
+        HttpServletResponse hres =
+          (HttpServletResponse) response.getResponse();
+        Session session = null;
+
+        String contextPath = hreq.getContextPath();
+        String requestURI = request.getDecodedRequestURI();
+        // Is this the action request from the login page?
+        boolean loginAction =
+            requestURI.startsWith(contextPath) &&
+            requestURI.endsWith(Constants.FORM_ACTION);
+
+        // Have we already authenticated someone?
+        Principal principal = hreq.getUserPrincipal();
+        // Treat the first and any subsequent j_security_check requests the
+        // same, by letting them fall through to the j_security_check 
+        // processing section of this method. 
+        if (principal != null && !loginAction) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, neutralizeForLog("Already authenticated '" + principal.getName() + "'"));
+            String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+            if (ssoId != null) {
+                getSession(request, true);
+            }
+            return (true);
+        }
+
+        // Have we authenticated this user before but have caching disabled?
+        // Treat the first and any subsequent j_security_check requests the
+        // same, by letting them fall through to the j_security_check 
+        // processing section of this method. 
+        if (!cache && !loginAction) {
+            session = getSession(request, true);
+            /* Do not log session
+            if (log.isLoggable(Level.FINE))
+               log.log(Level.FINE, "Checking for reauthenticate in session " + session);
+           */
+            String username = (String) session.getNote(Constants.SESS_USERNAME_NOTE);
+            char[] password =                (char[]) session.getNote(Constants.SESS_PASSWORD_NOTE);
+            if ((username != null) && (password != null)) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, neutralizeForLog("Reauthenticating username '" + username + "'"));
+                principal =
+                    context.getRealm().authenticate(username, password);
+                if (principal != null) {
+                    session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
+                    if (!matchRequest(request)) {
+                        register(request, response, principal,
+                                 Constants.FORM_METHOD,
+                                 username, password);
+                        return (true);
+                    }
+                }
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Reauthentication failed, proceed normally");
+            }
+        }
+
+        // Is this the re-submit of the original request URI after successful
+        // authentication?  If so, forward the *original* request instead.
+        if (matchRequest(request)) {
+            session = getSession(request, true);
+            /* Do not log session
+            if (log.isLoggable(Level.FINE)) {
+                String msg = "Restore request from session '" +
+                             session.getIdInternal() + "'";
+                log.log(Level.FINE, msg);
+            }*/
+            principal = (Principal)
+                session.getNote(Constants.FORM_PRINCIPAL_NOTE);
+            register(request, response, principal, Constants.FORM_METHOD,
+                     (String) session.getNote(Constants.SESS_USERNAME_NOTE),
+                     (char[]) session.getNote(Constants.SESS_PASSWORD_NOTE));
+            String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+            if (ssoId != null) {
+                associate(ssoId, getSsoVersion(request), session);
+            }
+            if (restoreRequest(request, session)) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Proceed to restored request");
+                return (true);
+            } else {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Restore of original request failed");
+                hres.sendError(HttpServletResponse.SC_BAD_REQUEST);
+                return (false);
+            }
+        }
+
+        // Acquire references to objects we will need to evaluate
+        MessageBytes uriMB = MessageBytes.newInstance();
+        CharChunk uriCC = uriMB.getCharChunk();
+        uriCC.setLimit(-1);
+        response.setContext(request.getContext());
+
+        // No -- Save this request and redirect to the form login page
+        if (!loginAction) {
+            session = getSession(request, true);
+            /* Do not log session
+            if (log.isLoggable(Level.FINE)) {
+                String msg = "Save request in session '" +
+                             session.getIdInternal() + "'";
+                log.log(Level.FINE, msg);
+            }
+            */
+            saveRequest(request, session);
+
+            //START Apache bug 36136: Refactor the login and error page forward
+            /*
+            RequestDispatcher disp =
+                context.getServletContext().getRequestDispatcher
+                (config.getLoginPage());
+            try {
+                disp.forward(hreq, hres);
+                response.finishResponse();
+            } catch (Throwable t) {
+                log.warn("Unexpected error forwarding to login page", t);
+            }
+            */
+            forwardToLoginPage(request, response, config);
+            //END Apache bug 36136: Refactor the login and error page forward
+
+            return (false);
+        }
+
+        // Yes -- Validate the specified credentials and redirect
+        // to the error page if they are not correct
+        Realm realm = context.getRealm();
+        String username = hreq.getParameter(Constants.FORM_USERNAME);
+        String pwd = hreq.getParameter(Constants.FORM_PASSWORD);
+        char[] password = ((pwd != null)? pwd.toCharArray() : null);
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, neutralizeForLog("Authenticating username '" + username + "'"));
+        principal = realm.authenticate(username, password);
+        if (principal == null) {
+
+            //START Apache bug 36136: Refactor the login and error page forward
+            /*
+            RequestDispatcher disp =
+                context.getServletContext().getRequestDispatcher
+                (config.getErrorPage());
+            try {
+                disp.forward(hreq, hres);
+            } catch (Throwable t) {
+                log.warn("Unexpected error forwarding to error page", t);
+            }
+            */
+            forwardToErrorPage(request, response, config);
+            //END Apache bug 36136: Refactor the login and error page forward
+
+            return (false);
+        }
+
+        // Save the authenticated Principal in our session
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, neutralizeForLog("Authentication of '" + username + "' was successful"));
+        if (session == null)
+            session = getSession(request, true);
+        session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
+
+        // If we are not caching, save the username and password as well
+        if (!cache) {
+            session.setNote(Constants.SESS_USERNAME_NOTE, username);
+            session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+        }
+
+        // Redirect the user to the original request URI (which will cause
+        // the original request to be restored)
+        requestURI = savedRequestURL(session);
+        if (requestURI == null) {
+            // requestURI will be null if the login form is submitted
+            // directly, i.e., if there has not been any original request
+            // that was stored away before the redirect to the login form was
+            // issued. In this case, assume that the original request has been
+            // for the context root, and have the welcome page mechanism take
+            // care of it
+            requestURI = hreq.getContextPath() + "/";
+            register(request, response, principal, Constants.FORM_METHOD,
+                     (String) session.getNote(Constants.SESS_USERNAME_NOTE),
+                     (char[]) session.getNote(Constants.SESS_PASSWORD_NOTE));
+            String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+            if (ssoId != null) {
+                associate(ssoId, getSsoVersion(request), session);
+            }
+        }
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, neutralizeForLog("Redirecting to original '" + requestURI + "'"));
+        }
+
+        hres.sendRedirect(hres.encodeRedirectURL(requestURI));
+
+        return (false);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    @Override
+    protected String getAuthMethod() {
+        return HttpServletRequest.FORM_AUTH;
+    }
+
+    /**
+     * Does this request match the saved one (so that it must be the redirect
+     * we signaled after successful authentication?
+     *
+     * @param request The request to be verified
+     */
+    protected boolean matchRequest(HttpRequest request) {
+
+      // Has a session been created?
+      Session session = getSession(request, false);
+      if (session == null)
+          return (false);
+
+      // Is there a saved request?
+      SavedRequest sreq = (SavedRequest)
+          session.getNote(Constants.FORM_REQUEST_NOTE);
+      if (sreq == null)
+          return (false);
+
+      // Is there a saved principal?
+      if (session.getNote(Constants.FORM_PRINCIPAL_NOTE) == null)
+          return (false);
+
+      // Does the request URI match?
+      HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+      String requestURI = hreq.getRequestURI();
+      if (requestURI == null)
+          return (false);
+      return (requestURI.equals(sreq.getRequestURI()));
+
+    }
+
+
+    /**
+     * Restore the original request from information stored in our session.
+     * If the original request is no longer present (because the session
+     * timed out), return <code>false</code>; otherwise, return
+     * <code>true</code>.
+     *
+     * @param request The request to be restored
+     * @param session The session containing the saved information
+     */
+    protected boolean restoreRequest(HttpRequest request, Session session)
+            throws IOException {
+
+        // Retrieve and remove the SavedRequest object from our session
+        SavedRequest saved = (SavedRequest)
+            session.getNote(Constants.FORM_REQUEST_NOTE);
+        /*
+         * PWC 6463046:
+         * Do not remove the saved request: It will be needed again in case
+         * another j_security_check is sent. The saved request will be
+         * purged when the session expires.
+        session.removeNote(Constants.FORM_REQUEST_NOTE);
+         */
+        session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
+        if (saved == null)
+            return (false);
+
+        // Swallow any request body since we will be replacing it
+        // Need to do this before headers are restored as AJP connector uses
+        // content length header to determine how much data needs to be read for
+        // request body
+        byte[] buffer = new byte[4096];
+        InputStream is = request.getStream();
+        while (is.read(buffer) >= 0) {
+            // Ignore request body
+        }
+
+        // Modify our current request to reflect the original one
+        request.clearCookies();
+        Iterator<Cookie> cookies = saved.getCookies();
+        while (cookies.hasNext()) {
+            request.addCookie(cookies.next());
+        }
+
+        String method = saved.getMethod();
+        boolean cachable = "GET".equalsIgnoreCase(method) ||
+                "HEAD".equalsIgnoreCase(method);
+        request.clearHeaders();
+        Iterator<String> names = saved.getHeaderNames();
+        while (names.hasNext()) {
+            String name = names.next();
+            // The browser isn't expecting this conditional response now.
+            // Assuming that it can quietly recover from an unexpected 412.
+            // BZ 43687
+            if(!("If-Modified-Since".equalsIgnoreCase(name) ||
+                    (cachable && "If-None-Match".equalsIgnoreCase(name)))) {
+                Iterator<String> values = saved.getHeaderValues(name);
+                while (values.hasNext()) {
+                    request.addHeader(name, values.next());
+                }
+            }
+        }
+
+        request.setContentLength(saved.getContentLenght());
+
+        request.clearLocales();
+        Iterator<Locale> locales = saved.getLocales();
+        while (locales.hasNext()) {
+            request.addLocale(locales.next());
+        }
+
+        request.clearParameters();
+        // setQueryStringEncoding is done inside request.clearParameters
+
+        ByteChunk body = saved.getBody();
+
+        if (body != null) {
+            request.replayPayload(body.getBytes());
+
+            // If no content type specified, use default for POST
+            String savedContentType = saved.getContentType();
+            if (savedContentType == null && "POST".equalsIgnoreCase(method)) {
+                savedContentType = "application/x-www-form-urlencoded";
+            }
+
+            request.setContentType(savedContentType);
+        }
+
+        request.setMethod(method);
+        request.setQueryString(saved.getQueryString());
+
+        return true;
+
+    }
+
+
+    /**
+     * Called to forward to the login page. may redirect current request to HTTPS
+     *
+     * @param request HttpRequest we are processing
+     * @param response HttpResponse we are creating
+     * @param config    Login configuration describing how authentication
+     *                  should be performed
+     */
+    protected void forwardToLoginPage(HttpRequest request,
+                                      HttpResponse response,
+                                      LoginConfig config) {
+
+        if (isChangeSessionIdOnAuthentication() && getSession(request, false) != null) {
+            request.changeSessionId();
+        }
+
+        ServletContext sc = context.getServletContext();
+        try {
+            String loginPage = config.getLoginPage();
+            if (!request.getRequest().isSecure()) {
+                Realm realm = context.getRealm();
+                if (realm != null) {
+                    SecurityConstraint[] secConstraints =
+                            realm.findSecurityConstraints(loginPage, "GET", context);
+                    if (secConstraints != null &&
+                            !realm.hasUserDataPermission
+                                    (request, response,secConstraints, loginPage, "GET")) {
+                        /*
+                         * Note that hasUserDataPermission will have already
+                         * issued a redirect to HTTPS unless redirects
+                         * have been disabled, in which case it will have
+                         * called sendError(FORBIDDEN)
+                         */
+                        return;
+                    }
+                }
+            }
+            RequestDispatcher disp = sc.getRequestDispatcher(loginPage);
+            disp.forward(request.getRequest(), response.getResponse());
+            //NOTE: is finishResponse necessary or is it unnecessary after forward
+            response.finishResponse();
+        } catch (Throwable t) {
+            log.log(Level.WARNING, LogFacade.UNEXPECTED_ERROR_FORWARDING_TO_LOGIN_PAGE,
+                    t);
+        }
+    }
+    
+    
+    /**
+     * Called to forward to the error page. may redirect current request to HTTPS
+     *
+     * @param request HttpRequest we are processing
+     * @param response HttpResponse we are creating
+     * @param config    Login configuration describing how authentication
+     *                  should be performed
+     */
+    protected void forwardToErrorPage(HttpRequest request,
+                                HttpResponse response,
+                                LoginConfig config) {
+        ServletContext sc = context.getServletContext();
+        try {
+            String errorPage = config.getErrorPage();
+            if (!request.getRequest().isSecure()) {
+                Realm realm = context.getRealm();
+                if (realm != null) {
+                    SecurityConstraint[] secConstraints =
+                            realm.findSecurityConstraints(errorPage, "GET", context);
+                    if (secConstraints != null &&
+                            !realm.hasUserDataPermission
+                                    (request, response,secConstraints, errorPage, "GET")) {
+                        /*
+                         * Note that hasUserDataPermission will have already
+                         * issued a redirect to HTTPS unless redirects
+                         * have been disabled, in which case it will have
+                         * called sendError(FORBIDDEN).
+                         */
+                        return;
+                    }
+                }
+            }
+            RequestDispatcher disp = sc.getRequestDispatcher(errorPage);
+            disp.forward(request.getRequest(), response.getResponse());
+        } catch (Throwable t) {
+            log.log(Level.WARNING, LogFacade.UNEXPECTED_ERROR_FORWARDING_TO_LOGIN_PAGE,
+                    t);
+        }
+    }
+    
+    /**
+     * Save the original request information into our session.
+     *
+     * @param request The request to be saved
+     * @param session The session to contain the saved information
+     */
+    //START Apache bug 36136: Refactor the login and error page forward
+    //private void saveRequest(HttpRequest request, Session session) {
+    protected void saveRequest(HttpRequest request, Session session)
+            throws IOException {
+    //END Apache bug 36136: Refactor the login and error page forward
+
+        // Create and populate a SavedRequest object for this request
+        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+        SavedRequest saved = new SavedRequest();
+        Cookie cookies[] = hreq.getCookies();
+        if (cookies != null) {
+            for (int i = 0; i < cookies.length; i++)
+                saved.addCookie(cookies[i]);
+        }
+        Enumeration names = hreq.getHeaderNames();
+        while (names.hasMoreElements()) {
+            String name = (String) names.nextElement();
+            Enumeration values = hreq.getHeaders(name);
+            while (values.hasMoreElements()) {
+                String value = (String) values.nextElement();
+                saved.addHeader(name, value);
+            }
+        }
+
+        saved.setContentLength(hreq.getContentLength());
+
+        Enumeration locales = hreq.getLocales();
+        while (locales.hasMoreElements()) {
+            Locale locale = (Locale) locales.nextElement();
+            saved.addLocale(locale);
+        }
+
+        // May need to acknowledge a 100-continue expectation
+        ((HttpResponse)request.getResponse()).sendAcknowledgement();
+
+        ByteChunk body = new ByteChunk();
+        body.setLimit(request.getConnector().getMaxSavePostSize());
+
+        byte[] buffer = new byte[4096];
+        int bytesRead;
+        InputStream is = request.getStream();
+
+        while ( (bytesRead = is.read(buffer) ) >= 0) {
+            body.append(buffer, 0, bytesRead);
+        }
+
+        // Only save the request body if there is something to save
+        if (body.getLength() > 0) {
+            saved.setContentType(hreq.getContentType());
+            saved.setBody(body);
+        }
+
+        saved.setMethod(hreq.getMethod());
+        saved.setQueryString(hreq.getQueryString());
+        saved.setRequestURI(hreq.getRequestURI());
+
+        // Stash the SavedRequest in our session for later use
+        session.setNote(Constants.FORM_REQUEST_NOTE, saved);
+    }
+
+
+    /**
+     * Return the request URI (with the corresponding query string, if any)
+     * from the saved request so that we can redirect to it.
+     *
+     * @param session Our current session
+     */
+    //START Apache bug 36136: Refactor the login and error page forward
+    //private String savedRequestURL(Session session) {
+    protected String savedRequestURL(Session session) {
+    //END Apache bug 36136: Refactor the login and error page forward
+
+        SavedRequest saved =
+            (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE);
+        if (saved == null)
+            return (null);
+        StringBuilder sb = new StringBuilder(saved.getRequestURI());
+        if (saved.getQueryString() != null) {
+            sb.append('?');
+            sb.append(saved.getQueryString());
+        }
+
+        return (sb.toString());
+    }
+
+    private long getSsoVersion(HttpRequest request) {
+        long ssoVersion = 0L;
+        Long ssoVersionObj = (Long)request.getNote(
+                Constants.REQ_SSO_VERSION_NOTE);
+        if (ssoVersionObj != null) {
+            ssoVersion = ssoVersionObj.longValue();
+        }
+        return ssoVersion;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java
new file mode 100644
index 0000000..ec13d2a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.realm.GenericPrincipal;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation that checks
+ * only security constraints not involving user authentication.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.4 $ $Date: 2007/03/05 20:42:26 $
+ */
+
+public final class NonLoginAuthenticator
+    extends AuthenticatorBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    //START SJSAS 6202703
+    /**
+     * Principal name of nonlogin principal.
+     */
+    private static final String NONLOGIN_PRINCIPAL_NAME = "nonlogin-principal";
+    
+    /**
+     * A dummy principal that is set to request in authenticate method.
+     */
+    private final GenericPrincipal NONLOGIN_PRINCIPAL =
+        new GenericPrincipal(NONLOGIN_PRINCIPAL_NAME, (char[]) null, 
+                            (java.util.List<String>) null);
+    //END SJSAS 6202703
+    
+    /**
+     * Descriptive information about this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.authenticator.NonLoginAuthenticator/1.0";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    @Override
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * constraint has been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config Login configuration describing how authentication
+     * should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    @Override
+    public boolean authenticate(HttpRequest request,
+                                HttpResponse response,
+                                LoginConfig config)
+        throws IOException {
+
+        if (debug >= 1)
+            log("User authentication is not required");
+        
+        //START SJSAS 6202703
+        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+        if (hreq.getUserPrincipal() == null) {
+            request.setUserPrincipal(NONLOGIN_PRINCIPAL);
+        }
+        //END SJSAS 6202703
+        return (true);
+
+
+    }
+
+    /**
+     * Return the authentication method, which is vendor-specific and
+     * not defined by HttpServletRequest.
+     */
+    @Override
+    protected String getAuthMethod() {
+        return "NONE";
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SSLAuthenticator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SSLAuthenticator.java
new file mode 100644
index 0000000..6c120b8
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SSLAuthenticator.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.deploy.LoginConfig;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+
+
+/**
+ * An <b>Authenticator</b> and <b>Valve</b> implementation of authentication
+ * that utilizes SSL certificates to identify client users.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.4 $ $Date: 2007/04/17 21:33:22 $
+ */
+
+public class SSLAuthenticator
+    extends AuthenticatorBase {
+
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.SSLAuthenticator/1.0";
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    @Override
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Authenticate the user by checking for the existence of a certificate
+     * chain, and optionally asking a trust manager to validate that we trust
+     * this user.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param config Login configuration describing how authentication
+     * should be performed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    @Override
+    public boolean authenticate(HttpRequest request,
+                                HttpResponse response,
+                                LoginConfig config)
+        throws IOException {
+
+        // Have we already authenticated someone?
+        Principal principal =
+            ((HttpServletRequest) request.getRequest()).getUserPrincipal();
+        if (principal != null) {
+            if (debug >= 1) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.PRINCIPAL_BEEN_AUTHENTICATED_INFO),
+                                                  principal.getName());
+                log(msg);
+            }
+            return (true);
+        }
+
+        // Retrieve the certificate chain for this client
+        HttpServletResponse hres =
+            (HttpServletResponse) response.getResponse();
+        if (debug >= 1)
+            log(rb.getString(LogFacade.LOOK_UP_CERTIFICATE_INFO));
+
+        X509Certificate certs[] = (X509Certificate[])
+            request.getRequest().getAttribute(Globals.CERTIFICATES_ATTR);
+        if ((certs == null) || (certs.length < 1)) {
+            certs = (X509Certificate[])
+                request.getRequest().getAttribute(Globals.SSL_CERTIFICATE_ATTR);
+        }
+        if ((certs == null) || (certs.length < 1)) {
+            if (debug >= 1)
+                log(rb.getString(LogFacade.NO_CERTIFICATE_INCLUDED_INFO));
+            // BEGIN S1AS 4878272
+            hres.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            response.setDetailMessage(rb.getString(LogFacade.NO_CLIENT_CERTIFICATE_CHAIN));
+            // END S1AS 4878272
+            return (false);
+        }
+
+        // Authenticate the specified certificate chain
+        principal = context.getRealm().authenticate(certs);
+        if (principal == null) {
+            if (debug >= 1)
+                log("Realm.authenticate() returned false");
+            // BEGIN S1AS 4878272
+            hres.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+            response.setDetailMessage(rb.getString(LogFacade.CANNOT_AUTHENTICATE_WITH_CREDENTIALS));
+            // END S1AS 4878272
+            return (false);
+        }
+
+        // Cache the principal (if requested) and record this authentication
+        register(request, response, principal, Constants.CERT_METHOD,
+                 null, null);
+        String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
+        if (ssoId != null) {
+            getSession(request, true);
+        }
+
+        return (true);
+
+    }
+
+    @Override
+    protected String getAuthMethod() {
+        return HttpServletRequest.CLIENT_CERT_AUTH;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SavedRequest.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SavedRequest.java
new file mode 100644
index 0000000..097a2dc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SavedRequest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+
+import javax.servlet.http.Cookie;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+
+import org.glassfish.grizzly.http.util.ByteChunk;
+
+/**
+ * Object that saves the critical information from a request so that
+ * form-based authentication can reproduce it once the user has been
+ * authenticated.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b> - It is assumed that this object is accessed
+ * only from the context of a single thread, so no synchronization around
+ * internal collection classes is performed.
+ * <p>
+ * <b>FIXME</b> - Currently, this object has no mechanism to save or
+ * restore the data content of the request, although it does save
+ * request parameters so that a POST transaction can be faithfully
+ * duplicated.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:28 $
+ */
+
+public final class SavedRequest {
+
+
+    /**
+     * The set of Cookies associated with this Request.
+     */
+    private ArrayList<Cookie> cookies = new ArrayList<Cookie>();
+
+    public void addCookie(Cookie cookie) {
+        cookies.add(cookie);
+    }
+
+    public Iterator<Cookie> getCookies() {
+        return (cookies.iterator());
+    }
+
+
+    /**
+     * The set of Headers associated with this Request.  Each key is a header
+     * name, while the value is a ArrayList containing one or more actual
+     * values for this header.  The values are returned as an Iterator when
+     * you ask for them.
+     */
+    private HashMap<String, ArrayList<String>> headers =
+        new HashMap<String, ArrayList<String>>();
+
+    public void addHeader(String name, String value) {
+        ArrayList<String> values = (ArrayList<String>) headers.get(name);
+        if (values == null) {
+            values = new ArrayList<String>();
+            headers.put(name, values);
+        }
+        values.add(value);
+    }
+
+    public Iterator<String> getHeaderNames() {
+        return (headers.keySet().iterator());
+    }
+
+    public Iterator<String> getHeaderValues(String name) {
+        ArrayList<String> values = (ArrayList<String>) headers.get(name);
+        if (values == null)
+            return ((new ArrayList<String>()).iterator());
+        else
+            return (values.iterator());
+    }
+
+
+    /**
+     * The set of Locales associated with this Request.
+     */
+    private ArrayList<Locale> locales = new ArrayList<Locale>();
+
+    public void addLocale(Locale locale) {
+        locales.add(locale);
+    }
+
+    public Iterator<Locale> getLocales() {
+        return (locales.iterator());
+    }
+
+
+    /**
+     * The request method used on this Request.
+     */
+    private String method = null;
+
+    public String getMethod() {
+        return (this.method);
+    }
+
+    public void setMethod(String method) {
+        this.method = method;
+    }
+
+
+
+    /**
+     * The set of request parameters associated with this Request.  Each
+     * entry is keyed by the parameter name, pointing at a String array of
+     * the corresponding values.
+     */
+    private HashMap<String, String[]> parameters =
+        new HashMap<String, String[]>();
+
+    public void addParameter(String name, String values[]) {
+        parameters.put(name, values);
+    }
+
+    public Iterator<String> getParameterNames() {
+        return (parameters.keySet().iterator());
+    }
+
+    public String[] getParameterValues(String name) {
+        return parameters.get(name);
+    }
+
+
+    /**
+     * The query string associated with this Request.
+     */
+    private String queryString = null;
+
+    public String getQueryString() {
+        return (this.queryString);
+    }
+
+    public void setQueryString(String queryString) {
+        this.queryString = queryString;
+    }
+
+
+    /**
+     * The request URI associated with this Request.
+     */
+    private String requestURI = null;
+
+    public String getRequestURI() {
+        return (this.requestURI);
+    }
+
+    public void setRequestURI(String requestURI) {
+        this.requestURI = requestURI;
+    }
+
+    /**
+     * The body of this request.
+     */
+    private ByteChunk body = null;
+
+    public ByteChunk getBody() {
+        return (this.body);
+    }
+
+    public void setBody(ByteChunk body) {
+        this.body = body;
+    }
+
+    /**
+     * The content type of the request, used if this is a POST.
+     */
+    private String contentType = null;
+
+    public String getContentType() {
+        return (this.contentType);
+    }
+
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    private int contentLength = -1;
+
+    public int getContentLenght() {
+        return contentLength;
+    }
+
+    public void setContentLength(int contentLength) {
+        this.contentLength = contentLength;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SingleSignOn.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SingleSignOn.java
new file mode 100644
index 0000000..b2fb27e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SingleSignOn.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.Logger;
+import org.apache.catalina.valves.ValveBase;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.security.Principal;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+
+
+/**
+ * A <strong>Valve</strong> that supports a "single sign on" user experience,
+ * where the security identity of a user who successfully authenticates to one
+ * web application is propagated to other web applications in the same
+ * security domain.  For successful use, the following requirements must
+ * be met:
+ * <ul>
+ * <li>This Valve must be configured on the Container that represents a
+ *     virtual host (typically an implementation of <code>Host</code>).</li>
+ * <li>The <code>Realm</code> that contains the shared user and role
+ *     information must be configured on the same Container (or a higher
+ *     one), and not overridden at the web application level.</li>
+ * <li>The web applications themselves must use one of the standard
+ *     Authenticators found in the
+ *     <code>org.apache.catalina.authenticator</code> package.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.7 $ $Date: 2007/05/05 05:31:53 $
+ */
+
+public class SingleSignOn
+    extends ValveBase
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    implements Lifecycle, SessionListener {
+    */
+    // START CR 6411114
+    implements SessionListener {
+    // END CR 6411114
+
+
+    // ----------------------------------------------------- Static Variables
+
+    private static final java.util.logging.Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    /**
+     * Descriptive information about this Valve implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.authenticator.SingleSignOn";
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The cache of SingleSignOnEntry instances for authenticated Principals,
+     * keyed by the cookie value that is used to select them.
+     */
+    protected Map<String, SingleSignOnEntry> cache = new HashMap<String, SingleSignOnEntry>();
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+    */
+
+    /**
+     * Component started flag.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    protected boolean started = false;
+    */
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the debugging detail level.
+     */
+    public int getDebug() {
+        return (this.debug);
+    }
+
+    /**
+     * Set the debugging detail level.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+        this.debug = debug;
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    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.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+    */
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+    */
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+        // START CR 6411114
+        if (started)            // Ignore multiple starts
+            return;
+        super.start();
+        // END CR 6411114
+
+        if (debug >= 1)
+            log(rb.getString(LogFacade.START_COMPONENT_INFO));
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+        // START CR 6411114
+        if (!started)       // Ignore stop if not started
+            return;
+        // END CR 6411114
+
+        if (debug >= 1)
+            log(rb.getString(LogFacade.STOP_COMPONENT_INFO));
+        // START CR 6411114
+        super.stop();
+        // END CR 6411114
+
+    }
+
+
+    // ------------------------------------------------ SessionListener Methods
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event SessionEvent that has occurred
+     */
+    public void sessionEvent(SessionEvent event) {
+
+        // We only care about session destroyed events
+        if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType()))
+            return;
+
+        // Look up the single session id associated with this session (if any)
+        Session session = event.getSession();
+        if (debug >= 1) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.PROCESS_SESSION_DESTROYED_INFO), session);
+            log(msg);
+        }
+        String ssoId = session.getSsoId();
+        if (ssoId == null) {
+            return;
+        }
+
+        deregister(ssoId, session);
+    }
+
+
+    // ---------------------------------------------------------- Valve Methods
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Perform single-sign-on support processing for this request.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public int invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        // If this is not an HTTP request and response, just pass them on
+        /* GlassFish 6386229
+        if (!(request instanceof HttpRequest) ||
+            !(response instanceof HttpResponse)) {
+            return INVOKE_NEXT;
+        }
+        */
+        HttpServletRequest hreq =
+            (HttpServletRequest) request.getRequest();
+        HttpServletResponse hres =
+            (HttpServletResponse) response.getResponse();
+        request.removeNote(Constants.REQ_SSOID_NOTE);
+        request.removeNote(Constants.REQ_SSO_VERSION_NOTE);
+
+        // Has a valid user already been authenticated?
+        if (debug >= 1) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.PROCESS_REQUEST_INFO), hreq.getRequestURI());
+            log(msg);
+        }
+        if (hreq.getUserPrincipal() != null) {
+            if (debug >= 1) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.PRINCIPAL_BEEN_AUTHENTICATED_INFO),
+                                                  hreq.getUserPrincipal());
+                log(msg);
+            }
+            return END_PIPELINE;
+        }
+
+        // Check for the single sign on cookie
+        if (debug >= 1)
+            log(rb.getString(LogFacade.CHECK_SSO_COOKIE_INFO));
+        Cookie cookie = null;
+        Cookie versionCookie = null;
+        Cookie cookies[] = hreq.getCookies();
+        if (cookies == null)
+            cookies = new Cookie[0];
+        for (int i = 0; i < cookies.length; i++) {
+            if (Constants.SINGLE_SIGN_ON_COOKIE.equals(cookies[i].getName())) {
+                cookie = cookies[i];
+            } else if (Constants.SINGLE_SIGN_ON_VERSION_COOKIE.equals(cookies[i].getName())) {
+                versionCookie = cookies[i];
+            }
+            
+            if (cookie != null && versionCookie != null) {
+                break;
+            }
+        }
+        if (cookie == null) {
+            if (debug >= 1)
+                log(rb.getString(LogFacade.SSO_COOKIE_NOT_PRESENT_INFO));
+            return INVOKE_NEXT;
+        }
+
+        // Look up the cached Principal associated with this cookie value
+        if (debug >= 1) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CHECK_CACHED_PRINCIPAL_INFO), cookie.getValue());
+            log(msg);
+        }
+        long version = 0;
+        if (isVersioningSupported() && versionCookie != null) {
+            version = Long.parseLong(versionCookie.getValue());
+        }
+        SingleSignOnEntry entry = lookup(cookie.getValue(), version);
+        if (entry != null) {
+            if (debug >= 1) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.FOUND_CACHED_PRINCIPAL_AUTH_TYPE_INFO),
+                                                  new Object[] {entry.getPrincipal().getName(), entry.getAuthType()});
+                log(msg);
+            }
+            request.setNote(Constants.REQ_SSOID_NOTE, cookie.getValue());
+            if (isVersioningSupported()) {
+                long ver = entry.incrementAndGetVersion();
+                request.setNote(Constants.REQ_SSO_VERSION_NOTE,
+                        Long.valueOf(ver));
+            }
+
+            ((HttpRequest) request).setAuthType(entry.getAuthType());
+            ((HttpRequest) request).setUserPrincipal(entry.getPrincipal());
+        } else {
+            if (debug >= 1)
+                log(rb.getString(LogFacade.NO_CACHED_PRINCIPAL_FOUND_INFO));
+            cookie.setMaxAge(0);
+            hres.addCookie(cookie);
+        }
+
+        // Invoke the next Valve in our pipeline
+        return INVOKE_NEXT;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String rendering of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("SingleSignOn[");
+        if (container == null )
+            sb.append("Container is null");
+        else
+            sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Associate the specified single sign on identifier with the
+     * specified Session.
+     *
+     * @param ssoId Single sign on identifier
+     * @param ssoVersion Single sign on version
+     * @param session Session to be associated
+     */
+    public void associate(String ssoId, long ssoVersion, Session session) {
+
+        if (!started) {
+            return;
+        }
+
+        if (debug >= 1)
+            log(rb.getString(LogFacade.ASSOCIATE_SSO_WITH_SESSION_INFO));
+
+        SingleSignOnEntry sso = lookup(ssoId, ssoVersion);
+        if (sso != null) {
+            session.setSsoId(ssoId);
+            session.setSsoVersion(ssoVersion);
+            sso.addSession(this, session);
+        }
+    }
+
+    /**
+     * Deregister the specified session.  If it is the last session,
+     * then also get rid of the single sign on identifier
+     *
+     * @param ssoId Single sign on identifier
+     * @param session Session to be deregistered
+     */
+    protected void deregister(String ssoId, Session session) {
+
+        SingleSignOnEntry sso = lookup(ssoId);
+        if ( sso == null )
+            return;
+
+        session.setSsoId(null);
+        session.setSsoVersion(0L);
+        sso.removeSession( session );
+
+        // see if we are the last session, if so blow away ssoId
+        if (sso.isEmpty()) {
+            synchronized (cache) {
+                cache.remove(ssoId);
+            }
+        }
+    }
+
+
+    /**
+     * Register the specified Principal as being associated with the specified
+     * value for the single sign on identifier.
+     *
+     * @param ssoId Single sign on identifier to register
+     * @param principal Associated user principal that is identified
+     * @param authType Authentication type used to authenticate this
+     *  user principal
+     * @param username Username used to authenticate this user
+     * @param password Password used to authenticate this user
+     */
+    protected void register(String ssoId, Principal principal, String authType,
+                  String username, char[] password, String realmName) {
+
+        if (debug >= 1) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.REGISTERING_SSO_INFO),
+                                              new Object[] {ssoId, principal.getName(), authType});
+            log(msg);
+        }
+        synchronized (cache) {
+            cache.put(ssoId, new SingleSignOnEntry(ssoId, 0L, principal, authType,
+                                                   username, realmName));
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+        message = neutralizeForLog(message);
+        Logger logger = container.getLogger();
+        if (logger != null) {
+            logger.log(this.toString() + ": " + message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, this.toString() + ": " + message);
+            }
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    protected void log(String message, Throwable t) {
+        message = neutralizeForLog(message);
+        Logger logger = container.getLogger();
+        if (logger != null) {
+            logger.log(this.toString() + ": " + message, t,
+                Logger.WARNING);
+        } else {
+            log.log(Level.WARNING,
+                this.toString() + ": " + message, t);
+        }
+    }
+
+
+    /**
+     * Look up and return the cached SingleSignOn entry associated with this
+     * sso id value, if there is one; otherwise return <code>null</code>.
+     *
+     * @param ssoId Single sign on identifier to look up
+     */
+    protected SingleSignOnEntry lookup(String ssoId) {
+
+        synchronized (cache) {
+            return cache.get(ssoId);
+        }
+
+    }
+
+    /**
+     * Look up and return the cached SingleSignOn entry associated with this
+     * sso id value, if there is one; otherwise return <code>null</code>.
+     *
+     * @param ssoId Single sign on identifier to look up
+     * @param ssoVersion Single sign on version to look up
+     */
+    protected SingleSignOnEntry lookup(String ssoId, long ssoVersion) {
+
+        return lookup(ssoId);
+
+    }
+
+    /**
+     * Return a boolean to indicate whether the sso id version is
+     * supported or not.
+     */
+    public boolean isVersioningSupported() {
+        return false;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SingleSignOnEntry.java b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SingleSignOnEntry.java
new file mode 100644
index 0000000..2f4c107
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/SingleSignOnEntry.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.authenticator;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Session;
+
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A private class representing entries in the cache of authenticated users.
+ */
+public class SingleSignOnEntry {
+
+    private static final Logger log = LogFacade.getLogger();
+
+    protected String id = null;
+
+    protected String authType = null;
+
+    protected Principal principal = null;
+
+    protected Set<Session> sessions = new HashSet<Session>();
+
+    protected String username = null;
+
+    protected String realmName = null;
+
+    protected long lastAccessTime;
+
+    protected AtomicLong version = null;
+
+    public SingleSignOnEntry(String id, long ver,
+                             Principal principal, String authType,
+                             String username, String realmName) {
+        super();
+        this.id = id;
+        this.version = new AtomicLong(ver);
+        this.principal = principal;
+        this.authType = authType;
+        this.username = username;
+        this.realmName = realmName;
+        this.lastAccessTime = System.currentTimeMillis();
+    }
+
+    /**
+     * Adds the given session to this SingleSignOnEntry if it does not
+     * already exist.
+     * 
+     * @return true if the session was added, false otherwise
+     */
+    public synchronized boolean addSession(SingleSignOn sso, Session session) {
+        boolean result = sessions.add(session);
+        if (result) {
+            session.addSessionListener(sso);
+        }
+
+        return true;
+    }
+
+    public synchronized void removeSession(Session session) {
+        sessions.remove(session);
+    }
+
+
+    /**
+     * Returns true if this SingleSignOnEntry does not have any sessions
+     * associated with it, and false otherwise.
+     *
+     * @return true if this SingleSignOnEntry does not have any sessions
+     * associated with it, and false otherwise
+     */
+    public synchronized boolean isEmpty() {
+        return (sessions.size() == 0);
+    }
+
+
+    /**
+     * Expires all sessions associated with this SingleSignOnEntry
+     *
+     */
+    public synchronized void expireSessions() {
+        for (Session session: sessions) {
+            if (log.isLoggable(Level.FINE)) {
+
+                log.log(Level.FINE, " Invalidating session " + session);
+            }
+        
+            //6406580 START
+            /*
+            // Invalidate this session
+            session.expire();
+             */
+            // Invalidate this session
+            // if it is not already invalid(ated)
+            if( (session).getIsValid() ) {
+                session.expire();
+            }
+            //6406580 END
+        }
+    }
+
+    /**
+     * Gets the id of this SSO entry.
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Gets the id version of this SSO entry
+     */
+    public long getVersion() {
+        return version.get();
+    }
+
+    /**
+     * Gets the name of the authentication type originally used to authenticate
+     * the user associated with the SSO.
+     *
+     * @return "BASIC", "CLIENT_CERT", "DIGEST", "FORM" or "NONE"
+     */
+    public String getAuthType() {
+        return authType;
+    }
+
+    /**
+     * Gets the <code>Principal</code> that has been authenticated by
+     * the SSO.
+     */
+    public Principal getPrincipal() {
+        return principal;
+    }
+
+    /**
+     * Gets the username provided by the user as part of the authentication
+     * process.
+     */
+    public String getUsername() {
+        return username;
+    }
+
+    public String getRealmName() {
+        return realmName;
+    }
+
+    public long getLastAccessTime() {
+        return lastAccessTime;
+    }
+
+    public void setLastAccessTime(long lastAccessTime) {
+        this.lastAccessTime = lastAccessTime;
+    }
+
+    public long incrementAndGetVersion() {
+        return version.incrementAndGet();
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/package.html b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/package.html
new file mode 100644
index 0000000..8d160bb
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/authenticator/package.html
@@ -0,0 +1,57 @@
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<body>
+
+<p>This package contains <code>Authenticator</code> implementations for the
+various supported authentication methods (BASIC, DIGEST, and FORM).  In
+addition, there is a convenience base class,
+<code>AuthenticatorBase</code>, for customized <code>Authenticator</code>
+implementations.</p>
+
+<p>If you are using the standard context configuration class
+(<code>org.apache.catalina.startup.ContextConfig</code>) to configure the
+Authenticator associated with a particular context, you can register the Java
+class to be used for each possible authentication method by modifying the
+following Properties file:</p>
+<pre>
+    src/share/org/apache/catalina/startup/Authenticators.properties
+</pre>
+
+<p>Each of the standard implementations extends a common base class
+(<code>AuthenticatorBase</code>), which is configured by setting the
+following JavaBeans properties (with default values in square brackets):</p>
+<ul>
+<li><b>cache</b> - Should we cache authenticated Principals (thus avoiding
+    per-request lookups in our underyling <code>Realm</code>) if this request
+    is part of an HTTP session?  [true]</li>
+<li><b>debug</b> - Debugging detail level for this component.  [0]</li>
+</ul>
+
+<p>The standard authentication methods that are currently provided include:</p>
+<ul>
+<li><b>BasicAuthenticator</b> - Implements HTTP BASIC authentication, as
+    described in RFC 2617.</li>
+<li><b>DigestAuthenticator</b> - Implements HTTP DIGEST authentication, as
+    described in RFC 2617.</li>
+<li><b>FormAuthenticator</b> - Implements FORM-BASED authentication, as
+    described in the Servlet API Specification, version 2.2.</li>
+</ul>
+
+</body>
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/AsyncContextImpl.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/AsyncContextImpl.java
new file mode 100644
index 0000000..73cb2bb
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/AsyncContextImpl.java
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.connector;
+
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Globals;
+import org.apache.catalina.core.*;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.EventListener;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+class AsyncContextImpl implements AsyncContext {
+    // Note...this constant is also defined in org.glassfish.weld.WeldDeployer.  If it changes here it must
+    // change there as well.  The reason it is duplicated is so that a dependency from web-core to gf-weld-connector
+    // is not necessary.
+    private static final String WELD_LISTENER = "org.jboss.weld.module.web.servlet.WeldListener";
+
+
+    /*
+     * Event notification types for async mode
+     */
+    static enum AsyncEventType { COMPLETE, TIMEOUT, ERROR, START_ASYNC }
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+    // Default timeout for async operations
+    private static final long DEFAULT_ASYNC_TIMEOUT_MILLIS = 30000L;
+
+    // Thread pool for async dispatches
+    private static final ExecutorService pool =
+        Executors.newCachedThreadPool(new AsyncPoolThreadFactory());
+
+    // The original (unwrapped) request
+    private Request origRequest;
+
+    // The possibly wrapped request passed to ServletRequest.startAsync
+    private ServletRequest servletRequest;
+
+    // The possibly wrapped response passed to ServletRequest.startAsync
+    private ServletResponse servletResponse;
+
+    private boolean isOriginalRequestAndResponse = false;
+
+    private boolean isStartAsyncWithZeroArg = false;
+
+    // defaults to false
+    private AtomicBoolean isDispatchInProgress = new AtomicBoolean();
+
+    private ThreadLocal<Boolean> isDispatchInScope = new ThreadLocal<Boolean>() {
+        @Override
+        protected Boolean initialValue() {
+            return Boolean.FALSE;
+        }
+    };
+
+    private volatile boolean hasDispatch = false;
+
+    private AtomicBoolean isOkToConfigure = new AtomicBoolean(true);
+
+    private long asyncTimeoutMillis = DEFAULT_ASYNC_TIMEOUT_MILLIS;
+
+    private final Queue<AsyncListener> listenerQueue = new ConcurrentLinkedQueue<AsyncListener>();
+
+    private final LinkedList<AsyncListenerContext> asyncListenerContexts =
+        new LinkedList<AsyncListenerContext>();
+
+    // The number of times this AsyncContext has been reinitialized via a call
+    // to ServletRequest#startAsync
+    private AtomicInteger startAsyncCounter = new AtomicInteger(0);
+    // Has AsyncContext.complete been called?
+    private final AtomicBoolean isAsyncCompleteCalled = new AtomicBoolean();
+    // Has AsyncContext.complete been really completed?
+    private final AtomicBoolean isAsyncCompleted = new AtomicBoolean();
+    private volatile boolean delayAsyncDispatchAndComplete = true;
+
+    private ThreadLocal<Boolean> isStartAsyncInScope = new ThreadLocal<Boolean>() {
+        @Override
+        protected Boolean initialValue() {
+            return Boolean.FALSE;
+        }
+    };
+
+    private Handler handler = null;
+
+    /**
+     * Constructor
+     *
+     * @param origRequest the original (unwrapped) request
+     * @param servletRequest the possibly wrapped request passed to
+     * ServletRequest.startAsync
+     * @param servletResponse the possibly wrapped response passed to
+     * ServletRequest.startAsync
+     * @param isStartAsyncWithZeroArg true if the zero-arg version of
+     * startAsync was called, false otherwise
+     */
+    AsyncContextImpl(Request origRequest,
+                            ServletRequest servletRequest,
+                            Response origResponse,
+                            ServletResponse servletResponse,
+                            boolean isStartAsyncWithZeroArg) {
+        this.origRequest = origRequest;
+        init(servletRequest, servletResponse, isStartAsyncWithZeroArg);
+    }
+
+    @Override
+    public ServletRequest getRequest() {
+        return servletRequest;
+    }
+
+    Request getOriginalRequest() {
+        return origRequest;
+    }
+
+    @Override
+    public ServletResponse getResponse() {
+        return servletResponse;
+    }
+
+    @Override
+    public boolean hasOriginalRequestAndResponse() {
+        return isOriginalRequestAndResponse;
+    }
+
+    @Override
+    public void dispatch() {
+        ApplicationDispatcher dispatcher =
+            (ApplicationDispatcher)getZeroArgDispatcher(
+                origRequest, servletRequest, isStartAsyncWithZeroArg);
+
+        dispatch(dispatcher, null, null);
+    }
+
+    @Override
+    public void dispatch(String path) {
+        if (path == null) {
+            throw new IllegalArgumentException("Null path");
+        }
+        ApplicationDispatcher dispatcher = (ApplicationDispatcher)
+            servletRequest.getRequestDispatcher(path);
+
+        dispatch(dispatcher, null, path);
+    }
+
+    @Override
+    public void dispatch(ServletContext context, String path) {
+        if (path == null || context == null) {
+            throw new IllegalArgumentException("Null context or path");
+        }
+        ApplicationDispatcher dispatcher = (ApplicationDispatcher)
+            context.getRequestDispatcher(path);
+
+        dispatch(dispatcher, context, path);
+    }
+
+    private void dispatch(ApplicationDispatcher dispatcher,
+            ServletContext context, String path) {
+
+        isDispatchInScope.set(true);
+        hasDispatch = true;
+        if (dispatcher != null) {
+            if (isDispatchInProgress.compareAndSet(false, true)) {
+                if (delayAsyncDispatchAndComplete) {
+                    handler = new Handler(this, dispatcher);
+                } else {
+                    pool.execute(new Handler(this, dispatcher));
+                }
+            } else {
+                String msg = rb.getString(LogFacade.ASYNC_DISPATCH_ALREADY_IN_PROGRESS_EXCEPTION);
+                throw new IllegalStateException(msg);
+            }
+        } else {
+            // Should never happen, because any unmapped paths will be
+            // mapped to the DefaultServlet
+            if (context == null && path == null) {
+                log.log(Level.WARNING, LogFacade.UNABLE_DETERMINE_TARGET_OF_DISPATCHER);
+            } else if (context == null && path != null) {
+                log.log(Level.WARNING, LogFacade.UNABLE_ACQUIRE_REQUEST_DISPATCHER, path);
+            } else {
+                log.log(Level.WARNING, LogFacade.UNABLE_ACQUIRE_REQUEST_DISPATCHER_IN_SERVLET_CONTEXT,
+                        new Object[] {path, context.getContextPath()});
+            }
+        }
+    }
+
+    void invokeDelayDispatch() {
+        if (handler != null) {
+            pool.execute(handler);
+            handler = null;
+        }
+    }
+
+    boolean isDispatchInScope() {
+        return isDispatchInScope.get();
+    }
+    
+    boolean getAndResetDispatchInScope() {
+        final boolean flag = isDispatchInScope.get();
+        isDispatchInScope.set(Boolean.FALSE);
+        return flag;
+    }
+
+
+    boolean isDelayAsyncDispatchAndComplete() {
+        return delayAsyncDispatchAndComplete;
+    }
+
+    void setDelayAsyncDispatchAndComplete(boolean delayAsync) {
+        delayAsyncDispatchAndComplete = delayAsync;
+    }
+
+    boolean isAsyncComplete() {
+        return isAsyncCompleteCalled.get();
+    }
+
+    @Override
+    public void complete() {
+        if (isAsyncCompleteCalled.compareAndSet(false, true)) {
+            if (delayAsyncDispatchAndComplete) {
+                return;
+            }
+            
+            doComplete();
+        } else {
+            throw new IllegalStateException(rb.getString(
+                    LogFacade.REQUEST_ALREADY_RELEASED_EXCEPTION));
+        }
+    }
+
+    private void doComplete() {
+        if (isAsyncCompleted.compareAndSet(false, true)) {
+            origRequest.asyncComplete();
+        }
+    }
+    
+    void processAsyncOperations() {
+        processAsyncOperations(false);
+    }
+
+    private void processAsyncOperations(boolean exit) {
+        if (isDispatchInScope() || (exit && hasDispatch)) {
+            invokeDelayDispatch();
+        } else if (isAsyncComplete()) {
+            doComplete();
+        }
+    }
+
+    
+    /**
+     * The method is called once service thread finished with the
+     * request/response processing and doesn't rely on its existence anymore.
+     * 
+     * Now it's safe to finish async request/response processing.
+     */
+    void onExitService() {
+        delayAsyncDispatchAndComplete = false;
+        processAsyncOperations(true);
+    }
+    
+    @Override
+    public void start(Runnable run) {
+        ClassLoader oldCL = null;
+        if (Globals.IS_SECURITY_ENABLED) {
+            PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
+            oldCL = AccessController.doPrivileged(pa);
+        } else {
+            oldCL = Thread.currentThread().getContextClassLoader();
+        }
+
+        try {
+            ClassLoader newCL = origRequest.getContext().getLoader().getClassLoader();
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<Void> pa = new PrivilegedSetTccl(newCL);
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(newCL);
+            }
+
+            pool.execute(run);
+        } finally {
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<Void> pa = new PrivilegedSetTccl(oldCL);
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(oldCL);
+            }
+        }
+    }
+
+    @Override
+    public void addListener(AsyncListener listener) {
+        addListener(listener, this.servletRequest, this.servletResponse);
+    }
+
+    @Override
+    public void addListener(AsyncListener listener,
+                            ServletRequest servletRequest,
+                            ServletResponse servletResponse) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Null listener");
+        }
+
+        if (servletRequest == null || servletResponse == null) {
+            throw new IllegalArgumentException(
+                "Null request, or response");
+        }
+
+        if (!isOkToConfigure.get()) {
+            String msg = rb.getString(LogFacade.ASYNC_CONTEXT_ADD_LISTENER_EXCEPTION);
+            throw new IllegalStateException(msg);
+        }
+
+        synchronized(asyncListenerContexts) {
+            asyncListenerContexts.add(new AsyncListenerContext(
+                listener, servletRequest, servletResponse));
+        }
+    }
+
+    @Override
+    public <T extends AsyncListener> T createListener(Class<T> clazz)
+            throws ServletException {
+        T listener = null;
+        StandardContext ctx = (StandardContext) origRequest.getContext();
+        if (ctx != null) {
+            try {
+                listener = ctx.createListenerInstance(clazz);
+                listenerQueue.add(listener);
+            } catch (Throwable t) {
+                throw new ServletException(t);
+            }
+        }
+        return listener;
+    }
+
+    @Override
+    public void setTimeout(long timeout) {
+        if (!isOkToConfigure.get()) {
+            String msg = rb.getString(LogFacade.ASYNC_CONTEXT_SET_TIMEOUT_EXCEPTION);
+            throw new IllegalStateException(msg);
+        }
+        asyncTimeoutMillis = timeout;
+//        origRequest.setAsyncTimeout(timeout);
+    }
+
+    @Override
+    public long getTimeout() {
+        return asyncTimeoutMillis;
+    }
+
+    /*
+     * Reinitializes this AsyncContext with the given request and response.
+     *
+     * @param servletRequest the ServletRequest with which to initialize
+     * the AsyncContext
+     * @param servletResponse the ServletResponse with which to initialize
+     * the AsyncContext
+     * @param isStartAsyncWithZeroArg true if the zero-arg version of
+     * startAsync was called, false otherwise
+     */
+    void reinitialize(ServletRequest servletRequest,
+                      ServletResponse servletResponse,
+                      boolean isStartAsyncWithZeroArg) {
+
+        init(servletRequest, servletResponse, isStartAsyncWithZeroArg);
+        isDispatchInProgress.set(false);
+        setOkToConfigure(true);
+        startAsyncCounter.incrementAndGet();
+        notifyAsyncListeners(AsyncEventType.START_ASYNC, null);
+    }
+
+    /**
+     * @return value true if calls to AsyncContext#addListener and
+     * AsyncContext#setTimeout will be accepted, and false if these
+     * calls will result in an IllegalStateException
+     */
+    boolean isOkToConfigure() {
+        return isOkToConfigure.get();
+    }
+
+    /**
+     * @param value true if calls to AsyncContext#addListener and
+     * AsyncContext#setTimeout will be accepted, and false if these
+     * calls will result in an IllegalStateException
+     */
+    void setOkToConfigure(boolean value) {
+        isOkToConfigure.set(value);
+    }
+
+    private void init(ServletRequest servletRequest,
+            ServletResponse servletResponse, boolean isStartAsyncWithZeroArg) {
+
+        this.servletRequest = servletRequest;
+        this.servletResponse = servletResponse;
+        // If original or container-wrapped request and response,
+        // AsyncContext#hasOriginalRequestAndResponse must return true;
+        // false otherwise (i.e., if application-wrapped)
+        this.isOriginalRequestAndResponse =
+                ((servletRequest instanceof RequestFacade ||
+                servletRequest instanceof ApplicationHttpRequest) &&
+                (servletResponse instanceof ResponseFacade ||
+                servletResponse instanceof ApplicationHttpResponse));
+
+        this.isStartAsyncWithZeroArg = isStartAsyncWithZeroArg;
+    }
+
+    /**
+     * Determines the dispatcher of a zero-argument async dispatch for the
+     * given request.
+     *
+     * @return the dispatcher of the zero-argument async dispatch
+     */
+    private RequestDispatcher getZeroArgDispatcher(
+            Request origRequest, ServletRequest servletRequest,
+            boolean isStartAsyncWithZeroArg) {
+
+        String dispatchTarget = null;
+        boolean isNamed = false;
+        if ((!isStartAsyncWithZeroArg) &&
+                servletRequest instanceof HttpServletRequest) {
+
+            HttpServletRequest req = (HttpServletRequest)servletRequest;
+            dispatchTarget = getCombinedPath(req);
+        } else {
+            DispatchTargetsInfo dtInfo = (DispatchTargetsInfo)origRequest.getAttribute(
+                    ApplicationDispatcher.LAST_DISPATCH_REQUEST_PATH_ATTR);
+            if (dtInfo != null) {
+                dispatchTarget = dtInfo.getLastDispatchTarget();
+                isNamed = dtInfo.isLastNamedDispatchTarget();
+            }
+            if (dispatchTarget == null) {
+                dispatchTarget = getCombinedPath(origRequest);
+            }
+        }
+
+        RequestDispatcher dispatcher = null;
+        if (dispatchTarget != null) {
+            dispatcher = ((isNamed) ?
+                    servletRequest.getServletContext().getNamedDispatcher(dispatchTarget) :
+                    servletRequest.getRequestDispatcher(dispatchTarget));
+        }
+
+        return dispatcher;
+    }
+
+    private String getCombinedPath(HttpServletRequest req) {
+        String servletPath = req.getServletPath();
+        if (servletPath == null) {
+            return null;
+        }
+        String pathInfo = req.getPathInfo();
+        if (pathInfo == null) {
+            return servletPath;
+        }
+        return servletPath + pathInfo;
+    }
+
+    static class Handler implements Runnable {
+
+        private final AsyncContextImpl asyncContext;
+        private final ApplicationDispatcher dispatcher;
+
+        Handler(AsyncContextImpl asyncContext,
+                ApplicationDispatcher dispatcher) {
+            this.asyncContext = asyncContext;
+            this.dispatcher = dispatcher;
+        }
+
+        @Override
+        public void run() {
+            asyncContext.isStartAsyncInScope.set(Boolean.TRUE);
+            Request origRequest = asyncContext.getOriginalRequest();
+            origRequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR,
+                                     DispatcherType.ASYNC);
+            origRequest.setAsyncStarted(false);
+            int startAsyncCurrent = asyncContext.startAsyncCounter.get();
+            ClassLoader oldCL;
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
+                oldCL = AccessController.doPrivileged(pa);
+            } else {
+                oldCL = Thread.currentThread().getContextClassLoader();
+            }
+
+            try {
+                ClassLoader newCL = origRequest.getContext().getLoader().getClassLoader();
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(newCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(newCL);
+                }
+
+                asyncContext.setDelayAsyncDispatchAndComplete(true);
+                dispatcher.dispatch(asyncContext.getRequest(),
+                    asyncContext.getResponse(), DispatcherType.ASYNC);
+
+                asyncContext.setDelayAsyncDispatchAndComplete(false);
+
+                asyncContext.processAsyncOperations();
+
+                if ((!asyncContext.isAsyncComplete()) &&
+                        asyncContext.startAsyncCounter.compareAndSet(
+                        startAsyncCurrent, startAsyncCurrent)) {
+                    /*
+                     * Close the response after the dispatch target has
+                     * completed execution, unless the dispatch target has called
+                     * ServletRequest#startAsync, in which case the AsyncContext's
+                     * startAsyncCounter will be greater than it was before the
+                     * dispatch
+                     */
+                    asyncContext.complete();
+                } else {
+                    // Reset async timeout
+                    origRequest.setAsyncTimeout(asyncContext.getTimeout());
+                }
+            } catch (Throwable t) {
+                asyncContext.setDelayAsyncDispatchAndComplete(false);
+                asyncContext.notifyAsyncListeners(AsyncEventType.ERROR, t);
+                origRequest.errorDispatchAndComplete(t);
+            } finally {
+                asyncContext.isStartAsyncInScope.set(Boolean.FALSE);
+
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(oldCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(oldCL);
+                }
+            }
+        }
+    }
+
+    boolean isStartAsyncInScope() {
+        return isStartAsyncInScope.get().booleanValue();
+    }
+
+    /*
+     * Notifies all AsyncListeners of the given async event type
+     */
+    void notifyAsyncListeners(AsyncEventType asyncEventType, Throwable t) {
+        LinkedList<AsyncListenerContext> clone;
+        synchronized(asyncListenerContexts) {
+            if (asyncListenerContexts.isEmpty()) {
+                return;
+            }
+            clone =
+                new LinkedList<AsyncListenerContext>(asyncListenerContexts);
+            if (asyncEventType.equals(AsyncEventType.START_ASYNC)) {
+                asyncListenerContexts.clear();
+            }
+        }
+
+        ClassLoader oldCL;
+        if (Globals.IS_SECURITY_ENABLED) {
+            PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
+            oldCL = AccessController.doPrivileged(pa);
+        } else {
+            oldCL = Thread.currentThread().getContextClassLoader();
+        }
+
+        try {
+            ClassLoader newCL = origRequest.getContext().getLoader().getClassLoader();
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<Void> pa = new PrivilegedSetTccl(newCL);
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(newCL);
+            }
+
+            ServletRequestListener weldListener = getWeldListener();
+            if ( weldListener != null ) {
+                // must fire a request initialized so CDI can associate a request with the request and session contexts
+                ServletRequestEvent event = new ServletRequestEvent(origRequest.getContext().getServletContext(), origRequest);
+                weldListener.requestInitialized(event);
+            }
+
+            boolean oldDelay = isDelayAsyncDispatchAndComplete();
+            try {
+                setDelayAsyncDispatchAndComplete(true);
+                for (AsyncListenerContext asyncListenerContext : clone) {
+                    AsyncListener asyncListener =
+                        asyncListenerContext.getAsyncListener();
+                    AsyncEvent asyncEvent = new AsyncEvent(
+                        this, asyncListenerContext.getRequest(),
+                        asyncListenerContext.getResponse(), t);
+                    try {
+                        switch (asyncEventType) {
+                            case COMPLETE:
+                                asyncListener.onComplete(asyncEvent);
+                                break;
+                            case TIMEOUT:
+                                asyncListener.onTimeout(asyncEvent);
+                                break;
+                            case ERROR:
+                                asyncListener.onError(asyncEvent);
+                                break;
+                            case START_ASYNC:
+                                asyncListener.onStartAsync(asyncEvent);
+                                break;
+                            default: // not possible
+                                break;
+                        }
+                    } catch (Throwable throwable) {
+                        log.log(Level.WARNING, LogFacade.ERROR_INVOKE_ASYNCLISTENER,
+                                throwable);
+                    }
+                }
+            } finally {
+                setDelayAsyncDispatchAndComplete(oldDelay);
+                if ( weldListener != null ) {
+                    ServletRequestEvent event = new ServletRequestEvent(origRequest.getContext().getServletContext(), origRequest);
+                    weldListener.requestDestroyed(event);
+                }
+                processAsyncOperations();
+            }
+
+        } finally {
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<Void> pa = new PrivilegedSetTccl(oldCL);
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(oldCL);
+            }
+        }
+    }
+
+    private ServletRequestListener getWeldListener() {
+        List<EventListener> eventListeners = origRequest.getContext().getApplicationEventListeners();
+        if ( eventListeners != null ) {
+            for ( EventListener listener : eventListeners ) {
+                if ( listener.getClass().getName().equals( WELD_LISTENER ) ) {
+                    return (ServletRequestListener) listener;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    static ExecutorService getExecutorService() {
+        return pool;
+    }
+
+    void clear() {
+        synchronized(asyncListenerContexts) {
+            asyncListenerContexts.clear();
+        }
+
+        StandardContext ctx = (StandardContext) origRequest.getContext();
+        if (ctx != null) {
+            for (AsyncListener l : listenerQueue) {
+                ctx.fireContainerEvent(ContainerEvent.PRE_DESTROY, l);
+            }
+        }
+        listenerQueue.clear();
+        servletRequest = null;
+        servletResponse = null;
+        origRequest = null;
+    }
+
+    /**
+     * Class holding all the information required for invoking an
+     * AsyncListener (including the AsyncListener itself).
+     */
+    private static class AsyncListenerContext {
+
+        private AsyncListener listener;
+        private ServletRequest request;
+        private ServletResponse response;
+
+        public AsyncListenerContext(AsyncListener listener,
+                                    ServletRequest request,
+                                    ServletResponse response) {
+            this.listener = listener;
+            this.request = request;
+            this.response = response;
+        }
+
+        public AsyncListener getAsyncListener() {
+            return listener;
+        }
+
+        public ServletRequest getRequest() {
+            return request;
+        }
+
+        public ServletResponse getResponse() {
+            return response;
+        }
+    }
+
+
+    private static final class AsyncPoolThreadFactory implements ThreadFactory {
+
+        private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
+        private final AtomicInteger counter = new AtomicInteger(0);
+
+
+        // ------------------------------------------ Methods from ThreadFactory
+
+
+        @Override
+        public Thread newThread(Runnable r) {
+
+            final Thread t = defaultFactory.newThread(r);
+            t.setName("glassfish-web-async-thread-" + counter.incrementAndGet());
+            return t;
+
+        }
+
+    } // END AsyncPoolThreadFactory
+
+    private static class PrivilegedSetTccl implements PrivilegedAction<Void> {
+
+        private ClassLoader cl;
+
+        PrivilegedSetTccl(ClassLoader cl) {
+            this.cl = cl;
+        }
+
+        @Override
+        public Void run() {
+            Thread.currentThread().setContextClassLoader(cl);
+            return null;
+        }
+    }
+
+    private static class PrivilegedGetTccl
+            implements PrivilegedAction<ClassLoader> {
+
+        @Override
+        public ClassLoader run() {
+            return Thread.currentThread().getContextClassLoader();
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ClientAbortException.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ClientAbortException.java
new file mode 100644
index 0000000..9bbedfb
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ClientAbortException.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+
+/**
+ * Wrap an IOException identifying it as being caused by an abort
+ * of a request by a remote client.
+ *
+ * @author Glenn L. Nielsen
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:27:28 $
+ */
+
+public final class ClientAbortException extends IOException {
+
+
+    //------------------------------------------------------------ Constructors
+
+
+    /**
+     * Construct a new ClientAbortException with no other information.
+     */
+    public ClientAbortException() {
+        super();
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified message.
+     *
+     * @param message Message describing this exception
+     */
+    public ClientAbortException(String message) {
+        super(message);
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified throwable.
+     *
+     * @param throwable Throwable that caused this exception
+     */
+    public ClientAbortException(Throwable throwable) {
+        super(throwable);
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified message
+     * and throwable.
+     *
+     * @param message Message describing this exception
+     * @param throwable Throwable that caused this exception
+     */
+    public ClientAbortException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Connector.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Connector.java
new file mode 100644
index 0000000..251016d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Connector.java
@@ -0,0 +1,1870 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import java.lang.reflect.Constructor;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.servlet.http.HttpServletRequest;
+
+import com.sun.appserv.ProxyHandler;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Service;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.net.ServerSocketFactory;
+import org.apache.catalina.util.LifecycleSupport;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.web.util.IntrospectionUtils;
+import org.glassfish.grizzly.http.server.util.Mapper;
+
+/**
+ * Implementation of a Coyote connector for Tomcat 5.x.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.23 $ $Date: 2007/07/09 20:46:45 $
+ */
+public class Connector
+    implements org.apache.catalina.Connector, Lifecycle
+{
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ---------------------------------------------- Adapter Configuration --//
+    
+    // START SJSAS 6363251
+    /**
+     * Coyote Adapter class name.
+     * Defaults to the CoyoteAdapter.
+     */
+    private String defaultClassName =
+        "org.apache.catalina.connector.CoyoteAdapter";
+    // END SJSAS 6363251
+
+    
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Holder for our configured properties.
+     */
+    private Map<String, String> properties = new HashMap<String, String>();
+
+    /**
+     * 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 = true;
+
+    /**
+     * The input buffer size we should create on input streams.
+     */
+    private int bufferSize = 4096;
+
+    /**
+     * The Container used for processing requests received by this Connector.
+     */
+    protected Container container = null;
+
+    /**
+     * Compression value.
+     */
+    private String compression = "off";
+
+    /**
+     * 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;
+
+    /**
+     * Maximum size of a HTTP header. 4KB is the default.
+     */
+    private int maxHttpHeaderSize = 4 * 1024;
+
+    /*
+     * Is generation of X-Powered-By response header enabled/disabled?
+     */
+    private boolean xpoweredBy;
+
+    /**
+     * Descriptive information about this Connector implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.connector.Connector/2.0";
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+    /**
+     * The minimum number of processors to start at initialization time.
+     */
+    protected int minProcessors = 5;
+
+    /**
+     * The maximum number of processors allowed, or <0 for unlimited.
+     */
+    private int maxProcessors = 20;
+
+    /**
+     * Linger value on the incoming connection.
+     * Note : a value inferior to 0 means no linger.
+     */
+    private int connectionLinger = Constants.DEFAULT_CONNECTION_LINGER;
+
+    /**
+     * Timeout value on the incoming connection.
+     * Note : a value of 0 means no timeout.
+     */
+    private int connectionTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT;
+
+    /**
+     * Timeout value on the incoming connection during request processing.
+     * Note : a value of 0 means no timeout.
+     */
+    private int connectionUploadTimeout = 
+        Constants.DEFAULT_CONNECTION_UPLOAD_TIMEOUT;
+
+    /**
+     * Timeout value on the server socket.
+     * Note : a value of 0 means no timeout.
+     */
+    private int serverSocketTimeout = Constants.DEFAULT_SERVER_SOCKET_TIMEOUT;
+
+    /**
+     * The port number on which we listen for requests.
+     */
+    private int port = 8080;
+
+    /**
+     * The server name to which we should pretend requests to this Connector
+     * were directed.  This is useful when operating Tomcat behind a proxy
+     * server, so that redirects get constructed accurately.  If not specified,
+     * the server name included in the <code>Host</code> header is used.
+     */
+    private String proxyName = null;
+
+    /**
+     * The server port to which we should 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 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;
+
+    // BEGIN S1AS 5000999
+    /**
+     * The default host.
+     */
+    private String defaultHost;
+    // END S1AS 5000999
+
+    /**
+     * 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;
+    
+    // START SJSAS 6439313     
+    /**
+     * The blocking connection flag that will be set on all requests received
+     * through this connector.
+     */
+    private boolean blocking = false;
+    // END SJSAS 6439313     
+    
+    /** For jk, do tomcat authentication if true, trust server if false 
+     */ 
+    private boolean tomcatAuthentication = 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 = true;
+
+    /**
+     * Maximum number of Keep-Alive requests to honor per connection.
+     */
+    private int maxKeepAliveRequests = 100;
+
+    /**
+     * Maximum size of a POST which will be automatically parsed by the 
+     * container. 2MB by default.
+     */
+    private int maxPostSize = 2 * 1024 * 1024;
+
+    /**
+     * Maximum size of a POST which will be saved by the container
+     * during authentication. 4kB by default
+     */
+    protected int maxSavePostSize = 4 * 1024;
+
+    /**
+     * Has this component been initialized yet?
+     */
+    protected boolean initialized = false;
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+    /**
+     * The shutdown signal to our background thread
+     */
+    private boolean stopped = false;
+
+    /**
+     * The background thread.
+     */
+    private Thread thread = null;
+
+    /**
+     * Use TCP no delay ?
+     */
+    private boolean tcpNoDelay = true;
+
+    /**
+     * Coyote Protocol handler class name.
+     * Defaults to the Coyote HTTP/1.1 protocolHandler.
+     */
+    private String protocolHandlerClassName =
+    	"com.sun.enterprise.web.connector.grizzly.CoyoteConnectorLauncher";
+
+    /**
+     * Coyote protocol handler.
+     */
+    private ProtocolHandler protocolHandler = null;
+
+    private String instanceName;
+
+    /**
+     * The name of this Connector
+     */
+    private String name;
+
+    private HttpHandler handler = null;
+
+    /**
+     * Mapper.
+     */
+    protected Mapper mapper;
+
+    /**
+     * URI encoding.
+     */
+    /* GlassFish Issue 2339
+    private String uriEncoding = null;
+     */
+    // START GlassFish Issue 2339
+    private String uriEncoding = "UTF-8";
+    // END GlassFish Issue 2339
+
+    // START SJSAS 6331392
+    private boolean enabled = true;
+    // END SJSAS 6331392
+
+    // START S1AS 6188932
+    /**
+     * Flag indicating whether this connector is receiving its requests from
+     * a trusted intermediate server
+     */
+    protected boolean authPassthroughEnabled = false;
+
+    protected ProxyHandler proxyHandler = null;
+    // END S1AS 6188932
+
+    /**
+     * The <code>SelectorThread</code> implementation class.
+     */
+    private String selectorThreadImpl = null;
+
+    private String jvmRoute;
+    
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return a configured property.
+     */
+    public String getProperty(String name) {
+        return properties.get(name);
+    }
+
+    /**
+     * Set a configured property.
+     */
+    public void setProperty(String name, String value) {
+        properties.put(name, value);
+    }
+
+    /** 
+     * remove a configured property.
+     */
+    public void removeProperty(String name) {
+        properties.remove(name);
+    }
+
+    /**
+     * Return the <code>Service</code> with which we are associated (if any).
+     */
+    @Override
+    public Service getService() {
+        return service;
+    }
+
+    /**
+     * Set the <code>Service</code> with which we are associated (if any).
+     *
+     * @param service The service that owns this Engine
+     */
+    @Override
+    public void setService(Service service) {
+        this.service = service;
+    }
+
+    /**
+     * 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 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("soLinger", 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("soTimeout", 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("timeout", 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("serverSoTimeout", 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("backlog", String.valueOf(count));
+    }
+
+    /**
+     * Return the bind IP address for this Connector.
+     */
+    public String getAddress() {
+        return 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 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;
+        setProperty("allowTrace", String.valueOf(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 bufferSize;
+    }
+
+    /**
+     * Set the input buffer size for this Connector.
+     *
+     * @param bufferSize The new input buffer size.
+     */
+    public void setBufferSize(int bufferSize) {
+        this.bufferSize = bufferSize;
+        setProperty("bufferSize", String.valueOf(bufferSize));
+    }
+
+    /**
+     * Return the Container used for processing requests received by this
+     * Connector.
+     */
+    @Override
+    public Container getContainer() {
+        return container;
+    }
+
+    /**
+     * Set the Container used for processing requests received by this
+     * Connector.
+     *
+     * @param container The new Container to use
+     */
+    @Override
+    public void setContainer(Container container) {
+        this.container = container;
+    }
+
+    /**
+     * 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.
+     */
+    @Override
+    public boolean getEnableLookups() {
+        return enableLookups;
+    }
+
+    /**
+     * Set the "enable DNS lookups" flag.
+     *
+     * @param enableLookups The new "enable DNS lookups" flag value
+     */
+    @Override
+    public void setEnableLookups(boolean enableLookups) {
+        this.enableLookups = enableLookups;
+        setProperty("enableLookups", String.valueOf(enableLookups));
+    }
+
+    /**
+     * Return the server socket factory used by this Container.
+     */
+    @Override
+    public ServerSocketFactory getFactory() {
+        return factory;
+    }
+
+    /**
+     * Set the server socket factory used by this Container.
+     *
+     * @param factory The new server socket factory
+     */
+    @Override
+    public void setFactory(ServerSocketFactory factory) {
+        this.factory = factory;
+    }
+
+    /**
+     * Return descriptive information about this Connector implementation.
+     */
+    @Override
+    public String getInfo() {
+        return info;
+    }
+
+    /**
+     * Return the mapper.
+     */
+    public Mapper getMapper() {
+        return mapper;
+    }
+     
+    /**
+     * Set the {@link Mapper}.
+     * @param mapper
+     */
+    public void setMapper(Mapper mapper){
+        this.mapper = mapper;
+    }     
+
+    /**
+     * 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("minThreads", 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("maxThreads", String.valueOf(maxProcessors));
+    }
+
+    /**
+     * Return the maximum size of a POST which will be automatically
+     * parsed by the container.
+     */
+    public int getMaxPostSize() {
+        return maxPostSize;
+    }
+
+    /**
+     * Set the maximum size of a POST which will be automatically
+     * parsed by the container.
+     *
+     * @param maxPostSize The new maximum size in bytes of a POST which will 
+     * be automatically parsed by the container
+     */
+    public void setMaxPostSize(int maxPostSize) {
+        this.maxPostSize = maxPostSize;
+        setProperty("maxPostSize", String.valueOf(maxPostSize));
+    }
+
+    /**
+     * Return the maximum size of a POST which will be saved by the container
+     * during authentication.
+     */
+    public int getMaxSavePostSize() {
+
+        return (maxSavePostSize);
+
+    }
+
+    /**
+     * Set the maximum size of a POST which will be saved by the container
+     * during authentication.
+     *
+     * @param maxSavePostSize The new maximum size in bytes of a POST which will
+     * be saved by the container during authentication.
+     */
+    public void setMaxSavePostSize(int maxSavePostSize) {
+
+        this.maxSavePostSize = maxSavePostSize;
+        setProperty("maxSavePostSize", String.valueOf(maxSavePostSize));
+    }
+
+    /**
+     * Return the port number on which we listen for requests.
+     */
+    public int getPort() {
+        return 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));
+    }
+
+    /**
+     * Sets the name of this Connector.
+     */
+    public void setName(String name){
+        this.name = name;
+    }
+    
+    /**
+     * Gets the name of this Connector.
+     */
+    @Override
+    public String getName(){
+        return name;
+    }
+
+    /**
+     * Sets the instance name for this Connector.
+     * 
+     * @param instanceName the instance name
+     */
+    public void setInstanceName(String instanceName) {
+        this.instanceName = instanceName;
+    }
+
+    public String getInstanceName() {
+        return instanceName;
+    }
+
+    /**
+     * Return the Coyote protocol handler in use.
+     */
+    public String getProtocol() {
+        if ("org.glassfish.grizzly.tcp.http11.Http11Protocol".equals
+            (getProtocolHandlerClassName())) {
+            return "HTTP/1.1";
+        } else if ("org.apache.jk.server.JkCoyoteHandler".equals
+                   (getProtocolHandlerClassName())) {
+            return "AJP/1.3";
+        }
+        return null;
+    }
+
+    /**
+     * Set the Coyote protocol which will be used by the connector.
+     *
+     * @param protocol The Coyote protocol name
+     */
+    public void setProtocol(String protocol) {
+        if (protocol.equals("HTTP/1.1")) {
+            setProtocolHandlerClassName
+                ("org.glassfish.grizzly.tcp.http11.Http11Protocol");
+        } else if (protocol.equals("AJP/1.3")) {
+            setProtocolHandlerClassName
+                ("org.apache.jk.server.JkCoyoteHandler");
+        } else {
+            setProtocolHandlerClassName(null);
+        }
+    }
+
+    /**
+     * Return the class name of the Coyote protocol handler in use.
+     */
+    public String getProtocolHandlerClassName() {
+        return 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 protocol handler associated with the connector.
+     */
+    public ProtocolHandler getProtocolHandler() {
+        return protocolHandler;
+    }
+
+    /**
+     * Return the proxy server name for this Connector.
+     */
+    public String getProxyName() {
+        return proxyName;
+    }
+
+    /**
+     * Set the proxy server name for this Connector.
+     *
+     * @param proxyName The new proxy server name
+     */
+    public void setProxyName(String proxyName) {
+        if(proxyName != null && proxyName.length() > 0) {
+            this.proxyName = proxyName;
+            setProperty("proxyName", proxyName);
+        } else {
+            this.proxyName = null;
+            removeProperty("proxyName");
+        }
+    }
+
+    /**
+     * Return the proxy server port for this Connector.
+     */
+    public int getProxyPort() {
+        return proxyPort;
+    }
+
+    /**
+     * Set the proxy server port for this Connector.
+     *
+     * @param proxyPort The new proxy server port
+     */
+    public void setProxyPort(int proxyPort) {
+        this.proxyPort = proxyPort;
+        setProperty("proxyPort", String.valueOf(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.
+     */
+    @Override
+    public int getRedirectPort() {
+        return redirectPort;
+    }
+
+    /**
+     * Set the redirect port number.
+     *
+     * @param redirectPort The redirect port number (non-SSL to SSL)
+     */
+    @Override
+    public void setRedirectPort(int redirectPort) {
+        this.redirectPort = redirectPort;
+        setProperty("redirectPort", String.valueOf(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(isDisabled));
+    }
+
+    /**
+      * Return the maximum HTTP header size.
+      */
+    public int getMaxHttpHeaderSize() {
+      return maxHttpHeaderSize;
+    }
+  
+    /**
+     * Set the maximum HTTP header size.
+     */
+    public void setMaxHttpHeaderSize(int size) {
+        maxHttpHeaderSize = size;
+        setProperty("maxHttpHeaderSize", String.valueOf(size));
+    }
+
+    /**
+     * 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);
+        }
+    }
+
+    /**
+     * 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(mkar));
+    }
+
+    /**
+     * Return the scheme that will be assigned to requests received
+     * through this connector.  Default value is "http".
+     */
+    @Override
+    public String getScheme() {
+        return scheme;
+    }
+
+    /**
+     * Set the scheme that will be assigned to requests received through
+     * this connector.
+     *
+     * @param scheme The new scheme
+     */
+    @Override
+    public void setScheme(String scheme) {
+        this.scheme = scheme;
+        setProperty("scheme", scheme);
+    }
+
+    /**
+     * Return the secure connection flag that will be assigned to requests
+     * received through this connector.  Default value is "false".
+     */
+    @Override
+    public boolean getSecure() {
+        return secure;
+    }
+
+    /**
+     * Set the secure connection flag that will be assigned to requests
+     * received through this connector.
+     *
+     * @param secure The new secure connection flag
+     */
+    @Override
+    public void setSecure(boolean secure) {
+        this.secure = secure;
+        setProperty("secure", String.valueOf(secure));
+    }
+
+    // START SJSAS 6439313     
+    /**
+     * Return the blocking connection flag that will be assigned to requests
+     * received through this connector.  Default value is "false".
+     */
+    public boolean getBlocking() {
+        return blocking;
+    }
+
+    /**
+     * Set the blocking connection flag that will be assigned to requests
+     * received through this connector.
+     *
+     * @param blocking The new blocking connection flag
+     */
+    public void setBlocking(boolean blocking) {
+        this.blocking = blocking;
+        setProperty("blocking", String.valueOf(blocking));
+    }
+    // END SJSAS 6439313     
+    
+    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 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.
+     */
+    @Override
+    public String getURIEncoding() {
+        return uriEncoding;
+    }
+
+    /**
+     * Set the URI encoding to be used for the URI.
+     *
+     * @param uriEncoding The new URI character encoding.
+     */
+    @Override
+    public void setURIEncoding(String uriEncoding) {
+    	if (Charset.isSupported(uriEncoding)) {
+        this.uriEncoding = uriEncoding;
+        setProperty("uRIEncoding", uriEncoding);
+    	} else {
+			if (log.isLoggable(Level.WARNING)) {
+				log.log(Level.WARNING, uriEncoding
+						+ "is not supported .Setting default URLEncoding as "
+						+ this.uriEncoding);
+			}
+		}
+    }
+
+    /**
+     * Indicates whether the generation of an X-Powered-By response header for
+     * servlet-generated responses is enabled or disabled for this Connector.
+     *
+     * @return true if generation of X-Powered-By response header is enabled,
+     * false otherwise
+     */
+    public boolean isXpoweredBy() {
+        return xpoweredBy;
+    }
+
+    /**
+     * Enables or disables the generation of an X-Powered-By header (with value
+     * Servlet/2.4) for all servlet-generated responses returned by this
+     * Connector.
+     *
+     * @param xpoweredBy true if generation of X-Powered-By response header is
+     * to be enabled, false otherwise
+     */
+    public void setXpoweredBy(boolean xpoweredBy) {
+        this.xpoweredBy = xpoweredBy;
+        setProperty("xpoweredBy", String.valueOf(xpoweredBy));
+    }
+
+    // BEGIN S1AS 5000999
+    /**
+     * Sets the default host for this Connector.
+     *
+     * @param defaultHost The default host for this Connector
+     */
+    @Override
+    public void setDefaultHost(String defaultHost) {
+        this.defaultHost = defaultHost;
+    }
+
+    /**
+     * Gets the default host of this Connector.
+     *
+     * @return The default host of this Connector
+     */
+    @Override
+    public String getDefaultHost() {
+        return defaultHost;
+    }
+    // END S1AS 5000999
+
+    // START S1AS 6188932
+    /**
+     * Returns the value of this connector's authPassthroughEnabled flag.
+     *
+     * @return true if this connector is receiving its requests from
+     * a trusted intermediate server, false otherwise
+     */
+    @Override
+    public boolean getAuthPassthroughEnabled() {
+        return authPassthroughEnabled;
+    }
+
+    /**
+     * Sets the value of this connector's authPassthroughEnabled flag.
+     *
+     * @param authPassthroughEnabled true if this connector is receiving its
+     * requests from a trusted intermediate server, false otherwise
+     */
+    @Override
+    public void setAuthPassthroughEnabled(boolean authPassthroughEnabled) {
+        this.authPassthroughEnabled = authPassthroughEnabled;
+    }
+
+    /**
+     * Gets the ProxyHandler instance associated with this CoyoteConnector.
+     * 
+     * @return ProxyHandler instance associated with this CoyoteConnector,
+     * or null
+     */
+    @Override
+    public ProxyHandler getProxyHandler() {
+        return proxyHandler;
+    }
+
+    /**
+     * Sets the ProxyHandler implementation for this CoyoteConnector to use.
+     * 
+     * @param proxyHandler ProxyHandler instance to use
+     */
+    @Override
+    public void setProxyHandler(ProxyHandler proxyHandler) {
+        this.proxyHandler = proxyHandler;
+    }
+
+    // END S1AS 6188932
+
+    // START SJSAS 6331392
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+    // END SJSAS 6331392
+
+    public void setJvmRoute(String jvmRoute) {
+        this.jvmRoute = jvmRoute;
+    }
+
+    public String getJvmRoute() {
+        return jvmRoute;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Create (or allocate) and return a Request object suitable for
+     * specifying the contents of a Request to the responsible Container.
+     */
+    @Override
+    public org.apache.catalina.Request createRequest() {
+        Request request = new Request();
+        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.
+     */
+    @Override
+    public org.apache.catalina.Response createResponse() {
+        Response response = new Response();
+        response.setConnector(this);
+        return response;
+    }
+
+
+    // -------------------------------------------------- Monitoring Methods
+
+    /**
+     * Fires probe event related to the fact that the given request has
+     * been entered the web container.
+     *
+     * @param request the request object
+     * @param host the virtual server to which the request was mapped
+     * @param context the Context to which the request was mapped
+     */
+    public void requestStartEvent(HttpServletRequest request, Host host,
+            Context context) {
+        // Deliberate noop
+    };
+
+    /**
+     * Fires probe event related to the fact that the given request is about
+     * to exit from the web container.
+     *
+     * @param request the request object
+     * @param host the virtual server to which the request was mapped
+     * @param context the Context to which the request was mapped
+     * @param statusCode the response status code
+     */
+    public void requestEndEvent(HttpServletRequest request, Host host,
+            Context context, int statusCode) {
+        // Deliberate noop
+    };
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    @Override
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners
+     * associated with this Connector.
+     */
+    @Override
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    @Override
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+    protected ObjectName createObjectName(String domain, String type)
+            throws MalformedObjectNameException {
+        String encodedAddr = null;
+        if (getAddress() != null) {
+            encodedAddr = URLEncoder.encode(getProperty("address"));
+        }
+        String addSuffix = (getAddress() == null) ? "" : ",address="
+                + encodedAddr;
+        ObjectName _oname = new ObjectName(domain + ":type=" + type + ",port="
+                + getPort() + addSuffix);
+        return _oname;
+    }
+
+    /**
+     * Initialize this connector (create ServerSocket here!)
+     */
+    @Override
+    public void initialize()
+        throws LifecycleException
+    {
+        if (initialized) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.CONNECTOR_BEEN_INIT);
+            }
+            return;
+        }
+
+        this.initialized = true;
+                
+        // If the Mapper is null, do not fail and creates one by default. 
+        if (mapper == null){
+            mapper = new Mapper();
+        }
+        
+        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(domain, "Connector");
+                controller=oname;
+            } catch (Exception e) {
+                log.log(Level.SEVERE, LogFacade.ERROR_REGISTER_CONNECTOR_EXCEPTION, e);
+            }
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Creating name for connector " + oname);
+            }
+        }
+        
+
+        //START SJSAS 6363251
+        // Initializa handler
+        //handler = new CoyoteAdapter(this);
+        //END SJSAS 6363251
+        // Instantiate Adapter
+        //START SJSAS 6363251
+        if ( handler == null){
+            try {
+                Class<?> clazz = Class.forName(defaultClassName);
+                Constructor constructor = 
+                        clazz.getConstructor(new Class<?>[]{Connector.class});
+                handler =
+                        (HttpHandler)constructor.newInstance(new Object[]{this});
+            } catch (Exception e) {
+                throw new LifecycleException
+                    (rb.getString(LogFacade.FAILED_INSTANCIATE_HTTP_HANDLER_EXCEPTION), e);
+            } 
+        }
+        //END SJSAS 6363251
+
+        // Instantiate protocol handler
+        if ( protocolHandler == null ) {
+            try {
+                Class<?> clazz = Class.forName(protocolHandlerClassName);
+
+                // use no-arg constructor for JkCoyoteHandler
+                if (protocolHandlerClassName.equals("org.apache.jk.server.JkCoyoteHandler")) {
+                    protocolHandler = (ProtocolHandler) clazz.newInstance();
+                    if (handler instanceof CoyoteAdapter){
+                        ((CoyoteAdapter) handler).setCompatWithTomcat(true);
+                    } else {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_ADAPTER_IMPLEMENTATION_EXCEPTION),
+                                                          handler);
+                        throw new IllegalStateException
+                          (msg);
+
+                    }
+                // START SJSAS 6439313
+                } else {
+                    Constructor constructor = 
+                            clazz.getConstructor(new Class<?>[]{Boolean.TYPE,
+                                                             Boolean.TYPE,
+                                                             String.class});
+
+                    protocolHandler = (ProtocolHandler) 
+                        constructor.newInstance(secure, blocking,
+                                                selectorThreadImpl);
+                // END SJSAS 6439313
+                }
+            } catch (Exception e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.PROTOCOL_HANDLER_INIT_FAILED_EXCEPTION), e);
+                throw new LifecycleException
+                    (msg);
+            }
+        }
+
+        protocolHandler.setHandler(handler);
+
+        IntrospectionUtils.setProperty(protocolHandler, "jkHome",
+                                       System.getProperty("catalina.base"));
+
+        // Configure secure socket factory
+        // XXX For backwards compatibility only.
+        if (factory instanceof CoyoteServerSocketFactory) {
+            IntrospectionUtils.setProperty(protocolHandler, "secure",
+                                           "" + true);
+            CoyoteServerSocketFactory ssf =
+                (CoyoteServerSocketFactory) factory;
+            IntrospectionUtils.setProperty(protocolHandler, "algorithm",
+                                           ssf.getAlgorithm());
+            if (ssf.getClientAuth()) {
+                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, "protocols",
+                                           ssf.getProtocols());
+            IntrospectionUtils.setProperty(protocolHandler,
+                                           "sSLImplementation",
+                                           ssf.getSSLImplementation());
+            IntrospectionUtils.setProperty(protocolHandler, "ciphers",
+                                           ssf.getCiphers());
+            IntrospectionUtils.setProperty(protocolHandler, "keyAlias",
+                                           ssf.getKeyAlias());
+        } else {
+            IntrospectionUtils.setProperty(protocolHandler, "secure",
+                                           "" + secure);
+        }
+
+        /* Set the configured properties.  This only sets the ones that were
+         * explicitly configured.  Default values are the responsibility of
+         * the protocolHandler.
+         */
+        Iterator<String> keys = properties.keySet().iterator();
+        while( keys.hasNext() ) {
+            String name = keys.next();
+            String value = properties.get(name);
+	    String trnName = translateAttributeName(name);
+            IntrospectionUtils.setProperty(protocolHandler, trnName, value);
+        }
+
+
+        try {
+            protocolHandler.init();
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.PROTOCOL_HANDLER_INIT_FAILED_EXCEPTION), e);
+            throw new LifecycleException
+                (msg);
+        }
+    }
+
+    /*
+     * Translate the attribute name from the legacy Factory names to their
+     * internal protocol names.
+     */
+    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;
+    }
+
+    /**
+     * Begin processing requests via this Connector.
+     *
+     * @exception LifecycleException if a fatal startup error occurs
+     */
+    @Override
+    public void start() throws LifecycleException {
+        if( !initialized )
+            initialize();
+
+        // Validate and update our current state
+        if (started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.CONNECTOR_BEEN_STARTED);
+            }
+            return;
+        }
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        try {
+            protocolHandler.start();
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.PROTOCOL_HANDLER_START_FAILED_EXCEPTION), e);
+            throw new LifecycleException
+                (msg);
+        }
+
+    }
+
+    /**
+     * Terminate processing requests via this Connector.
+     *
+     * @exception LifecycleException if a fatal shutdown error occurs
+     */
+    @Override
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current state
+        if (!started) {
+            log.log(Level.SEVERE, LogFacade.CONNECTOR_NOT_BEEN_STARTED);
+            return;
+
+        }
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        try {
+            protocolHandler.destroy();
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.PROTOCOL_HANDLER_DESTROY_FAILED_EXCEPTION), e);
+            throw new LifecycleException
+                (msg);
+        }
+
+    }
+
+
+    // -------------------- Management methods --------------------
+
+    public boolean getClientAuth() {
+        boolean ret = false;
+
+        String prop = getProperty("clientauth");
+        if (prop != null) {
+            ret = Boolean.valueOf(prop).booleanValue();
+        } else {	
+            ServerSocketFactory factory = this.getFactory();
+            if (factory instanceof CoyoteServerSocketFactory) {
+                ret = ((CoyoteServerSocketFactory)factory).getClientAuth();
+            }
+        }
+
+        return ret;
+    }
+
+    public void setClientAuth(boolean clientAuth) {
+        setProperty("clientauth", String.valueOf(clientAuth));
+        ServerSocketFactory factory = this.getFactory();
+        if (factory instanceof CoyoteServerSocketFactory) {
+            ((CoyoteServerSocketFactory)factory).setClientAuth(clientAuth);
+        }
+    }
+
+    public String getKeystoreFile() {
+        String ret = getProperty("keystore");
+        if (ret == null) {
+            ServerSocketFactory factory = this.getFactory();
+            if (factory instanceof CoyoteServerSocketFactory) {
+                ret = ((CoyoteServerSocketFactory)factory).getKeystoreFile();
+            }
+        }
+
+        return ret;
+    }
+
+    public void setKeystoreFile(String keystoreFile) {
+        setProperty("keystore", keystoreFile);
+        if (factory instanceof CoyoteServerSocketFactory) {
+            ((CoyoteServerSocketFactory)factory).setKeystoreFile(keystoreFile);
+        }
+    }
+
+    /**
+     * Return keystorePass
+     */
+    public String getKeystorePass() {
+        String ret = getProperty("keypass");
+        if (ret == null) {
+            if (factory instanceof CoyoteServerSocketFactory ) {
+                return ((CoyoteServerSocketFactory)factory).getKeystorePass();
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Set keystorePass
+     */
+    public void setKeystorePass(String keystorePass) {
+        setProperty("keypass", keystorePass);
+        ServerSocketFactory factory = getFactory();
+        if( factory instanceof CoyoteServerSocketFactory ) {
+            ((CoyoteServerSocketFactory)factory).setKeystorePass(keystorePass);
+        }
+    }
+
+    /**
+     * Gets the list of SSL cipher suites that are to be enabled
+     *
+     * @return Comma-separated list of SSL cipher suites, or null if all
+     * cipher suites supported by the underlying SSL implementation are being
+     * enabled
+     */
+    public String getCiphers() {
+        String ret = getProperty("ciphers");
+        if (ret == null) {
+            ServerSocketFactory factory = getFactory();
+            if (factory instanceof CoyoteServerSocketFactory) {
+                ret = ((CoyoteServerSocketFactory)factory).getCiphers();
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Sets the SSL cipher suites that are to be enabled.
+     *
+     * Only those SSL cipher suites that are actually supported by
+     * the underlying SSL implementation will be enabled.
+     *
+     * @param ciphers Comma-separated list of SSL cipher suites
+     */
+    public void setCiphers(String ciphers) {
+        setProperty("ciphers", ciphers);
+        ServerSocketFactory factory = getFactory();
+        if (factory instanceof CoyoteServerSocketFactory) {
+            ((CoyoteServerSocketFactory)factory).setCiphers(ciphers);
+        }
+    }
+
+    /**
+     * Sets the number of seconds after which SSL sessions expire and are
+     * removed from the SSL sessions cache.
+     */
+    public void setSslSessionTimeout(String timeout) {
+        setProperty("sslSessionTimeout", timeout);
+    }
+
+    public String getSslSessionTimeout() {
+        return getProperty("sslSessionTimeout");
+    }
+
+    /**
+     * Sets the number of seconds after which SSL3 sessions expire and are
+     * removed from the SSL sessions cache.
+     */
+    public void setSsl3SessionTimeout(String timeout) {
+        setProperty("ssl3SessionTimeout", timeout);
+    }
+
+    public String getSsl3SessionTimeout() {
+        return getProperty("ssl3SessionTimeout");
+    }
+
+    /**
+     * Sets the number of SSL sessions that may be cached
+     */
+    public void setSslSessionCacheSize(String cacheSize) {
+        setProperty("sslSessionCacheSize", cacheSize);
+    }
+
+    public String getSslSessionCacheSize() {
+        return getProperty("sslSessionCacheSize");
+    }
+
+    /**
+     * Gets the alias name of the keypair and supporting certificate chain
+     * used by this Connector to authenticate itself to SSL clients.
+     *
+     * @return The alias name of the keypair and supporting certificate chain
+     */
+    public String getKeyAlias() {
+        String ret = getProperty("keyAlias");
+        if (ret == null) {
+            ServerSocketFactory factory = getFactory();
+            if (factory instanceof CoyoteServerSocketFactory) {
+                ret = ((CoyoteServerSocketFactory)factory).getKeyAlias();
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Sets the alias name of the keypair and supporting certificate chain
+     * used by this Connector to authenticate itself to SSL clients.
+     *
+     * @param alias The alias name of the keypair and supporting certificate
+     * chain
+     */
+    public void setKeyAlias(String alias) {
+        setProperty("keyAlias", alias);
+        ServerSocketFactory factory = getFactory();
+        if (factory instanceof CoyoteServerSocketFactory) {
+            ((CoyoteServerSocketFactory)factory).setKeyAlias(alias);
+        }
+    }
+
+    /**
+     * Gets the SSL protocol variant to be used.
+     *
+     * @return SSL protocol variant
+     */
+    public String getSslProtocol() {
+        String ret = getProperty("sslProtocol");
+        if (ret == null) {
+            ServerSocketFactory factory = getFactory();
+            if (factory instanceof CoyoteServerSocketFactory) {
+                ret = ((CoyoteServerSocketFactory)factory).getProtocol();
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Sets the SSL protocol variant to be used.
+     *
+     * @param sslProtocol SSL protocol variant
+     */
+    public void setSslProtocol(String sslProtocol) {
+        setProperty("sslProtocol", sslProtocol);
+        ServerSocketFactory factory = getFactory();
+        if (factory instanceof CoyoteServerSocketFactory) {
+            ((CoyoteServerSocketFactory)factory).setProtocol(sslProtocol);
+        }
+    }
+
+    /**
+     * Gets the SSL protocol variants to be enabled.
+     *
+     * @return Comma-separated list of SSL protocol variants
+     */
+    public String getSslProtocols() {
+        String ret = getProperty("sslProtocols");
+        if (ret == null) {
+            ServerSocketFactory factory = getFactory();
+            if (factory instanceof CoyoteServerSocketFactory) {
+                ret = ((CoyoteServerSocketFactory)factory).getProtocols();
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Sets the SSL protocol variants to be enabled.
+     *
+     * @param sslProtocols Comma-separated list of SSL protocol variants
+     */
+    public void setSslProtocols(String sslProtocols) {
+        setProperty("sslProtocols", sslProtocols);
+        ServerSocketFactory factory = getFactory();
+        if (factory instanceof CoyoteServerSocketFactory) {
+            ((CoyoteServerSocketFactory)factory).setProtocols(sslProtocols);
+        }
+    }
+    
+    // START OF SJSAS 8.1 PE 6191830
+    /**
+     * Get the underlying WebContainer certificate for the request
+     */
+    @Override
+    public X509Certificate[] getCertificates(org.apache.catalina.Request request) {
+        
+        Request cRequest = null;
+        if (request instanceof Request) {
+            cRequest=(Request) request;
+        } else {
+            return null;
+        }
+        
+        X509Certificate certs[] = (X509Certificate[])
+        cRequest.getAttribute(Globals.CERTIFICATES_ATTR);
+        if ((certs == null) || (certs.length < 1)) {
+            certs = (X509Certificate[])
+            cRequest.getAttribute(Globals.SSL_CERTIFICATE_ATTR);
+        }
+        return certs;
+    }    
+    // END OF SJSAS 8.1 PE 6191830
+
+
+    // -------------------- JMX registration  --------------------
+
+    protected String domain;
+    protected ObjectName oname;
+    ObjectName controller;
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    /**
+     * Set the domain of this object.
+     */
+    public void setDomain(String domain){
+        this.domain = domain;
+    }
+
+    public void init() throws Exception {
+
+        if( this.getService() != null ) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Already configured");
+            }
+            return;
+        }
+    }
+
+    public void destroy() throws Exception {
+        if( oname!=null && controller==oname ) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Unregister itself " + oname );
+            }
+        }
+        if( getService() == null)
+            return;
+        getService().removeConnector(this);
+    }
+
+    // START SJSAS 6363251
+    /**
+     * Set the <code>Adapter</code> used by this connector.
+     */
+    @Override
+    public void setHandler(HttpHandler handler){
+        this.handler = handler;
+    }
+    
+    /**
+     * Get the <code>Adapter</code> used by this connector.
+     */    
+    @Override
+    public HttpHandler getHandler(){
+        return handler;
+    }
+ 
+    /**
+     * Set the <code>ProtocolHandler</code> used by this connector.
+     */
+    public void setProtocolHandler(ProtocolHandler protocolHandler){
+        this.protocolHandler = protocolHandler;
+    }
+    // END SJSAS 6363251
+
+    /**
+     * Get the underlying <code>SelectorThread</code> implementation, null if 
+     * the default is used.
+     */
+    public String getSelectorThreadImpl() {
+        return selectorThreadImpl;
+    }
+
+    /**
+     * Set the underlying <code>SelectorThread</code> implementation  
+     */   
+    public void setSelectorThreadImpl(String selectorThreadImpl) {
+        this.selectorThreadImpl = selectorThreadImpl;
+    } 
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Constants.java
new file mode 100644
index 0000000..d6bf800
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Constants.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+
+/**
+ * Static constants for this package.
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.connector";
+
+    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";
+
+
+    // S1AS 4703023
+    public static final int DEFAULT_MAX_DISPATCH_DEPTH = 20;
+
+    public final static String PROXY_AUTH_CERT = "Proxy-auth-cert";
+    public final static String PROXY_IP = "Proxy-ip";
+    public final static String PROXY_KEYSIZE = "Proxy-keysize";
+
+    // START SJSAS 6337561
+    public final static String PROXY_JROUTE = "proxy-jroute";
+    // END SJSAS 6337561
+
+    // START SJSAS 6346226
+    public final static String JROUTE_COOKIE = "JROUTE";
+    // END SJSAS 6346226
+
+    /**
+     * If true, custom HTTP status messages will be used in headers.
+     */
+    // In Tomcat, the following constant is in org.apache.coyote.Constants with default true.
+    public static final boolean USE_CUSTOM_STATUS_MSG_IN_HEADER =
+        Boolean.valueOf(System.getProperty(
+                "org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER",
+                "true")).booleanValue(); 
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteAdapter.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteAdapter.java
new file mode 100644
index 0000000..bd800b4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -0,0 +1,997 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import java.nio.charset.Charset;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.sun.appserv.ProxyHandler;
+import java.io.CharConversionException;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.ResponseUtil;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.glassfish.common.util.InputValidationUtil;
+import org.glassfish.grizzly.http.Method;
+import org.glassfish.grizzly.http.server.AfterServiceListener;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.Note;
+import org.glassfish.grizzly.http.server.util.MappingData;
+import org.glassfish.grizzly.http.util.ByteChunk;
+import org.glassfish.grizzly.http.util.CharChunk;
+import org.glassfish.grizzly.http.util.DataChunk;
+import org.glassfish.grizzly.http.util.MessageBytes;
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.web.valve.GlassFishValve;
+import org.glassfish.web.valve.ServletContainerInterceptor;
+
+/**
+ * Implementation of a request processor which delegates the processing to a
+ * Coyote processor.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.34 $ $Date: 2007/08/24 18:38:28 $
+ */
+
+public class CoyoteAdapter extends HttpHandler {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // -------------------------------------------------------------- Constants
+    private static final String POWERED_BY = "Servlet/3.1 JSP/2.3 " +
+            "(" + ServerInfo.getServerInfo() + " Java/" +
+            System.getProperty("java.vm.vendor") + "/" +
+            System.getProperty("java.specification.version") + ")";
+
+
+//    protected boolean v3Enabled =
+//        Boolean.valueOf(System.getProperty("v3.grizzly.useMapper", "true"));
+    
+    
+//    public static final int ADAPTER_NOTES = 1;
+
+    static final String JVM_ROUTE = System.getProperty("jvmRoute");
+
+    private Collection<ServletContainerInterceptor> interceptors = null;
+
+    protected static final boolean ALLOW_BACKSLASH =
+        Boolean.valueOf(System.getProperty("org.glassfish.grizzly.tcp.tomcat5.CoyoteAdapter.ALLOW_BACKSLASH", "false"));
+
+    private static final boolean COLLAPSE_ADJACENT_SLASHES =
+        Boolean.valueOf(System.getProperty(
+            "com.sun.enterprise.web.collapseAdjacentSlashes", "true"));
+
+    /**
+     * When mod_jk is used, the adapter must be invoked the same way 
+     * Tomcat does by invoking service(...) and the afterService(...). This
+     * is a hack to make it compatible with Tomcat 5|6.
+     */
+    private boolean compatWithTomcat = false;
+    
+    private String serverName = ServerInfo.getPublicServerInfo();
+    
+    // Make sure this value is always aligned with {@link ContainerMapper}
+    // (@see com.sun.enterprise.v3.service.impl.ContainerMapper)
+    protected final static Note<MappingData> MAPPING_DATA =
+            org.glassfish.grizzly.http.server.Request.<MappingData>createNote("MappingData");
+
+    static final Note<Request> CATALINA_REQUEST_NOTE =
+            org.glassfish.grizzly.http.server.Request.createNote(Request.class.getName());
+    static final Note<Response> CATALINA_RESPONSE_NOTE =
+            org.glassfish.grizzly.http.server.Request.createNote(Response.class.getName());
+
+    static final CatalinaAfterServiceListener catalinaAfterServiceListener =
+            new CatalinaAfterServiceListener();
+
+    // Make sure this value is always aligned with {@link ContainerMapper}
+    // (@see com.sun.enterprise.v3.service.impl.ContainerMapper)
+    private final static Note<DataChunk> DATA_CHUNK =
+            org.glassfish.grizzly.http.server.Request.<DataChunk>createNote("DataChunk");
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new CoyoteProcessor associated with the specified connector.
+     *
+     * @param connector CoyoteConnector that owns this processor
+     */
+    public CoyoteAdapter(Connector connector) {
+        super();
+        this.connector = connector;
+        initServletInterceptors();
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The CoyoteConnector with which this processor is associated.
+     */
+    private Connector connector = null;
+
+
+    // -------------------------------------------------------- Adapter Methods
+
+
+    /**
+     * Service method.
+     */
+    @Override
+    public void service(org.glassfish.grizzly.http.server.Request req,
+                        org.glassfish.grizzly.http.server.Response res)
+            throws Exception {
+
+        res.getResponse().setAllowCustomReasonPhrase(Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER);
+
+        Request request = req.getNote(CATALINA_REQUEST_NOTE);
+        Response response = req.getNote(CATALINA_RESPONSE_NOTE);
+        
+        // Grizzly already parsed, decoded, and mapped the request.
+        // Let's re-use this info here, before firing the
+        // requestStartEvent probe, so that the mapping data will be
+        // available to any probe event listener via standard
+        // ServletRequest APIs (such as getContextPath())
+        MappingData md = req.getNote(MAPPING_DATA);
+        final boolean v3Enabled = md != null;
+        if (request == null) {
+
+            // Create objects
+            request = (Request) connector.createRequest();
+            response = (Response) connector.createResponse();
+
+            // Link objects
+            request.setResponse(response);
+            response.setRequest(request);
+
+            // Set as notes
+            req.setNote(CATALINA_REQUEST_NOTE, request);
+            req.setNote(CATALINA_RESPONSE_NOTE, response);
+//            res.setNote(ADAPTER_NOTES, response);
+
+            // Set query string encoding
+            req.getRequest().getRequestURIRef().setDefaultURIEncoding(Charset.forName(connector.getURIEncoding()));
+        }
+
+        request.setCoyoteRequest(req);
+        response.setCoyoteResponse(res);
+
+        if (v3Enabled && !compatWithTomcat) {
+            request.setMappingData(md);
+            request.updatePaths(md);
+        }
+
+        req.addAfterServiceListener(catalinaAfterServiceListener);
+        
+        try {
+            doService(req, request, res, response, v3Enabled);
+
+            // Request may want to initialize async processing
+            request.onExitService();
+        } catch (Throwable t) {
+            log.log(Level.SEVERE, LogFacade.REQUEST_PROCESSING_EXCEPTION, t);
+        }
+    }
+
+    private void enteringServletContainer(Request req, Response res) {
+        if (interceptors == null)
+            return;
+        for(ServletContainerInterceptor interceptor:interceptors) {
+            try{
+                interceptor.preInvoke(req, res);
+            } catch (Throwable th) {
+                log.log(Level.SEVERE, LogFacade.INTERNAL_ERROR, th);
+            }
+        }
+    }
+
+    private void leavingServletContainer(Request req, Response res) {
+        if (interceptors == null)
+            return;
+        for(ServletContainerInterceptor interceptor:interceptors) {
+            try{
+                interceptor.postInvoke(req, res);
+            } catch (Throwable th) {
+                log.log(Level.SEVERE, LogFacade.INTERNAL_ERROR, th);
+            }
+        }
+    }
+
+    private void initServletInterceptors() {
+        try {
+            ServiceLocator services = org.glassfish.internal.api.Globals.getDefaultHabitat();
+            interceptors = services.getAllServices(ServletContainerInterceptor.class);
+        } catch (Throwable th) {
+            log.log(Level.SEVERE, LogFacade.FAILED_TO_INITIALIZE_THE_INTERCEPTOR, th);
+        }
+    }
+    
+
+    private void doService(final org.glassfish.grizzly.http.server.Request req,
+                           final Request request,
+                           final org.glassfish.grizzly.http.server.Response res,
+                           final Response response,
+                           final boolean v3Enabled)
+            throws Exception {
+        
+        // START SJSAS 6331392
+        // Check connector for disabled state
+        if (!connector.isEnabled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.HTTP_LISTENER_DISABLED),
+                                              String.valueOf(connector.getPort()));
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, msg);
+            }
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
+            return;
+        }
+        // END SJSAS 6331392
+
+////            "X-Powered-By" header is set by GlassfishHttpCodecFilter
+//        if (connector.isXpoweredBy()) {
+//            response.addHeader("X-Powered-By", POWERED_BY);
+//        }
+
+
+        // Parse and set Catalina and configuration specific 
+        // request parameters
+        if ( postParseRequest(req, request, res, response, v3Enabled) ) {
+
+            // START S1AS 6188932
+            boolean authPassthroughEnabled = 
+                connector.getAuthPassthroughEnabled();
+            ProxyHandler proxyHandler = connector.getProxyHandler();
+            if (authPassthroughEnabled && proxyHandler != null) {
+
+                // START SJSAS 6397218
+                if (proxyHandler.getSSLKeysize(
+                        (HttpServletRequest)request.getRequest()) > 0) {
+                    request.setSecure(true);
+                }
+                // END SJSAS 6397218
+
+                X509Certificate[] certs = null;
+                try {
+                    certs = proxyHandler.getSSLClientCertificateChain(
+                                request.getRequest());
+                } catch (CertificateException ce) {
+                    log.log(Level.SEVERE, LogFacade.PARSING_CLIENT_CERT_EXCEPTION,
+                            ce);
+                }
+                if (certs != null) {
+                    request.setAttribute(Globals.CERTIFICATES_ATTR,
+                                         certs);
+                }
+                    
+            }
+            // END S1AS 6188932
+
+////            "Server" header is set by GlassfishHttpCodecFilter
+//            if (serverName != null && !serverName.isEmpty()) {
+//                response.addHeader("Server", serverName);
+//            }
+                
+            // Invoke the web container
+            connector.requestStartEvent(request.getRequest(),
+                request.getHost(), request.getContext());
+            Container container = connector.getContainer();
+            enteringServletContainer(request, response);
+            try {
+                request.lockSession();
+                if (container.getPipeline().hasNonBasicValves() ||
+                        container.hasCustomPipeline()) {
+                    container.getPipeline().invoke(request, response);
+                } else {
+                    // Invoke host directly
+                    Host host = request.getHost();
+                    if (host == null) {
+                        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+
+                        String msg = MessageFormat.format(rb.getString(LogFacade.NO_HOST_MATCHES_SERVER_NAME_INFO),
+                                                          request.getRequest().getServerName());
+                        response.setDetailMessage(msg);
+                        return;
+                    }
+                    if (host.getPipeline().hasNonBasicValves() ||
+                            host.hasCustomPipeline()) {
+                        host.getPipeline().invoke(request, response);
+                    } else {
+                        GlassFishValve hostValve = host.getPipeline().getBasic();
+                        hostValve.invoke(request, response);
+                        // Error handling
+                        hostValve.postInvoke(request, response); 
+                    }
+                }
+            } finally {
+                try {
+                    connector.requestEndEvent(request.getRequest(),
+                        request.getHost(), request.getContext(),
+                        response.getStatus());
+                } finally {
+                    leavingServletContainer(request, response);
+                }
+            }
+        }
+
+    }
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parse additional request parameters.
+     */
+    protected boolean postParseRequest(final org.glassfish.grizzly.http.server.Request req,
+                                       final Request request,
+                                       final org.glassfish.grizzly.http.server.Response res,
+                                       final Response response,
+                                       final boolean v3Enabled)
+        throws Exception {
+        // 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
+        request.setSecure(req.isSecure());
+
+        // URI decoding
+        DataChunk decodedURI;
+        try {
+            decodedURI = req.getRequest().getRequestURIRef().getDecodedRequestURIBC();
+        } catch (CharConversionException cce) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid URI");
+            return false;
+        }
+        
+        if (compatWithTomcat || !v3Enabled) {
+//            decodedURI.duplicate(req.requestURI());
+//            try {
+//              req.getURLDecoder().convert(decodedURI, false);
+//            } catch (IOException ioe) {
+//              res.setStatus(400);
+//              res.setMessage("Invalid URI: " + ioe.getMessage());
+//              return false;
+//            }
+
+            /* GlassFish Issue 2339
+            // Normalize decoded URI
+            if (!normalize(req.decodedURI())) {
+                res.setStatus(400);
+                res.setMessage("Invalid URI");
+                return false;
+            }
+            */
+
+            // Set the remote principal
+            String principal = req.getRemoteUser();
+            if (principal != null) {
+                request.setUserPrincipal(new CoyotePrincipal(principal));
+            }
+
+            // Set the authorization type
+            String authtype = req.getAuthType();
+            if (authtype != null) {
+                request.setAuthType(authtype);
+            }
+
+            /* CR 6309511
+            // URI character decoding
+            convertURI(decodedURI, request);
+
+            // Parse session Id
+            parseSessionId(req, request);
+             */
+            // START CR 6309511
+//             URI character decoding
+//            request.convertURI(decodedURI);
+
+            // START GlassFish Issue 2339
+            // Normalize decoded URI
+//            if (!normalize(decodedURI)) {
+//                res.setStatus(400);
+//                res.setMessage("Invalid URI");
+//                return false;
+//            }
+            // END GlassFish Issue 2339
+        }
+        // END CR 6309511
+
+        /*
+         * Remove any parameters from the URI, so they won't be considered
+         * by the mapping algorithm, and save them in a temporary CharChunk,
+         * so that any session id param may be parsed once the target
+         * context, which may use a custom session parameter name, has been
+         * identified
+         */
+        final CharChunk uriParamsCC = request.getURIParams();
+        final CharChunk uriCC = decodedURI.getCharChunk();
+        final int semicolon = uriCC.indexOf(';');
+        if (semicolon > 0) {
+            final int absSemicolon = uriCC.getStart() + semicolon;
+            uriParamsCC.setChars(uriCC.getBuffer(), absSemicolon,
+                uriCC.getEnd() - absSemicolon);
+            decodedURI.setChars(uriCC.getBuffer(), uriCC.getStart(),
+                absSemicolon - uriCC.getStart());
+        }
+ 
+        if (compatWithTomcat || !v3Enabled) {
+            /*mod_jk*/
+            DataChunk localDecodedURI = decodedURI;
+            if (semicolon > 0) {
+                localDecodedURI = req.getNote(DATA_CHUNK);
+                if (localDecodedURI == null) {
+                    localDecodedURI = DataChunk.newInstance();
+                    req.setNote(DATA_CHUNK, localDecodedURI);
+                }
+                localDecodedURI.duplicate(decodedURI);
+            }
+            connector.getMapper().map(req.getRequest().serverName(), localDecodedURI,
+                                  request.getMappingData());
+            MappingData md = request.getMappingData();
+            req.setNote(MAPPING_DATA, md);
+            request.updatePaths(md);
+        }
+
+        // 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) {
+            req.setServerPort(proxyPort);
+        }
+        if (proxyName != null) {
+            req.setServerName(proxyName);
+        }
+
+        Context ctx = (Context) request.getMappingData().context;
+
+        // Parse session id
+        if (ctx != null) {
+            if (req.isRequestedSessionIdFromURL() &&
+                    Globals.SESSION_PARAMETER_NAME.equals(ctx.getSessionParameterName())) {
+                request.obtainSessionId();
+            } else if (!uriParamsCC.isNull()) {
+//            String sessionParam = ";" + ctx.getSessionParameterName() + "=";
+                request.parseSessionId(ctx.getSessionParameterName(), uriParamsCC);
+            }
+        }
+
+        // START GlassFish 1024
+        request.setDefaultContext(request.getMappingData().isDefaultContext);
+        // END GlassFish 1024
+
+        // START SJSAS 6253524
+        // request.setContext((Context) request.getMappingData().context);
+        // END SJSAS 6253524
+        // START SJSAS 6253524
+        request.setContext(ctx);
+        // END SJSAS 6253524
+
+        if (ctx != null && !uriParamsCC.isNull()) {
+            request.parseSessionVersion(uriParamsCC);
+        }
+
+        if (!uriParamsCC.isNull()) {
+            request.parseJReplica(uriParamsCC);
+        }
+
+        request.setWrapper((Wrapper) request.getMappingData().wrapper);
+
+        // Filter trace method
+        if (!connector.getAllowTrace() && Method.TRACE.equals(req.getMethod())) {
+            Wrapper wrapper = request.getWrapper();
+            String header = null;
+            if (wrapper != null) {
+                String[] methods = wrapper.getServletMethods();
+                if (methods != null) {
+                    for (String method : methods) {
+                        // Exclude TRACE from methods returned in Allow header
+                        if ("TRACE".equals(method)) {
+                            continue;
+                        }
+                        if (header == null) {
+                            header = method;
+                        } else {
+                            header += ", " + method;
+                        }
+                    }
+                }
+            }                               
+            res.setStatus(405, "TRACE method is not allowed");
+            res.addHeader("Allow", header);
+            return false;
+        }
+
+        // Possible redirect
+        DataChunk redirectPathMB = request.getMappingData().redirectPath;
+        // START SJSAS 6253524
+        // if (!redirectPathMB.isNull()) {
+        // END SJSAS 6253524
+        // START SJSAS 6253524
+        if (!redirectPathMB.isNull()
+            && (!ctx.hasAdHocPaths()
+                || (ctx.getAdHocServletName(((HttpServletRequest)
+                        request.getRequest()).getServletPath()) == null))) {
+        // END SJSAS 6253524
+            String redirectPath = redirectPathMB.toString();
+            String query = request.getQueryString();
+            if (request.isRequestedSessionIdFromURL()) {
+                // This is not optimal, but as this is not very common, it
+                // shouldn't matter
+                redirectPath = redirectPath + ";" + ctx.getSessionParameterName() + "=" 
+                    + request.getRequestedSessionId();
+            }            
+            // START GlassFish 936
+            redirectPath = response.encode(redirectPath);
+            // END GlassFish 936
+            if (query != null) {
+                // This is not optimal, but as this is not very common, it
+                // shouldn't matter
+                redirectPath = redirectPath + "?" + query;
+            }
+
+            // START CR 6590921
+            boolean authPassthroughEnabled = 
+                connector.getAuthPassthroughEnabled();
+            ProxyHandler proxyHandler = connector.getProxyHandler();
+            if (authPassthroughEnabled && proxyHandler != null) {
+
+                if (proxyHandler.getSSLKeysize(
+                        (HttpServletRequest)request.getRequest()) > 0) {
+                    request.setSecure(true);
+                }
+            }
+            // END CR 6590921
+            // Issue a permanent redirect
+            // Validating the redirectPath for header injection
+            if (InputValidationUtil.validateStringforCRLF(redirectPath)) {
+                response.sendError(403, "Forbidden");
+            } else {
+                response.sendRedirect(InputValidationUtil.removeLinearWhiteSpaces(redirectPath),
+                        false);
+            }
+
+            return false;
+        }
+
+        // Parse session Id
+        /* CR 6309511
+        parseSessionCookiesId(req, request);
+         */
+        // START CR 6309511
+        request.parseSessionCookiesId();
+        // END CR 6309511
+
+        // START SJSAS 6346226
+        request.parseJrouteCookie();
+        // END SJSAS 6346226
+
+        return true;
+    }
+
+
+    /**
+     * 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) {
+
+        int type = uriMB.getType();
+        if (type == MessageBytes.T_CHARS) {
+            return normalizeChars(uriMB);
+        } else {
+            return normalizeBytes(uriMB);
+        }
+    }
+
+
+    private static boolean normalizeBytes(MessageBytes uriMB) {
+
+        ByteChunk uriBC = uriMB.getByteChunk();
+        byte[] b = uriBC.getBytes();
+        int start = uriBC.getStart();
+        int end = uriBC.getEnd();
+
+        // An empty URL is not acceptable
+        if (start == end)
+            return false;
+
+        // 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 "/"
+        if (COLLAPSE_ADJACENT_SLASHES) {
+            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;
+
+    }
+
+
+    private static boolean normalizeChars(MessageBytes uriMB) {
+
+        CharChunk uriCC = uriMB.getCharChunk();
+        char[] c = uriCC.getChars();
+        int start = uriCC.getStart();
+        int end = uriCC.getEnd();
+
+        // URL * is acceptable
+        if ((end - start == 1) && c[start] == (char) '*')
+          return true;
+
+        int pos = 0;
+        int index = 0;
+
+        // Replace '\' with '/'
+        // Check for null char
+        for (pos = start; pos < end; pos++) {
+            if (c[pos] == (char) '\\') {
+                if (ALLOW_BACKSLASH) {
+                    c[pos] = (char) '/';
+                } else {
+                    return false;
+                }
+            }
+            if (c[pos] == (char) 0) {
+                return false;
+            }
+        }
+
+        // The URL must start with '/'
+        if (c[start] != (char) '/') {
+            return false;
+        }
+
+        // Replace "//" with "/"
+        if (COLLAPSE_ADJACENT_SLASHES) {
+            for (pos = start; pos < (end - 1); pos++) {
+                if (c[pos] == (char) '/') {
+                    while ((pos + 1 < end) && (c[pos + 1] == (char) '/')) {
+                        copyChars(c, 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) && (c[end - 1] == (char) '.')) {
+            if ((c[end - 2] == (char) '/') 
+                || ((c[end - 2] == (char) '.') 
+                    && (c[end - 3] == (char) '/'))) {
+                c[end] = (char) '/';
+                end++;
+            }
+        }
+
+        uriCC.setEnd(end);
+
+        index = 0;
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            index = uriCC.indexOf("/./", 0, 3, index);
+            if (index < 0)
+                break;
+            copyChars(c, start + index, start + index + 2, 
+                      end - start - index - 2);
+            end = end - 2;
+            uriCC.setEnd(end);
+        }
+
+        index = 0;
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            index = uriCC.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 (c[pos] == (char) '/') {
+                    index2 = pos;
+                }
+            }
+            copyChars(c, start + index2, start + index + 3,
+                      end - start - index - 3);
+            end = end + index2 - index - 3;
+            uriCC.setEnd(end);
+            index = index2;
+        }
+
+        uriCC.setChars(c, 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];
+        }
+    }
+
+
+    /**
+     * Copy an array of chars to a different position. Used during 
+     * normalization.
+     */
+    private static void copyChars(char[] c, int dest, int src, int len) {
+        for (int pos = 0; pos < len; pos++) {
+            c[pos + dest] = c[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) {
+        log.log(Level.INFO, 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) {
+        log.log(Level.SEVERE, message, throwable);
+    }
+
+
+     /**
+      * Character conversion of the a US-ASCII MessageBytes.
+      */
+    /* CR 6309511
+     protected void convertMB(MessageBytes mb) {
+ 
+        // This is of course only meaningful for bytes
+        if (mb.getType() != MessageBytes.T_BYTES)
+            return;
+        
+        ByteChunk bc = mb.getByteChunk();
+        CharChunk cc = mb.getCharChunk();
+        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);
+        }
+        mb.setChars(cbuf, 0, bc.getLength());
+   
+     }
+     */
+
+    
+    // START SJSAS 6349248
+    /**
+     * Notify all container event listeners that a particular event has
+     * occurred for this Adapter.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param data Event data
+     */
+    public void fireAdapterEvent(String type, Object data) {
+        if ( connector != null && connector.getContainer() != null) {
+            try{
+                ((ContainerBase)connector.getContainer())
+                    .fireContainerEvent(type,data);
+            } catch (Throwable t){
+                log.log(Level.SEVERE, LogFacade.REQUEST_PROCESSING_EXCEPTION, t);
+            }
+        }
+    }
+    // END SJSAS 6349248
+
+    
+    /**
+     * Return true when an instance is executed the same way it does in Tomcat.
+     */
+    public boolean isCompatWithTomcat() {
+        return compatWithTomcat;
+    }
+
+    
+    /**
+     * <tt>true</tt> if this class needs to be compatible with Tomcat
+     * Adapter class. Since Tomcat Adapter implementation doesn't support 
+     * the afterService method, the afterService method must be invoked 
+     * inside the service method.
+     */
+    public void setCompatWithTomcat(boolean compatWithTomcat) {
+        this.compatWithTomcat = compatWithTomcat;
+
+        // Add server header
+        if (compatWithTomcat){
+            serverName = "Apache/" + serverName;
+        } else {
+            // Recalculate.
+            serverName = ServerInfo.getPublicServerInfo();
+        }
+    }
+
+
+    /**
+     * Gets the port of this CoyoteAdapter.
+     *
+     * @return the port of this CoyoteAdapter
+     */
+    public int getPort() {
+        return connector.getPort();
+    }
+
+    /**
+     * AfterServiceListener, which is responsible for recycle catalina request and response
+     * objects.
+     */
+    static final class CatalinaAfterServiceListener implements AfterServiceListener {
+
+        @Override
+        public void onAfterService(final org.glassfish.grizzly.http.server.Request request) {
+            final Request servletRequest = request.getNote(CATALINA_REQUEST_NOTE);
+            final Response servletResponse = request.getNote(CATALINA_RESPONSE_NOTE);
+
+            if (servletRequest != null) {
+                try {
+                    if (!servletRequest.isUpgrade()) {
+                        servletResponse.finishResponse();
+                    } else {
+                        servletResponse.setUpgrade(servletRequest.isUpgrade());
+                    }
+                } catch (Exception e) {
+                    log.log(Level.SEVERE, LogFacade.REQUEST_PROCESSING_EXCEPTION, e);
+                } finally {
+                    try {
+                        servletRequest.unlockSession();
+                    } finally {
+                        servletRequest.recycle();
+                        servletResponse.recycle();
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteInputStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteInputStream.java
new file mode 100644
index 0000000..9280e26
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteInputStream.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.security.SecurityUtil;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ResourceBundle;
+
+
+/**
+ * This class handles reading bytes.
+ * 
+ * @author Remy Maucherat
+ * @author Jean-Francois Arcand
+ */
+public class CoyoteInputStream
+    extends ServletInputStream {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected InputBuffer ib;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyoteInputStream(InputBuffer ib) {
+        this.ib = ib;
+    }
+    
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+    * Prevent cloning the facade.
+    */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+    
+    
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear facade.
+     */
+    void clear() {
+        ib = null;
+    }
+
+
+    // --------------------------------------------- ServletInputStream Methods
+
+
+    public int read()
+        throws IOException {      
+
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+
+        }
+    
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            
+            try{
+                Integer result = 
+                    AccessController.doPrivileged(
+                        new PrivilegedExceptionAction<Integer>(){
+
+                            public Integer run() throws IOException{
+                                Integer integer = Integer.valueOf(ib.readByte());
+                                return integer;
+                            }
+
+                });
+                return result.intValue();
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+            return ib.readByte();
+        }   
+    }
+
+    public int available() throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                Integer result = 
+                    AccessController.doPrivileged(
+                        new PrivilegedExceptionAction<Integer>(){
+
+                            public Integer run() throws IOException{
+                                Integer integer = Integer.valueOf(ib.available());
+                                return integer;
+                            }
+
+                });
+                return result.intValue();
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+           return ib.available();
+        }           
+    }
+
+    public int read(final byte[] b) throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                Integer result = 
+                    AccessController.doPrivileged(
+                        new PrivilegedExceptionAction<Integer>(){
+
+                            public Integer run() throws IOException{
+                                Integer integer = 
+                                    Integer.valueOf(ib.read(b, 0, b.length));
+                                return integer;
+                            }
+
+                });
+                return result.intValue();
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+            return ib.read(b, 0, b.length);
+        }          
+    }
+
+
+    public int read(final byte[] b, final int off, final int len)
+        throws IOException {
+
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                Integer result = 
+                    AccessController.doPrivileged(
+                        new PrivilegedExceptionAction<Integer>(){
+
+                            public Integer run() throws IOException{
+                                Integer integer = 
+                                    Integer.valueOf(ib.read(b, off, len));
+                                return integer;
+                            }
+
+                });
+                return result.intValue();
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+            return ib.read(b, off, len);
+        }        
+    }
+
+    
+    public int readLine(byte[] b, int off, int len) throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        return super.readLine(b, off, len);
+    }
+
+
+    public boolean isFinished() {
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        return ib.isFinished();
+    }
+
+
+    public boolean isReady() {
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        return ib.isReady();
+    }
+
+
+    public void setReadListener(ReadListener readListener) {
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (readListener == null) {
+            throw new NullPointerException(rb.getString(LogFacade.NULL_READ_LISTENER_EXCEPTION));
+        }
+
+        ib.setReadListener(readListener);
+    }
+
+
+    /** 
+     * Close the stream
+     * Since we re-cycle, we can't allow the call to super.close()
+     * which would permanently disable us.
+     */
+    public void close() throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged(
+                    new PrivilegedExceptionAction<Void>(){
+
+                        public Void run() throws IOException{
+                            ib.close();
+                            return null;
+                        }
+
+                });
+            } catch(PrivilegedActionException pae){
+                Exception e = pae.getException();
+                if (e instanceof IOException){
+                    throw (IOException)e;
+                } else {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        } else {
+             ib.close();
+        }            
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteOutputStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteOutputStream.java
new file mode 100644
index 0000000..e73fcc1
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteOutputStream.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import org.apache.catalina.LogFacade;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import java.io.IOException;
+import java.util.ResourceBundle;
+
+/**
+ * Coyote implementation of the servlet output stream.
+ * 
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class CoyoteOutputStream 
+    extends ServletOutputStream {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected OutputBuffer ob;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // BEGIN S1AS 6175642
+    /*
+    protected CoyoteOutputStream(OutputBuffer ob) {
+    */
+    public CoyoteOutputStream(OutputBuffer ob) {
+    // END S1AS 6175642
+        this.ob = ob;
+    }
+    
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+    * Prevent cloning the facade.
+    */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+  
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear facade.
+     */
+    void clear() {
+        ob = null;
+    }
+
+
+    // --------------------------------------------------- OutputStream Methods
+
+
+    public void write(int i)
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        ob.writeByte(i);
+    }
+
+
+    public void write(byte[] b)
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        write(b, 0, b.length);
+    }
+
+
+    public void write(byte[] b, int off, int len)
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        ob.write(b, off, len);
+    }
+
+
+    /**
+     * Will send the buffer to the client.
+     */
+    public void flush()
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        ob.flush();
+    }
+
+
+    public void close()
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        ob.close();
+    }
+
+
+    // -------------------------------------------- ServletOutputStream Methods
+
+
+    public void print(String s)
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        ob.write(s);
+    }
+
+
+    public boolean isReady() {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        return ob.isReady();
+    }
+
+
+    public void setWriteListener(WriteListener writeListener) {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (writeListener == null) {
+            throw new NullPointerException(rb.getString(LogFacade.NULL_WRITE_LISTENER_EXCEPTION));
+        }
+
+        ob.setWriteListener(writeListener);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyotePrincipal.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyotePrincipal.java
new file mode 100644
index 0000000..be32267
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyotePrincipal.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import java.io.Serializable;
+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: 1.2 $ $Date: 2005/12/08 01:28:34 $
+ */
+
+public class CoyotePrincipal implements Principal, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+
+    // ----------------------------------------------------------- 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() {
+
+        StringBuilder sb = new StringBuilder("CoyotePrincipal[");
+        sb.append(this.name);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteReader.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteReader.java
new file mode 100644
index 0000000..8bf1fe1
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteReader.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import org.apache.catalina.LogFacade;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Coyote implementation of the buffered reader.
+ * 
+ * @author Remy Maucherat
+ */
+public class CoyoteReader
+    extends BufferedReader {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    private static final char[] LINE_SEP = { '\r', '\n' };
+    private static final int MAX_LINE_LENGTH = 4096;
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected InputBuffer ib;
+
+    protected char[] lineBuffer = null;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyoteReader(InputBuffer ib) {
+        super(ib, 1);
+        this.ib = ib;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+    * Prevent cloning the facade.
+    */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+    
+    
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear facade.
+     */
+    void clear() {
+        ib = null;
+    }
+
+
+    // --------------------------------------------------------- Reader Methods
+
+
+    public void close()
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        ib.close();
+    }
+
+
+    public int read()
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        return ib.read();
+    }
+
+
+    public int read(char[] cbuf)
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        return ib.read(cbuf, 0, cbuf.length);
+    }
+
+
+    public int read(char[] cbuf, int off, int len)
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        return ib.read(cbuf, off, len);
+    }
+
+
+    public long skip(long n)
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        return ib.skip(n);
+    }
+
+
+    public boolean ready()
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        return ib.ready();
+    }
+
+
+    public boolean markSupported() {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        return true;
+    }
+
+
+    public void mark(int readAheadLimit)
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        ib.mark(readAheadLimit);
+    }
+
+
+    public void reset()
+        throws IOException {
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        ib.reset();
+    }
+
+
+    public String readLine()
+        throws IOException {
+
+        // Disallow operation if the object has gone out of scope
+        if (ib == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (lineBuffer == null) {
+            lineBuffer = new char[MAX_LINE_LENGTH];
+        }
+
+        String result = null;
+
+        int pos = 0;
+        int end = -1;
+        int skip = -1;
+        StringBuilder aggregator = null;
+        while (end < 0) {
+            mark(MAX_LINE_LENGTH);
+            while ((pos < MAX_LINE_LENGTH) && (end < 0)) {
+                int nRead = read(lineBuffer, pos, MAX_LINE_LENGTH - pos);
+                if (nRead < 0) {
+                    if (pos == 0 && aggregator == null) {
+                        return null;
+                    }
+                    end = pos;
+                    skip = pos;
+                }
+                for (int i = pos; (i < (pos + nRead)) && (end < 0); i++) {
+                    if (lineBuffer[i] == LINE_SEP[0]) {
+                        end = i;
+                        skip = i + 1;
+                        char nextchar;
+                        if (i == (pos + nRead - 1)) {
+                            nextchar = (char) read();
+                        } else {
+                            nextchar = lineBuffer[i+1];
+                        }
+                        if (nextchar == LINE_SEP[1]) {
+                            skip++;
+                        }
+                    } else if (lineBuffer[i] == LINE_SEP[1]) {
+                        end = i;
+                        skip = i + 1;
+                    }
+                }
+                if (nRead > 0) {
+                    pos += nRead;
+                }
+            }
+            if (end < 0) {
+                if (aggregator == null) {
+                    aggregator = new StringBuilder();
+                }
+                aggregator.append(lineBuffer);
+                pos = 0;
+            } else {
+                reset();
+                if (skip(skip) != skip && log.isLoggable(Level.WARNING)) {
+                    log.log(Level.WARNING, LogFacade.FAILED_SKIP_CHARS_IN_BUFFER, skip);
+                }
+            }
+        }
+
+        if (aggregator == null) {
+            result = new String(lineBuffer, 0, end);
+        } else {
+            aggregator.append(lineBuffer, 0, end);
+            result = aggregator.toString();
+        }
+
+        return result;
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteServerSocketFactory.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteServerSocketFactory.java
new file mode 100644
index 0000000..386676e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteServerSocketFactory.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+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>. [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 {
+
+    private String algorithm = null;
+    private boolean clientAuth = false;
+    private String keystoreFile =
+        System.getProperty("user.home") + File.separator + ".keystore";
+    private String randomFile =
+        System.getProperty("user.home") + File.separator + "random.pem";
+    private String rootFile =
+        System.getProperty("user.home") + File.separator + "root.pem";
+    private String keystorePass = "changeit";
+    private String keystoreType = "JKS";
+    private String protocol = "TLS";
+    private String protocols;
+    private String sslImplementation = null;
+    private String cipherSuites;
+    private String keyAlias;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Gets the certificate encoding algorithm to be used.
+     *
+     * @return Certificate encoding algorithm
+     */
+    public String getAlgorithm() {
+        return (this.algorithm);
+    }
+
+    /**
+     * Sets the certificate encoding algorithm to be used.
+     *
+     * @param algorithm Certificate encoding algorithm
+     */
+    public void setAlgorithm(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+    /**
+     * Provides information about whether client authentication is enforced.
+     *
+     * @return true if client authentication is enforced, false otherwise
+     */
+    public boolean getClientAuth() {
+        return (this.clientAuth);
+    }
+
+    /**
+     * Sets the requirement of client authentication.
+     *
+     * @param clientAuth true if client authentication is enforced, false
+     * otherwise
+     */
+    public void setClientAuth(boolean clientAuth) {
+        this.clientAuth = clientAuth;
+    }
+
+    /**
+     * Gets the pathname to the keystore file.
+     *
+     * @return Pathname to the keystore file
+     */
+    public String getKeystoreFile() {
+        return (this.keystoreFile);
+    }
+
+    /**
+     * Sets the pathname to the keystore file.
+     *
+     * @param keystoreFile Pathname to the keystore file
+     */
+    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();
+    }
+
+    /**
+     * Gets the pathname to the random file.
+     *
+     * @return Pathname to the random file
+     */
+    public String getRandomFile() {
+        return (this.randomFile);
+    }
+
+    /**
+     * Sets the pathname to the random file.
+     *
+     * @param randomFile Pathname to the random file
+     */
+    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();
+    }
+
+    /**
+     * Gets the pathname to the root list.
+     *
+     * @return Pathname to the root list
+     */
+    public String getRootFile() {
+        return (this.rootFile);
+    }
+
+    /**
+     * Sets the pathname to the root list.
+     *
+     * @param rootFile Pathname to the root list
+     */
+    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();
+    }
+     
+    /**
+     * Gets the keystore password.
+     *
+     * @return Keystore password
+     */
+    public String getKeystorePass() {
+        return (this.keystorePass);
+    }
+
+    /**
+     * Sets the keystore password.
+     *
+     * @param keystorePass Keystore password
+     */
+    public void setKeystorePass(String keystorePass) {
+        this.keystorePass = keystorePass;
+    }
+
+    /**
+     * Gets the keystore type.
+     *
+     * @return Keystore type
+     */
+    public String getKeystoreType() {
+        return (this.keystoreType);
+    }
+
+    /**
+     * Sets the keystore type.
+     *
+     * @param keystoreType Keystore type
+     */
+    public void setKeystoreType(String keystoreType) {
+        this.keystoreType = keystoreType;
+    }
+
+    /**
+     * Gets the SSL protocol variant to be used.
+     *
+     * @return SSL protocol variant
+     */
+    public String getProtocol() {
+        return (this.protocol);
+    }
+
+    /**
+     * Sets the SSL protocol variant to be used.
+     *
+     * @param protocol SSL protocol variant
+     */
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    /**
+     * Gets the SSL protocol variants to be enabled.
+     *
+     * @return Comma-separated list of SSL protocol variants
+     */
+    public String getProtocols() {
+        return this.protocols;
+    }
+
+    /**
+     * Sets the SSL protocol variants to be enabled.
+     *
+     * @param protocols Comma-separated list of SSL protocol variants
+     */
+    public void setProtocols(String protocols) {
+        this.protocols = protocols;
+    }
+
+    /**
+     * Gets the name of the SSL implementation to be used.
+     *
+     * @return SSL implementation name
+     */
+    public String getSSLImplementation() {
+        return (this.sslImplementation);
+    }
+
+    /**
+     * Sets the name of the SSL implementation to be used.
+     *
+     * @param sslImplementation SSL implementation name
+     */
+    public void setSSLImplementation(String sslImplementation) {
+        this.sslImplementation = sslImplementation;
+    }
+
+    /**
+     * Gets the alias name of the keypair and supporting certificate chain
+     * used by the server to authenticate itself to SSL clients.
+     *
+     * @return The alias name of the keypair and supporting certificate chain
+     */
+    public String getKeyAlias() {
+        return this.keyAlias;
+    }
+
+    /**
+     * Sets the alias name of the keypair and supporting certificate chain
+     * used by the server to authenticate itself to SSL clients.
+     *
+     * @param alias The alias name of the keypair and supporting certificate
+     * chain
+     */
+    public void setKeyAlias(String alias) {
+        this.keyAlias = alias;
+    }
+
+    /**
+     * Gets the list of SSL cipher suites that are to be enabled
+     *
+     * @return Comma-separated list of SSL cipher suites, or null if all
+     * cipher suites supported by the underlying SSL implementation are being
+     * enabled
+     */
+    public String getCiphers() {
+	return this.cipherSuites;
+    }
+
+    /**
+     * Sets the SSL cipher suites that are to be enabled.
+     *
+     * Only those SSL cipher suites that are actually supported by
+     * the underlying SSL implementation will be enabled.
+     *
+     * @param ciphers Comma-separated list of SSL cipher suites
+     */
+    public void setCiphers(String ciphers) {
+	this.cipherSuites = ciphers;
+    }
+
+
+    // --------------------------------------------------------- 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/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteWriter.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteWriter.java
new file mode 100644
index 0000000..77bed10
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/CoyoteWriter.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import org.apache.catalina.LogFacade;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ResourceBundle;
+
+
+/**
+ * Coyote implementation of the servlet writer.
+ * 
+ * @author Remy Maucherat
+ * @author Kin-man Chung
+ */
+public class CoyoteWriter
+    extends PrintWriter {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // No need for a do privileged block - every web app has permission to read
+    // this by default
+    private static final char[] LINE_SEP =
+        System.getProperty("line.separator").toCharArray();
+
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected OutputBuffer ob;
+    protected boolean error = false;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyoteWriter(OutputBuffer ob) {
+        super(ob);
+        this.ob = ob;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+    * Prevent cloning the facade.
+    */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+    
+    
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Clear facade.
+     */
+    void clear() {
+        ob = null;
+    }
+
+    /**
+     * Recycle.
+     */
+    void recycle() {
+        error = false;
+    }
+
+
+    // --------------------------------------------------------- Writer Methods
+
+
+    public void flush() {
+
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (error)
+            return;
+
+        try {
+            ob.flush();
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void close() {
+
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        // 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 ) {
+            // Ignore
+        }
+        error = false;
+
+    }
+
+
+    public boolean checkError() {
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+        flush();
+        return error;
+    }
+
+
+    public void write(int c) {
+
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (error)
+            return;
+
+        try {
+            ob.write(c);
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void write(char buf[], int off, int len) {
+
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        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) {
+
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (error)
+            return;
+
+        try {
+            ob.write(s, off, len);
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void write(String s) {
+        write(s, 0, s.length());
+    }
+
+
+    public void write(byte[] buff, int off, int len) {
+
+        // Disallow operation if the object has gone out of scope
+        if (ob == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.OBJECT_INVALID_SCOPE_EXCEPTION));
+        }
+
+        if (error)
+            return;
+
+        try {
+            ob.write(buff, off, len);
+        } catch (IOException e) {
+            error = true;
+        }
+    }
+
+
+    // ---------------------------------------------------- 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/appserver/web/web-core/src/main/java/org/apache/catalina/connector/InputBuffer.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/InputBuffer.java
new file mode 100644
index 0000000..65190a4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/InputBuffer.java
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.channels.InterruptedByTimeoutException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ReadListener;
+import javax.servlet.http.WebConnection;
+
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.Context;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Globals;
+import org.glassfish.grizzly.ReadHandler;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.util.ByteChunk.ByteInputChannel;
+import org.glassfish.grizzly.http.util.CharChunk;
+
+/**
+ * The buffer used by Tomcat request. This is a derivative of the Tomcat 3.3
+ * OutputBuffer, adapted to handle input instead of output. This allows 
+ * complete recycling of the facade objects (the ServletInputStream and the
+ * BufferedReader).
+ *
+ * @author Remy Maucherat
+ */
+public class InputBuffer extends Reader
+    implements ByteInputChannel, CharChunk.CharInputChannel,
+               CharChunk.CharOutputChannel {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Grizzly request.
+     */
+    private Request grizzlyRequest;
+
+    private org.glassfish.grizzly.http.io.InputBuffer grizzlyInputBuffer;
+
+    private org.apache.catalina.connector.Request request;
+
+    private ReadHandler readHandler = null;
+    private boolean prevIsReady = true;
+    private static final ThreadLocal<Boolean> IS_READY_SCOPE = new ThreadLocal<Boolean>();
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Allocate the buffer with the default buffer size.
+     */
+    public InputBuffer() {
+
+        this(DEFAULT_BUFFER_SIZE);
+
+    }
+
+
+    /**
+     * Alternate constructor which allows specifying the initial buffer size.
+     *
+     * @param size Buffer size to use
+     */
+    public InputBuffer(int size) {
+
+//        this.size = size;
+//        bb = new ByteChunk(size);
+//        bb.setLimit(size);
+//        bb.setByteInputChannel(this);
+    }
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Grizzly request.
+     * 
+     * @param grizzlyRequest Associated Grizzly request
+     */
+    public void setRequest(Request grizzlyRequest) {
+        this.grizzlyRequest = grizzlyRequest;
+        this.grizzlyInputBuffer = grizzlyRequest.getInputBuffer();
+    }
+
+
+    public void setRequest(org.apache.catalina.connector.Request request) {
+        this.request = request;
+    }
+
+
+    /**
+     * Get associated Grizzly request.
+     * 
+     * @return the associated Grizzly request
+     */
+    public Request getRequest() {
+        return this.grizzlyRequest;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the output buffer.
+     */
+    public void recycle() {
+
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "recycle()");
+
+        grizzlyInputBuffer = null;
+        grizzlyRequest = null;
+        readHandler = null;
+        prevIsReady = true;
+
+    }
+
+
+    /**
+     * Close the input buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void close()
+        throws IOException {
+        grizzlyInputBuffer.close();
+    }
+
+
+    public int available()
+        throws IOException {
+        return grizzlyInputBuffer.readyData();
+    }
+
+
+    // ------------------------------------------------- Bytes Handling Methods
+
+
+    /** 
+     * Reads new bytes in the byte chunk.
+     * 
+     * @param cbuf Byte buffer to be written to the response
+     * @param off Offset
+     * @param len Length
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public int realReadBytes(byte cbuf[], int off, int len)
+	throws IOException {
+        return grizzlyInputBuffer.read(cbuf, off, len);
+    }
+
+
+    public int readByte()
+        throws IOException {
+        if (grizzlyInputBuffer.isClosed())
+            throw new IOException(rb.getString(LogFacade.STREAM_CLOSED));
+
+        return grizzlyInputBuffer.readByte();
+    }
+
+
+    public int read(final byte[] b, final int off, final int len)
+        throws IOException {
+        if (grizzlyInputBuffer.isClosed())
+            throw new IOException(rb.getString(LogFacade.STREAM_CLOSED));
+
+        return grizzlyInputBuffer.read(b, off, len);
+    }
+
+
+    public boolean isFinished() {
+        return grizzlyInputBuffer.isFinished();
+    }
+
+
+    public boolean isReady() {
+        if (!prevIsReady) {
+            return false;
+        }
+
+        boolean result = (grizzlyInputBuffer.available() > 0);
+        if (!result) {
+            if (readHandler != null) {
+                prevIsReady = false; // Not data available
+                IS_READY_SCOPE.set(Boolean.TRUE);
+                try {
+                    grizzlyInputBuffer.notifyAvailable(readHandler);
+                } finally {
+                    IS_READY_SCOPE.remove();
+                }
+                
+            } else {
+                prevIsReady = true;  // Allow next .isReady() call to check underlying inputStream
+            }
+        }
+
+        return result;
+    }
+
+
+    public void setReadListener(ReadListener readListener) {
+        if (readHandler != null) {
+            throw new IllegalStateException(rb.getString(LogFacade.ALREADY_SET_READ_LISTENER));
+        }
+
+        if (!(request.isAsyncStarted() || request.isUpgrade())) {
+            throw new IllegalStateException(rb.getString(LogFacade.NON_ASYNC_UPGRADE_READER_EXCEPTION));
+        }
+
+        readHandler = new ReadHandlerImpl(readListener);
+
+        if (isReady()) {
+            try {
+                readHandler.onDataAvailable();
+            } catch(Throwable t) {
+                log.log(Level.WARNING, LogFacade.READ_LISTENER_ON_DATA_AVAILABLE_ERROR, t);
+            }
+        }
+    }
+
+    void disableReadHandler() {
+        if (readHandler != null) {
+            synchronized(readHandler) {
+                readHandler.onError(new InterruptedByTimeoutException());
+            }
+        }
+    }
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    /**
+     * Since the converter will use append, it is possible to get chars to
+     * be removed from the buffer for "writing". Since the chars have already
+     * been read before, they are ignored. If a mark was set, then the
+     * mark is lost.
+     */
+    public void realWriteChars(char c[], int off, int len) 
+        throws IOException {
+        // START OF SJSAS 6231069
+//        initChar();
+        // END OF SJSAS 6231069
+//        markPos = -1;
+    }
+
+
+    public void setEncoding(final String encoding) {
+        grizzlyInputBuffer.setDefaultEncoding(encoding);
+    }
+
+
+    public int realReadChars(final char cbuf[], final int off, final int len)
+        throws IOException {
+
+        return grizzlyInputBuffer.read(cbuf, off, len);
+
+    }
+
+
+    public int read()
+        throws IOException {
+
+        if (grizzlyInputBuffer.isClosed())
+            throw new IOException(rb.getString(LogFacade.STREAM_CLOSED));
+
+        return grizzlyInputBuffer.readChar();
+    }
+
+
+    public int read(char[] cbuf)
+        throws IOException {
+
+        return read(cbuf, 0, cbuf.length);
+    }
+
+
+    public int read(char[] cbuf, int off, int len)
+        throws IOException {
+
+        if (grizzlyInputBuffer.isClosed())
+            throw new IOException(rb.getString(LogFacade.STREAM_CLOSED));
+
+        return grizzlyInputBuffer.read(cbuf, off, len);
+    }
+
+
+    public long skip(long n)
+        throws IOException {
+
+        if (grizzlyInputBuffer.isClosed())
+            throw new IOException(rb.getString(LogFacade.STREAM_CLOSED));
+
+        if (n < 0) {
+            throw new IllegalArgumentException();
+        }
+        return grizzlyInputBuffer.skip(n, true);
+
+    }
+
+
+    public boolean ready()
+        throws IOException {
+
+        if (grizzlyInputBuffer.isClosed())
+            throw new IOException(rb.getString(LogFacade.STREAM_CLOSED));
+
+        return grizzlyInputBuffer.ready();
+    }
+
+
+    public boolean markSupported() {
+        return true;
+    }
+
+
+    public void mark(int readAheadLimit)
+        throws IOException {
+        grizzlyInputBuffer.mark(readAheadLimit);
+    }
+
+
+    public void reset()
+        throws IOException {
+
+        if (grizzlyInputBuffer.isClosed())
+            throw new IOException(rb.getString(LogFacade.STREAM_CLOSED));
+        grizzlyInputBuffer.reset();
+    }
+
+
+    public void checkConverter() 
+        throws IOException {
+
+        grizzlyInputBuffer.processingChars();
+
+    }
+
+
+    class ReadHandlerImpl implements ReadHandler {
+        private ReadListener readListener = null;
+        private volatile boolean disable = false;
+
+        private ReadHandlerImpl(ReadListener listener) {
+            readListener = listener;
+        }
+
+        @Override
+        public void onDataAvailable() {
+            if (disable) {
+                return;
+            }
+            if (!Boolean.TRUE.equals(IS_READY_SCOPE.get())) {
+                processDataAvailable();
+            } else {
+                AsyncContextImpl.getExecutorService().execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        processDataAvailable();
+                    }
+                });
+            }
+        }
+
+        private void processDataAvailable() {
+            ClassLoader oldCL;
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
+                oldCL = AccessController.doPrivileged(pa);
+            } else {
+                oldCL = Thread.currentThread().getContextClassLoader();
+            }
+
+            try {
+                Context context = request.getContext();
+                ClassLoader newCL = context.getLoader().getClassLoader();
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(newCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(newCL);
+                }
+
+                synchronized(this) {
+                    prevIsReady = true;
+                    try {
+                        context.fireContainerEvent(
+                            ContainerEvent.BEFORE_READ_LISTENER_ON_DATA_AVAILABLE, readListener);
+                        readListener.onDataAvailable();
+                    } catch(Throwable t) {
+                        disable = true;
+                        readListener.onError(t);
+                    } finally {
+                        context.fireContainerEvent(
+                            ContainerEvent.AFTER_READ_LISTENER_ON_DATA_AVAILABLE, readListener);
+                    }
+                }
+            } finally {
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(oldCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(oldCL);
+                }
+            }
+        }
+
+        @Override
+        public void onAllDataRead() {
+            if (disable) {
+                return;
+            }
+            if (!Boolean.TRUE.equals(IS_READY_SCOPE.get())) {
+                processAllDataRead();
+            } else {
+                AsyncContextImpl.getExecutorService().execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        processAllDataRead();
+                    }
+                });
+            }
+        }
+
+        private void processAllDataRead() {
+            ClassLoader oldCL;
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
+                oldCL = AccessController.doPrivileged(pa);
+            } else {
+                oldCL = Thread.currentThread().getContextClassLoader();
+            }
+
+            try {
+                Context context = request.getContext();
+                ClassLoader newCL = context.getLoader().getClassLoader();
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(newCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(newCL);
+                }
+
+                synchronized(this) {
+                    prevIsReady = true;
+                    try {
+                        context.fireContainerEvent(
+                            ContainerEvent.BEFORE_READ_LISTENER_ON_ALL_DATA_READ, readListener);
+                        readListener.onAllDataRead();
+                    } catch(Throwable t) {
+                        disable = true;
+                        readListener.onError(t);
+                    } finally {
+                        context.fireContainerEvent(
+                            ContainerEvent.AFTER_READ_LISTENER_ON_ALL_DATA_READ, readListener);
+                    }
+                }
+            } finally {
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(oldCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(oldCL);
+                }
+            }
+        }
+
+        @Override
+        public void onError(final Throwable t) {
+            if (disable) {
+                return;
+            }
+            disable = true;
+
+            if (!Boolean.TRUE.equals(IS_READY_SCOPE.get())) {
+                processError(t);
+            } else {
+                AsyncContextImpl.getExecutorService().execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        processError(t);
+                    }
+                });
+            }
+        }
+
+        private void processError(final Throwable t) {
+            ClassLoader oldCL;
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
+                oldCL = AccessController.doPrivileged(pa);
+            } else {
+                oldCL = Thread.currentThread().getContextClassLoader();
+            }
+
+            try {
+                Context context = request.getContext();
+                ClassLoader newCL = context.getLoader().getClassLoader();
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(newCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(newCL);
+                }
+
+                synchronized(this) {
+                    // Get isUpgrade and WebConnection before calling onError
+                    // Just in case onError will complete the async processing.
+                    final boolean isUpgrade = request.isUpgrade();
+                    final WebConnection wc = request.getWebConnection();
+
+                    try {
+                        context.fireContainerEvent(
+                            ContainerEvent.BEFORE_READ_LISTENER_ON_ERROR, readListener);
+                        readListener.onError(t);
+                    } finally {
+                        if (isUpgrade && wc != null) {
+                            try {
+                                wc.close();
+                            } catch (Exception ignored) {
+                            }
+                        }
+                        context.fireContainerEvent(
+                            ContainerEvent.AFTER_READ_LISTENER_ON_ERROR, readListener);
+
+                    }
+                }
+            } finally {
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(oldCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(oldCL);
+                }
+            }
+        }
+    }
+
+    private static class PrivilegedSetTccl implements PrivilegedAction<Void> {
+
+        private ClassLoader cl;
+
+        PrivilegedSetTccl(ClassLoader cl) {
+            this.cl = cl;
+        }
+
+        @Override
+        public Void run() {
+            Thread.currentThread().setContextClassLoader(cl);
+            return null;
+        }
+    }
+
+    private static class PrivilegedGetTccl
+            implements PrivilegedAction<ClassLoader> {
+
+        @Override
+        public ClassLoader run() {
+            return Thread.currentThread().getContextClassLoader();
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/MappingImpl.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/MappingImpl.java
new file mode 100644
index 0000000..9a5357d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/MappingImpl.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.connector;
+
+import java.io.Serializable;
+import java.util.Objects;
+import java.util.ResourceBundle;
+
+import javax.servlet.http.MappingMatch;
+import javax.servlet.http.HttpServletMapping;
+
+import org.glassfish.grizzly.http.server.util.MappingData;
+
+import org.apache.catalina.LogFacade;
+
+public class MappingImpl implements HttpServletMapping, Serializable {
+
+    private static final long serialVersionUID = -5134622427867249518L;
+
+    private String matchValue;
+
+    private String pattern;
+    
+    private String servletName;
+
+    private MappingMatch mappingMatch;   
+    
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    public MappingImpl(MappingData mappingData) {
+        if (null == mappingData) {
+            throw new NullPointerException(rb.getString(LogFacade.MAPPING_ERROR_EXCEPTION));
+        }
+        
+        // Trim leading "/"
+        matchValue = (null != mappingData.matchedPath) &&
+                     (mappingData.matchedPath.length() >= 2) ? mappingData.matchedPath.substring(1) : "";
+        pattern = null != mappingData.descriptorPath ? mappingData.descriptorPath : "";
+        servletName = null != mappingData.servletName ? mappingData.servletName : "";
+        
+        switch (mappingData.mappingType) {
+            case MappingData.CONTEXT_ROOT:
+                mappingMatch = MappingMatch.CONTEXT_ROOT;
+                break;
+            case MappingData.DEFAULT:
+                mappingMatch = MappingMatch.DEFAULT;
+                matchValue = "";
+                break;
+            case MappingData.EXACT:
+                mappingMatch = MappingMatch.EXACT;
+                break;
+            case MappingData.EXTENSION:
+                mappingMatch = MappingMatch.EXTENSION;
+                // Ensure pattern is valid
+                if (null != pattern && '*' == pattern.charAt(0)) {
+                    // Mutate matchValue to mean "what * was matched with".
+                    int i = matchValue.indexOf(pattern.substring(1));
+                    if (-1 != i) {
+                        matchValue = matchValue.substring(0, i);
+                    }
+                }
+                break;
+            case MappingData.PATH:
+                mappingMatch = MappingMatch.PATH;
+                // Ensure pattern is valid
+                if (null != pattern) {
+                    int patternLen = pattern.length();
+                    if (0 < patternLen && '*' == pattern.charAt(patternLen-1)) {
+                        int indexOfPatternStart = patternLen - 2;
+                        int matchValueLen = matchValue.length();
+                        if (0 <= indexOfPatternStart && indexOfPatternStart < matchValueLen) {
+                            // Remove the pattern from the end of matchValue
+                            matchValue = matchValue.substring(indexOfPatternStart);
+                        }
+                    }
+                }
+                break;
+        }
+
+    }
+    
+    @Override
+    public String getMatchValue() {
+        return matchValue;
+    }
+
+    @Override
+    public String getPattern() {
+        return pattern;
+    }
+
+    @Override
+    public String getServletName() {
+        return servletName;
+    }
+    
+    
+    
+    @Override
+    public MappingMatch getMappingMatch() {
+        return mappingMatch;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 29 * hash + Objects.hashCode(this.matchValue);
+        hash = 29 * hash + Objects.hashCode(this.pattern);
+        hash = 29 * hash + Objects.hashCode(this.servletName);
+        hash = 29 * hash + Objects.hashCode(this.mappingMatch);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final MappingImpl other = (MappingImpl) obj;
+        if (!Objects.equals(this.matchValue, other.matchValue)) {
+            return false;
+        }
+        if (!Objects.equals(this.pattern, other.pattern)) {
+            return false;
+        }
+        if (!Objects.equals(this.servletName, other.servletName)) {
+            return false;
+        }
+        if (this.mappingMatch != other.mappingMatch) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "MappingImpl{" + "matchValue=" + matchValue 
+                + ", pattern=" + pattern 
+                + ", servletName=" + servletName 
+                + ", mappingMatch=" + mappingMatch + '}';
+    }
+
+
+    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/OutputBuffer.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/OutputBuffer.java
new file mode 100644
index 0000000..7ae1b84
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/OutputBuffer.java
@@ -0,0 +1,856 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.channels.InterruptedByTimeoutException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.WriteListener;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Session;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.RequestUtil;
+import org.glassfish.common.util.InputValidationUtil;
+import org.glassfish.grizzly.WriteHandler;
+import org.glassfish.grizzly.http.util.ByteChunk;
+
+/**
+ * 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 responsibility).
+ *
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class OutputBuffer extends Writer
+    implements ByteChunk.ByteOutputChannel {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // -------------------------------------------------------------- Constants
+
+    private static final String SET_COOKIE_HEADER = "Set-Cookie";
+    public static final String DEFAULT_ENCODING = 
+        org.glassfish.grizzly.http.util.Constants.DEFAULT_HTTP_CHARACTER_ENCODING;
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+    static final int debug = 0;
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Number of bytes written.
+     */
+    private int bytesWritten = 0;
+
+
+    /**
+     * Number of chars written.
+     */
+    private int charsWritten = 0;
+
+
+    /**
+     * Associated Coyote response.
+     */
+    private Response response;
+    
+    private org.glassfish.grizzly.http.server.Response grizzlyResponse;
+    private org.glassfish.grizzly.http.io.OutputBuffer grizzlyOutputBuffer;
+
+    private WriteHandler writeHandler = null;
+    private boolean prevIsReady = true;
+    private static final ThreadLocal<Boolean> CAN_WRITE_SCOPE = new ThreadLocal<Boolean>();
+
+    /**
+     * Suspended flag. All output bytes will be swallowed if this is true.
+     */
+    private boolean suspended = false;
+
+    private int size;
+    
+    private org.glassfish.grizzly.http.io.OutputBuffer.LifeCycleListener sessionCookieChecker =
+            new SessionCookieChecker();
+    // ----------------------------------------------------------- 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) {
+        // START S1AS8 4861933
+        /*
+        bb = new ByteChunk(size);
+        bb.setLimit(size);
+        bb.setByteOutputChannel(this);
+        cb = new CharChunk(size);
+        cb.setCharOutputChannel(this);
+        cb.setLimit(size);
+        */
+        this.size = size;
+        // END S1AS8 4861933
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    public void setCoyoteResponse(Response coyoteResponse) {
+        this.response = coyoteResponse;
+        this.grizzlyResponse = coyoteResponse.getCoyoteResponse();
+        this.grizzlyOutputBuffer = grizzlyResponse.getOutputBuffer();
+        grizzlyOutputBuffer.setBufferSize(size);
+        grizzlyOutputBuffer.registerLifeCycleListener(sessionCookieChecker);
+        // @TODO set chunkingDisabled
+    }
+
+    /**
+     * 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 (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "recycle()");
+
+        bytesWritten = 0;
+        charsWritten = 0;
+
+        suspended = false;
+        grizzlyResponse = null;
+        grizzlyOutputBuffer = null;
+        writeHandler = null;
+        prevIsReady = true;
+        response = 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 (suspended)
+            return;
+
+        grizzlyOutputBuffer.close();
+
+    }
+
+
+    /**
+     * 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;
+
+        if (realFlush || !grizzlyResponse.isCommitted()) {
+            grizzlyOutputBuffer.flush();
+        }
+
+    }
+
+
+    // ------------------------------------------------- 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 (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "realWrite(b, " + off + ", " + cnt + ") " + grizzlyResponse);
+
+        if (grizzlyResponse == null)
+            return;
+        
+        if (grizzlyOutputBuffer.isClosed())
+            return;
+
+        // If we really have something to write
+        if (cnt > 0) {
+            try {
+                grizzlyOutputBuffer.write(buf, off, cnt);
+            } 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;
+
+        writeBytes(b, off, len);
+
+    }
+
+
+    private void writeBytes(byte b[], int off, int len) 
+        throws IOException {
+
+        if (grizzlyOutputBuffer.isClosed())
+            return;
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "write(b,off,len)");
+
+        grizzlyOutputBuffer.write(b, off, len);
+        bytesWritten += len;
+
+    }
+
+
+    // XXX Char or byte ?
+    public void writeByte(int b)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        grizzlyOutputBuffer.writeByte(b);
+        bytesWritten++;
+    }
+
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    public void write(int c)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        grizzlyOutputBuffer.writeChar(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;
+
+        grizzlyOutputBuffer.write(c, off, len);
+        charsWritten += len;
+    }
+
+
+    /**
+     * Append a string to the buffer
+     */
+    public void write(String s, int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        charsWritten += len;
+        if (s==null)
+            s="null";
+        grizzlyOutputBuffer.write(s, off, len);
+    }
+
+
+    public void write(String s)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        if (s == null)
+            s = "null";
+        grizzlyOutputBuffer.write(s);
+    } 
+
+
+    public void checkConverter()
+        throws IOException {
+        
+        grizzlyOutputBuffer.prepareCharacterEncoder();
+
+    }
+
+    
+    // --------------------  BufferedOutputStream compatibility
+
+
+    /**
+     * Real write - this buffer will be sent to the client
+     */
+    public void flushBytes()
+        throws IOException {
+
+        grizzlyOutputBuffer.flush();
+
+    }
+
+
+    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 > grizzlyOutputBuffer.getBufferSize()) {
+            grizzlyOutputBuffer.setBufferSize(size);
+        }
+    }
+
+
+    public void reset() {
+
+        grizzlyOutputBuffer.reset();
+        bytesWritten = 0;
+        charsWritten = 0;
+
+    }
+
+
+    public int getBufferSize() {
+        return grizzlyOutputBuffer.getBufferSize();
+    }
+
+
+    public boolean isReady() {
+        if (!prevIsReady) {
+            return false;
+        }
+        
+        boolean result = grizzlyOutputBuffer.canWrite();
+        if (!result) {
+            if (writeHandler != null) {
+                prevIsReady = false; // Not can write
+                CAN_WRITE_SCOPE.set(Boolean.TRUE);
+                try {
+                    grizzlyOutputBuffer.notifyCanWrite(writeHandler);
+                } finally {
+                    CAN_WRITE_SCOPE.remove();
+                }
+                
+            } else {
+                prevIsReady = true;  // Allow next .isReady() call to check underlying outputStream
+            }
+        }
+        
+        return result;
+    }
+
+    public void setWriteListener(WriteListener writeListener) {
+        if (writeHandler != null) {
+            throw new IllegalStateException(rb.getString(LogFacade.WRITE_LISTENER_BEEN_SET));
+        }
+
+        Request req = (Request)response.getRequest();
+        if (!(req.isAsyncStarted() || req.isUpgrade())) {
+            throw new IllegalStateException(rb.getString(LogFacade.NON_ASYNC_UPGRADE_WRITER_EXCEPTION));
+        }
+
+        writeHandler = new WriteHandlerImpl(writeListener);
+
+        if (isReady()) {
+            try {
+                writeHandler.onWritePossible();
+            } catch(Throwable t) {
+                log.log(Level.WARNING, LogFacade.WRITE_LISTENER_ON_WRITE_POSSIBLE_ERROR, t);
+            }
+        }
+    }
+
+    void disableWriteHandler() {
+        if (writeHandler != null) {
+            synchronized(writeHandler) {
+                writeHandler.onError(new InterruptedByTimeoutException());
+            }
+        }
+    }
+
+
+    private void addSessionCookies() throws IOException {
+        Request req = (Request) response.getRequest();
+        if (req.isRequestedSessionIdFromURL()) {
+            return;
+        }
+
+        StandardContext ctx = (StandardContext) response.getContext();
+        if (ctx == null || !ctx.getCookies()) {
+            // cookies disabled
+            return;
+        }
+
+        Session sess = req.getSessionInternal(false);
+        if (sess != null) {
+            addSessionVersionCookie(req, ctx);
+            addSessionCookieWithJvmRoute(req, ctx, sess);
+            addSessionCookieWithJReplica(req, ctx, sess);
+            addPersistedSessionCookie(req, ctx, sess);
+            addJrouteCookie(req, ctx, sess);
+            addSsoVersionCookie(req, ctx);
+        }
+    }
+
+    /**
+     * Adds a session version cookie to the response if necessary.
+     */
+    private void addSessionVersionCookie(Request request,
+                                         StandardContext context) {
+        Map<String, String> sessionVersions =
+            request.getSessionVersionsRequestAttribute();
+        if (sessionVersions != null) {
+            Cookie cookie = new Cookie(
+                Globals.SESSION_VERSION_COOKIE_NAME,
+                RequestUtil.createSessionVersionString(sessionVersions));
+            request.configureSessionCookie(cookie);
+            if (request.isRequestedSessionIdFromCookie()) {
+                /*
+                 * Have the JSESSIONIDVERSION cookie inherit the 
+                 * security setting of the JSESSIONID cookie to avoid
+                 * session loss when switching from HTTPS to HTTP,
+                 * see IT 7414
+                 */
+                cookie.setSecure(
+                    request.isRequestedSessionIdFromSecureCookie());
+            }
+            grizzlyResponse.addHeader(SET_COOKIE_HEADER,
+                               response.getCookieString(cookie));
+        }
+    }
+
+    /**
+     * Adds JSESSIONID cookie whose value includes jvmRoute if necessary.
+     */
+    private void addSessionCookieWithJvmRoute(Request request, StandardContext ctx,
+            Session sess) {
+
+        if (ctx.getJvmRoute() == null || sess == null) {
+            return;
+        }
+
+        // Create JSESSIONID cookie that includes jvmRoute
+        Cookie cookie = getSafeCookie(ctx.getSessionCookieName(),
+                sess.getIdInternal() + "." + ctx.getJvmRoute());
+        request.configureSessionCookie(cookie);
+        grizzlyResponse.addHeader(SET_COOKIE_HEADER,
+                response.getCookieString(cookie));
+    }
+
+    private Cookie getSafeCookie(String name, String value) {
+        Cookie cookie = null;
+        try {
+            String safeName = InputValidationUtil.getSafeHeaderName(name);
+            String safeValue = InputValidationUtil.getSafeCookieHeaderValue(value);
+            cookie = new Cookie(safeName, safeValue);
+        } catch (Exception e) {
+            try {
+                grizzlyResponse.sendError(403, "Forbidden");
+            } catch (Exception ex) {
+                // just return
+            }
+        }
+        return cookie;
+    }
+
+    /**
+     * Adds JSESSIONID cookie whose value includes jvmRoute if necessary.
+     */
+    private void addSessionCookieWithJReplica(Request request, StandardContext ctx,
+            Session sess) {
+
+        String replicaLocation = null;
+
+        if (sess != null) {
+            replicaLocation = (String)sess.getNote(Globals.JREPLICA_SESSION_NOTE);
+            sess.removeNote(Globals.JREPLICA_SESSION_NOTE);
+        }
+
+        if (replicaLocation != null) {
+            Cookie cookie = getSafeCookie(Globals.JREPLICA_COOKIE_NAME, replicaLocation);
+            request.configureSessionCookie(cookie);
+            if (request.isRequestedSessionIdFromCookie()) {
+                cookie.setSecure(
+                    request.isRequestedSessionIdFromSecureCookie());
+            }
+            grizzlyResponse.addHeader(SET_COOKIE_HEADER,
+                               response.getCookieString(cookie));
+        }
+
+    }
+
+    /**
+     * Adds JSESSIONSSOVERSION cookie
+     */
+    private void addSsoVersionCookie(Request request, StandardContext ctx) {
+
+        Long ssoVersion = (Long)request.getNote(
+                org.apache.catalina.authenticator.Constants.REQ_SSO_VERSION_NOTE);
+        if (ssoVersion != null) {
+            Cookie cookie = new Cookie(
+                    org.apache.catalina.authenticator.Constants.SINGLE_SIGN_ON_VERSION_COOKIE,
+                    ssoVersion.toString());
+            cookie.setMaxAge(-1);
+            cookie.setPath("/");
+            StandardHost host = (StandardHost) ctx.getParent();
+            HttpServletRequest hreq =
+                    (HttpServletRequest)request.getRequest();
+            if (host != null) {
+                host.configureSingleSignOnCookieSecure(cookie, hreq);
+                host.configureSingleSignOnCookieHttpOnly(cookie);
+            } else {
+                cookie.setSecure(hreq.isSecure());
+            }
+
+            grizzlyResponse.addHeader(SET_COOKIE_HEADER,
+                    response.getCookieString(cookie));
+        }
+    }
+
+    private void addPersistedSessionCookie(Request request, StandardContext ctx,
+            Session sess) throws IOException {
+
+        if (sess == null) {
+            return;
+        }
+        Cookie cookie = ctx.getManager().toCookie(sess);
+        if (cookie != null) {
+            request.configureSessionCookie(cookie);
+            grizzlyResponse.addHeader(SET_COOKIE_HEADER,
+                    response.getCookieString(cookie));
+        }
+    }
+
+    private void addJrouteCookie(Request request, StandardContext ctx,
+            Session sess) {
+
+        String jrouteId = request.getHeader(Constants.PROXY_JROUTE);
+
+        if (jrouteId == null) {
+            // Load-balancer plugin is not front-ending this instance
+            return;
+        }
+
+        if (sess == null) {
+            // No session exists
+            return;
+        }
+
+        if (request.getJrouteId() == null
+                || !request.getJrouteId().equals(jrouteId)) {
+            // Initial request or failover
+            Cookie cookie = getSafeCookie(Constants.JROUTE_COOKIE, jrouteId);
+            request.configureSessionCookie(cookie);
+            if (request.isRequestedSessionIdFromCookie()) {
+                /*
+                 * Have the JSESSIONIDVERSION cookie inherit the
+                 * security setting of the JSESSIONID cookie to avoid
+                 * session loss when switching from HTTPS to HTTP,
+                 * see IT 7414
+                 */
+                cookie.setSecure(
+                        request.isRequestedSessionIdFromSecureCookie());
+            }
+            grizzlyResponse.addHeader(SET_COOKIE_HEADER,
+                    response.getCookieString(cookie));
+        }
+
+    }
+
+    // START PWC 6512276
+    /**
+     * Are there any pending writes waiting to be flushed?
+     */
+    public boolean hasData() {
+        
+        return !suspended && (!grizzlyResponse.isCommitted() ||
+                grizzlyOutputBuffer.getBufferedDataSize() > 0);
+    }
+    // END PWC 6512276
+    
+    private class SessionCookieChecker implements org.glassfish.grizzly.http.io.OutputBuffer.LifeCycleListener {
+
+        @Override
+        public void onCommit() throws IOException {
+            grizzlyOutputBuffer.removeLifeCycleListener(this);
+            addSessionCookies();
+        }
+    }
+    
+    class WriteHandlerImpl implements WriteHandler {
+        private WriteListener writeListener = null;
+        private volatile boolean disable = false;
+
+        private WriteHandlerImpl(WriteListener listener) {
+            writeListener = listener;
+        }
+
+        public void onWritePossible() {
+            if (disable) {
+                return;
+            }
+            if (!Boolean.TRUE.equals(CAN_WRITE_SCOPE.get())) {
+                processWritePossible();
+            } else {
+                AsyncContextImpl.getExecutorService().execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        processWritePossible();
+                    }
+                });
+            }
+        }
+
+        private void processWritePossible() {
+            ClassLoader oldCL;
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
+                oldCL = AccessController.doPrivileged(pa);
+            } else {
+                oldCL = Thread.currentThread().getContextClassLoader();
+            }
+
+            try {
+                Context context = response.getContext();
+                ClassLoader newCL = context.getLoader().getClassLoader();
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(newCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(newCL);
+                }
+
+                synchronized(this) {
+                    prevIsReady = true;
+                    try {
+                        context.fireContainerEvent(
+                            ContainerEvent.BEFORE_WRITE_LISTENER_ON_WRITE_POSSIBLE, writeListener);
+                        writeListener.onWritePossible();
+                    } catch(Throwable t) {
+                        disable = true;
+                        writeListener.onError(t);
+                    } finally {
+                        context.fireContainerEvent(
+                            ContainerEvent.AFTER_WRITE_LISTENER_ON_WRITE_POSSIBLE, writeListener);
+                    }
+                }
+            } finally {
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(oldCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(oldCL);
+                }
+            }
+        }
+
+        public void onError(final Throwable t) {
+            if (disable) {
+                return;
+            }
+            disable = true;
+
+            if (!Boolean.TRUE.equals(CAN_WRITE_SCOPE.get())) {
+                processError(t);
+            } else {
+                AsyncContextImpl.getExecutorService().execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        processError(t);
+                    }
+                });
+            }
+        }
+
+        private void processError(final Throwable t) {
+            ClassLoader oldCL;
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
+                oldCL = AccessController.doPrivileged(pa);
+            } else {
+                oldCL = Thread.currentThread().getContextClassLoader();
+            }
+
+            try {
+                Context context = response.getContext();
+                ClassLoader newCL = context.getLoader().getClassLoader();
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(newCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(newCL);
+                }
+
+                synchronized(this) {
+                    try {
+                        context.fireContainerEvent(
+                            ContainerEvent.BEFORE_WRITE_LISTENER_ON_ERROR, writeListener);
+                        writeListener.onError(t);
+                    } finally {
+                        context.fireContainerEvent(
+                            ContainerEvent.AFTER_WRITE_LISTENER_ON_ERROR, writeListener);
+                    }
+                }
+            } finally {
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(oldCL);
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(oldCL);
+                }
+            }
+        }
+    }
+
+    private static class PrivilegedSetTccl implements PrivilegedAction<Void> {
+
+        private ClassLoader cl;
+
+        PrivilegedSetTccl(ClassLoader cl) {
+            this.cl = cl;
+        }
+
+        @Override
+        public Void run() {
+            Thread.currentThread().setContextClassLoader(cl);
+            return null;
+        }
+    }
+
+    private static class PrivilegedGetTccl
+            implements PrivilegedAction<ClassLoader> {
+
+        @Override
+        public ClassLoader run() {
+            return Thread.currentThread().getContextClassLoader();
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ProtocolHandler.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ProtocolHandler.java
new file mode 100644
index 0000000..0c9dcb3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ProtocolHandler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2007-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import org.glassfish.grizzly.http.server.HttpHandler;
+
+/**
+ * Abstract the protocol implementation, including threading, etc.
+ * Processor is single threaded and specific to stream-based protocols,
+ * will not fit Jk protocols like JNI.
+ * <p/>
+ * This is the main interface to be implemented by a coyote connector.
+ * (In contrast, Adapter is the main interface to be implemented by a
+ * coyote servlet container.)
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ * @see HttpHandler
+ */
+public interface ProtocolHandler {
+    /**
+     * Pass config info.
+     */
+    void setAttribute(String name, Object value);
+
+    Object getAttribute(String name);
+
+    /**
+     * The adapter, used to call the connector.
+     */
+    void setHandler(HttpHandler handler);
+
+    HttpHandler getHandler();
+
+    /**
+     * Init the protocol.
+     */
+    void init() throws Exception;
+
+    /**
+     * Start the protocol.
+     */
+    void start() throws Exception;
+
+    void destroy() throws Exception;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Request.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Request.java
new file mode 100644
index 0000000..4e40e2f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Request.java
@@ -0,0 +1,4614 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import java.io.BufferedReader;
+import java.io.CharConversionException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URLDecoder;
+import java.net.UnknownHostException;
+import java.nio.charset.UnsupportedCharsetException;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.security.auth.Subject;
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletResponse;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpUpgradeHandler;
+import javax.servlet.http.Part;
+import javax.servlet.http.PushBuilder;
+import javax.servlet.http.WebConnection;
+
+import com.sun.appserv.ProxyHandler;
+import javax.servlet.http.MappingMatch;
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.Context;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.authenticator.SingleSignOn;
+import org.apache.catalina.core.ApplicationPushBuilder;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.core.StandardWrapper;
+import org.apache.catalina.fileupload.Multipart;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.session.PersistentManagerBase;
+import org.apache.catalina.session.StandardSession;
+import org.apache.catalina.util.*;
+import org.glassfish.grizzly.Buffer;
+import org.glassfish.grizzly.CompletionHandler;
+import org.glassfish.grizzly.EmptyCompletionHandler;
+import org.glassfish.grizzly.http.server.Response.SuspendedContextImpl;
+import org.glassfish.grizzly.http.server.TimeoutHandler;
+import org.glassfish.grizzly.http.server.util.MappingData;
+import org.glassfish.grizzly.http.server.util.RequestUtils;
+import org.glassfish.grizzly.http.util.B2CConverter;
+import org.glassfish.grizzly.http.util.ByteChunk;
+import org.glassfish.grizzly.http.util.CharChunk;
+import org.glassfish.grizzly.http.util.DataChunk;
+import org.glassfish.grizzly.http.util.FastHttpDateFormat;
+import org.glassfish.grizzly.http.util.MessageBytes;
+import org.glassfish.grizzly.http2.Http2Stream;
+import org.glassfish.grizzly.memory.Buffers;
+import org.glassfish.grizzly.utils.Charsets;
+import org.glassfish.web.valve.GlassFishValve;
+import javax.servlet.http.HttpServletMapping;
+import static com.sun.logging.LogCleanerUtil.getSafeHeaderValue;
+
+/**
+ * Wrapper object for the Coyote request.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @author Rajiv Mordani
+ * @version $Revision: 1.67.2.9 $ $Date: 2008/04/17 18:37:34 $
+ */
+public class Request
+        implements HttpRequest, HttpServletRequest {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------------- Statics
+    /**
+     * Descriptive information about this Request implementation.
+     */
+    protected static final String info =
+            "org.apache.catalina.connector.Request/1.0";
+
+    /**
+     * Whether or not to enforce scope checking of this object.
+     */
+    private static boolean enforceScope = false;
+
+    /**
+     * The notes key for the password used to authenticate this user.
+     */
+    private static final String SESS_PASSWORD_NOTE =
+      "org.apache.catalina.session.PASSWORD";
+
+
+    /**
+     * The notes key for the username used to authenticate this user.
+     */
+    private static final String SESS_USERNAME_NOTE =
+      "org.apache.catalina.session.USERNAME";
+
+    // END CR 6309511
+    // START OF SJSAS 6231069
+    /*
+    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 set of SimpleDateFormat formats to use in getDateHeader().
+     *
+     * Notice that because SimpleDateFormat is not thread-safe, we can't
+     * declare formats[] as a static variable.
+     */
+    private static ThreadLocal staticDateFormats = new ThreadLocal() {
+
+        @Override
+        protected Object initialValue() {
+            SimpleDateFormat[] f = new SimpleDateFormat[3];
+            f[0] = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz",
+                    Locale.US);
+            f[1] = new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz",
+                    Locale.US);
+            f[2] = new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US);
+            return f;
+        }
+    };
+    protected SimpleDateFormat formats[];
+    // END OF SJSAS 6231069    
+    /**
+     * ThreadLocal object to keep track of the reentrancy status of each thread.
+     * It contains a byte[] object whose single element is either 0 (initial
+     * value or no reentrancy), or 1 (current thread is reentrant). When a
+     * thread exits the implies method, byte[0] is alwasy reset to 0.
+     */
+    private static ThreadLocal reentrancyStatus;
+
+    static {
+        reentrancyStatus = new ThreadLocal() {
+
+            @Override
+            protected synchronized Object initialValue() {
+                return new byte[]{0};
+            }
+        };
+    }
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of cookies associated with this Request.
+     */
+    protected ArrayList<Cookie> cookies = new ArrayList<Cookie>();
+    /**
+     * The default Locale if none are specified.
+     */
+    protected static final Locale defaultLocale = Locale.getDefault();
+    /**
+     * The attributes associated with this Request, keyed by attribute name.
+     */
+    protected Map<String, Object> attributes = new HashMap<String, Object>();
+    /**
+     * The preferred Locales associated with this Request.
+     */
+    protected ArrayList<Locale> locales = new ArrayList<Locale>();
+    /**
+     * Internal notes associated with this request by Catalina components
+     * and event listeners.
+     */
+    private Map<String, Object> notes = new HashMap<String, Object>();
+    /**
+     * Authentication type.
+     */
+    protected String authType = null;
+    /**
+     * The current dispatcher type.
+     */
+    protected Object dispatcherTypeAttr = null;
+    /**
+     * The associated input buffer.
+     */
+    protected InputBuffer inputBuffer = new InputBuffer();
+    /**
+     * ServletInputStream.
+     */
+    protected CoyoteInputStream inputStream =
+            new CoyoteInputStream(inputBuffer);
+    /**
+     * Reader.
+     */
+    protected CoyoteReader reader = new CoyoteReader(inputBuffer);
+    /**
+     * Using stream flag.
+     */
+    protected boolean usingInputStream = false;
+    /**
+     * Using writer flag.
+     */
+    protected boolean usingReader = false;
+    /**
+     * User principal.
+     */
+    protected Principal userPrincipal = null;
+    /**
+     * Session parsed flag.
+     */
+    protected boolean sessionParsed = false;
+
+    protected boolean parametersProcessed = false;
+    /**
+     * Cookies parsed flag.
+     */
+    protected boolean cookiesParsed = false;
+    /**
+     * Secure flag.
+     */
+    protected boolean secure = false;
+    /**
+     * The Subject associated with the current AccessControllerContext
+     */
+    protected Subject subject = null;
+    /**
+     * Post data buffer.
+     */
+    protected static final int CACHED_POST_LEN = 8192;
+    protected byte[] postData = null;
+    /**
+     * Hash map used in the getParametersMap method.
+     */
+    protected ParameterMap<String, String[]> parameterMap = new ParameterMap<String, String[]>();
+    /**
+     * The currently active session for this request.
+     */
+    protected Session session = null;
+    /**
+     * The current request dispatcher path.
+     */
+    protected Object requestDispatcherPath = 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;
+
+    /**
+     * The requested session version (if any) for this request.
+     */
+    protected String requestedSessionVersion = null;
+
+    private boolean isRequestedSessionIdFromSecureCookie;
+
+    // The requested session cookie path, see IT 7426
+    protected String requestedSessionCookiePath;
+
+    // Temporary holder for URI params from which session id is parsed
+    protected CharChunk uriParamsCC = new CharChunk();
+    /**
+     * 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();
+    /**
+     * Local port
+     */
+    protected int localPort = -1;
+    /**
+     * Remote address.
+     */
+    protected String remoteAddr = null;
+    /**
+     * Remote host.
+     */
+    protected String remoteHost = null;
+    /**
+     * Remote port
+     */
+    protected int remotePort = -1;
+    /**
+     * Local address
+     */
+    protected String localName = null;
+    /**
+     * Local address
+     */
+    protected String localAddr = null;
+    /** After the request is mapped to a ServletContext, we can also
+     * map it to a logger.
+     */
+    /* CR 6309511
+    protected Log log=null;
+     */
+    // START CR 6415120
+    /**
+     * Whether or not access to resources in WEB-INF or META-INF needs to be
+     * checked.
+     */
+    protected boolean checkRestrictedResources = true;
+    // END CR 6415120
+    /**
+     * has findSession been called and returned null already
+     */
+    private boolean unsuccessfulSessionFind = false;
+
+    /*
+     * Are we supposed to honor the unsuccessfulSessionFind flag?
+     * WS overrides this to false.
+     */
+    protected boolean checkUnsuccessfulSessionFind = true;
+    // START S1AS 4703023
+    /**
+     * The current application dispatch depth.
+     */
+    private int dispatchDepth = 0;
+    /**
+     * The maximum allowed application dispatch depth.
+     */
+    private static int maxDispatchDepth = Constants.DEFAULT_MAX_DISPATCH_DEPTH;
+    // END S1AS 4703023
+    // START SJSAS 6346226
+    private String jrouteId;
+    // END SJSAS 6346226
+    // START GlassFish 896
+    private SessionTracker sessionTracker = new SessionTracker();
+    // END GlassFish 896
+    // START GlassFish 1024
+    private boolean isDefaultContext = false;
+    // END GlassFish 1024
+    private String requestURI = null;
+    /**
+     * Coyote request.
+     */
+    protected org.glassfish.grizzly.http.server.Request coyoteRequest;
+    /**
+     * The facade associated with this request.
+     */
+    protected RequestFacade facade = null;
+    /**
+     * Request facade that masks the fact that a request received
+     * at the root context was mapped to a default-web-module (if such a
+     * mapping exists).
+     * For example, its getContextPath() will return "/" rather than the
+     * context root of the default-web-module.
+     */
+    protected RequestFacade defaultContextMaskingFacade = null;
+    /**
+     * The response with which this request is associated.
+     */
+    protected org.apache.catalina.Response response = null;
+    /**
+     * Associated Catalina connector.
+     */
+    protected org.apache.catalina.Connector connector;
+    /**
+     * Mapping data.
+     */
+    protected MappingData mappingData = new MappingData();
+    /**
+     * Associated wrapper.
+     */
+    protected Wrapper wrapper = null;
+    /**
+     * Filter chain associated with the request.
+     */
+    protected FilterChain filterChain = null;
+    /**
+     * Async operation
+     */
+    // Async mode is supported by default for a request, unless the request
+    // has passed a filter or servlet that does not support async
+    // operation, in which case async operation will be disabled
+    private boolean isAsyncSupported = true;
+    private AtomicBoolean asyncStarted = new AtomicBoolean();
+    private AsyncContextImpl asyncContext;
+    private Thread asyncStartedThread;
+
+    /**
+     * Multi-Part support
+     */
+    private Multipart multipart;
+    /**
+     * Associated context.
+     */
+    protected Context context = null;
+    protected ServletContext servletContext = null;
+    // Associated StandardHost valve for error dispatches
+    protected GlassFishValve hostValve;
+
+    /*
+     * The components of the request path
+     */
+    private String contextPath;
+    private String servletPath;
+    private String pathInfo;
+
+    private boolean initRequestFacadeHelper = false;
+
+    // Allow Grizzly to auto detect a remote close connection.
+    public final static boolean discardDisconnectEvent =
+            Boolean.getBoolean("org.glassfish.grizzly.discardDisconnect");
+
+    /*
+     * An upgrade request is received
+     */
+    private boolean upgrade = false;
+
+    private boolean afterService = false;
+    private boolean resume = false;
+
+    /*
+     * The HttpUpgradeHandler to be used for upgrade request
+     */
+    private HttpUpgradeHandler httpUpgradeHandler;
+
+    /*
+     * The WebConnection associated with upgraded request
+     */
+    private WebConnection webConnection;
+
+
+    // ----------------------------------------------------------- Constructor
+    public Request() {
+        // START OF SJSAS 6231069
+        formats = (SimpleDateFormat[]) staticDateFormats.get();
+        formats[0].setTimeZone(TimeZone.getTimeZone("GMT"));
+        formats[1].setTimeZone(TimeZone.getTimeZone("GMT"));
+        formats[2].setTimeZone(TimeZone.getTimeZone("GMT"));
+        // END OF SJSAS 6231069
+    }
+
+    // --------------------------------------------------------- Public Methods
+    /**
+     * Set the Coyote request.
+     *
+     * @param grizzlyRequest The Coyote request
+     */
+    public void setCoyoteRequest(org.glassfish.grizzly.http.server.Request grizzlyRequest) {
+        this.coyoteRequest = grizzlyRequest;
+        inputBuffer.setRequest(grizzlyRequest);
+        inputBuffer.setRequest(this);
+    }
+
+    /**
+     * Get the Coyote request.
+     */
+    public org.glassfish.grizzly.http.server.Request getCoyoteRequest() {
+        return this.coyoteRequest;
+    }
+
+    /**
+     * Set whether or not to enforce scope checking of this object.
+     */
+    public static void setEnforceScope(boolean enforce) {
+        enforceScope = enforce;
+    }
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    @Override
+    public void recycle() {
+
+        if (isAsyncStarted()) {
+            return;
+        }
+
+        context = null;
+        servletContext = null;
+        contextPath = null;
+        servletPath = null;
+        pathInfo = null;
+        wrapper = null;
+
+        dispatcherTypeAttr = null;
+        requestDispatcherPath = null;
+
+        authType = null;
+        requestURI = null;
+        inputBuffer.recycle();
+        usingInputStream = false;
+        usingReader = false;
+        userPrincipal = null;
+        subject = null;
+        sessionParsed = false;
+        parametersProcessed = false;
+        cookiesParsed = false;
+        locales.clear();
+        localesParsed = false;
+        secure = false;
+        remoteAddr = null;
+        remoteHost = null;
+        remotePort = -1;
+        localPort = -1;
+        localAddr = null;
+        localName = null;
+        multipart = null;
+        jrouteId = null;
+        upgrade = false;
+        afterService = false;
+        resume = false;
+
+        attributes.clear();
+        notes.clear();
+        cookies.clear();
+
+        unsuccessfulSessionFind = false;
+
+        if (session != null) {
+            session.endAccess();
+        }
+        session = null;
+        requestedSessionCookie = false;
+        requestedSessionId = null;
+        requestedSessionCookiePath = null;
+        requestedSessionURL = false;
+        uriParamsCC.recycle();
+
+        // START GlassFish 896
+        sessionTracker.reset();
+        // END GlassFish 896
+
+        /* CR 6309511
+        log = null;
+         */
+        dispatchDepth = 0; // S1AS 4703023
+
+        parameterMap.setLocked(false);
+        parameterMap.clear();
+
+        mappingData.recycle();
+
+        initRequestFacadeHelper = false;
+        if (enforceScope) {
+            if (facade != null) {
+                facade.clear();
+                facade = null;
+            }
+            if (defaultContextMaskingFacade != null) {
+                defaultContextMaskingFacade.clear();
+                defaultContextMaskingFacade = null;
+            }
+            if (inputStream != null) {
+                inputStream.clear();
+                inputStream = null;
+            }
+            if (reader != null) {
+                reader.clear();
+                reader = null;
+            }
+        }
+
+        /*
+         * Clear and reinitialize all async related instance vars
+         */
+        if (asyncContext != null) {
+            asyncContext.clear();
+            asyncContext = null;
+        }
+        isAsyncSupported = true;
+        asyncStarted.set(false);
+        asyncStartedThread = null;
+    }
+
+    /**
+     * Set the unsuccessfulSessionFind flag.
+     *
+     * @param unsuccessfulSessionFind
+     */
+    public void setUnsuccessfulSessionFind(boolean unsuccessfulSessionFind) {
+        this.unsuccessfulSessionFind = unsuccessfulSessionFind;
+    }
+
+    /**
+     * Get the unsuccessfulSessionFind flag.
+     */
+    public boolean getUnsuccessfulSessionFind() {
+        return this.unsuccessfulSessionFind;
+    }
+
+    public void setUpgrade(boolean upgrade) {
+        this.upgrade = upgrade;
+    }
+
+    public boolean isUpgrade() {
+        return upgrade;
+    }
+
+    public HttpUpgradeHandler getHttpUpgradeHandler() {
+        return httpUpgradeHandler;
+    }
+
+    // -------------------------------------------------------- Request Methods
+    /**
+     * Return the authorization credentials sent with this request.
+     */
+    @Override
+    public String getAuthorization() {
+        return coyoteRequest.getHeader(Constants.AUTHORIZATION_HEADER);
+    }
+
+    /**
+     * Return the Connector through which this Request was received.
+     */
+    @Override
+    public org.apache.catalina.Connector getConnector() {
+        return connector;
+    }
+
+    /**
+     * Set the Connector through which this Request was received.
+     *
+     * @param connector The new connector
+     */
+    @Override
+    public void setConnector(org.apache.catalina.Connector connector) {
+        this.connector = connector;
+    }
+
+    /**
+     * Return the Context within which this Request is being processed.
+     */
+    @Override
+    public Context getContext() {
+        return 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
+     */
+    @Override
+    public void setContext(Context context) {
+        this.context = context;
+        if (context != null) {
+            this.servletContext = context.getServletContext();
+            Pipeline p = context.getParent().getPipeline();
+            if (p != null) {
+                hostValve = p.getBasic();
+            }
+            try {
+                String reqEncoding = this.servletContext.getRequestCharacterEncoding();
+                if (reqEncoding != null) {
+                    setCharacterEncoding(reqEncoding);
+                }
+                String resEncoding = this.servletContext.getResponseCharacterEncoding();
+                if (resEncoding != null) {
+                    getResponse().getResponse().setCharacterEncoding(resEncoding);
+                }
+            } catch(UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        // START GlassFish 896
+        initSessionTracker();
+        // END GlassFish 896
+    }
+
+    // START GlassFish 1024
+    /**
+     * @param isDefaultContext true if this request was mapped to a context
+     * with an empty context root that is backed by the vitual server's
+     * default-web-module
+     */
+    public void setDefaultContext(boolean isDefaultContext) {
+        this.isDefaultContext = isDefaultContext;
+    }
+    // END GlassFish 1024
+
+    /**
+     * Get filter chain associated with the request.
+     */
+    @Override
+    public FilterChain getFilterChain() {
+        return filterChain;
+    }
+
+    /**
+     * Set filter chain associated with the request.
+     * 
+     * @param filterChain new filter chain
+     */
+    @Override
+    public void setFilterChain(FilterChain filterChain) {
+        this.filterChain = filterChain;
+    }
+
+    /**
+     * Return the Host within which this Request is being processed.
+     */
+    @Override
+    public Host getHost() {
+        return (Host) mappingData.host;
+    }
+
+    /**
+     * Set the Host within which this Request is being processed.  This
+     * must be called as soon as the appropriate Host is identified, and
+     * before the Request is passed to a context.
+     *
+     * @param host The newly associated Host
+     */
+    @Override
+    public void setHost(Host host) {
+        mappingData.host = host;
+    }
+
+    /**
+     * Return descriptive information about this Request implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    @Override
+    public String getInfo() {
+        return info;
+    }
+
+    @Override
+    public HttpServletMapping getHttpServletMapping() {
+        HttpServletMapping result;
+        
+        result = new MappingImpl(mappingData);
+        return result;
+    }
+    /**
+     * Return mapping data.
+     */
+    public MappingData getMappingData() {
+        return mappingData;
+    }
+
+    /**
+     * Set the mapping data for this Request.
+     */
+    public void setMappingData(MappingData mappingData) {
+        this.mappingData = mappingData;
+    }
+
+    /**
+     * Update this instance with the content of the {@link MappingData}
+     * {@link MappingData}
+     */
+    public void updatePaths(MappingData md) {
+        /*
+         * Save the path components of this request, in order for them to
+         * survive when the mapping data get recycled as the request
+         * returns to the container after it has been put into async mode.
+         * This is required to satisfy the requirements of subsequent async
+         * dispatches (or error dispatches, if the async operation times out,
+         * and no async listeners have been registered that could be notified
+         * at their onTimeout method)
+         */
+        pathInfo = md.pathInfo.toString();
+        servletPath = md.wrapperPath.toString();
+        contextPath = md.contextPath.toString();
+    }
+
+    /**
+     * Gets the <code>ServletRequest</code> for which this object
+     * is the facade. This method must be implemented by a subclass.
+     */
+    @Override
+    public HttpServletRequest getRequest() {
+        return getRequest(false);
+    }
+
+    /**
+     * Gets the <code>ServletRequest</code> for which this object
+     * is the facade. This method must be implemented by a subclass.
+     *
+     * @param maskDefaultContextMapping true if the fact that a request
+     * received at the root context was mapped to a default-web-module will
+     * be masked, false otherwise
+     */
+    @Override
+    public HttpServletRequest getRequest(boolean maskDefaultContextMapping) {
+        if (!maskDefaultContextMapping || !isDefaultContext) {
+            if (facade == null) {
+                facade = new RequestFacade(this);
+            }
+
+            if (!initRequestFacadeHelper) {
+                attributes.put(Globals.REQUEST_FACADE_HELPER,
+                        facade.getRequestFacadeHelper());
+                initRequestFacadeHelper = true;
+            }
+            return facade;
+        } else {
+            if (defaultContextMaskingFacade == null) {
+                defaultContextMaskingFacade = new RequestFacade(this, true);
+            }
+
+            if (!initRequestFacadeHelper) {
+                attributes.put(Globals.REQUEST_FACADE_HELPER,
+                        defaultContextMaskingFacade.getRequestFacadeHelper());
+                initRequestFacadeHelper = true;
+            }
+
+            return defaultContextMaskingFacade;
+        }
+    }
+
+    /**
+     * Return the Response with which this Request is associated.
+     */
+    @Override
+    public org.apache.catalina.Response getResponse() {
+        return this.response;
+    }
+
+    /**
+     * Set the Response with which this Request is associated.
+     *
+     * @param response The new associated response
+     */
+    @Override
+    public void setResponse(org.apache.catalina.Response response) {
+        this.response = response;
+        if (response instanceof Response) {
+            sessionTracker.setResponse((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.
+     */
+    @Override
+    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
+     */
+    @Override
+    public void setSocket(Socket socket) {
+        this.socket = socket;
+        remoteHost = null;
+        remoteAddr = null;
+        remotePort = -1;
+        localPort = -1;
+        localAddr = null;
+        localName = null;
+    }
+
+    /**
+     * Return the input stream associated with this Request.
+     */
+    @Override
+    public InputStream getStream() {
+        if (inputStream == null) {
+            inputStream = new CoyoteInputStream(inputBuffer);
+        }
+        return inputStream;
+    }
+
+    /**
+     * Set the input stream associated with this Request.
+     *
+     * @param stream The new input stream
+     */
+    @Override
+    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 converter
+     */
+    protected void setURIConverter(B2CConverter URIConverter) {
+        this.URIConverter = URIConverter;
+    }
+
+    /**
+     * Return the Wrapper within which this Request is being processed.
+     */
+    @Override
+    public Wrapper getWrapper() {
+        return 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
+     */
+    @Override
+    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
+     */
+    @Override
+    public ServletInputStream createInputStream()
+            throws IOException {
+        if (inputStream == null) {
+            inputStream = new CoyoteInputStream(inputBuffer);
+        }
+        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
+     */
+    @Override
+    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
+     */
+    @Override
+    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<String> 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
+     */
+    @Override
+    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
+     */
+    @Override
+    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
+     */
+    @Override
+    public void setContentLength(int length) {
+        coyoteRequest.getRequest().setContentLength(length);
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public void setContentType(String type) {
+        // Not used
+    }
+
+    /**
+     * Set the protocol name and version associated with this Request.
+     *
+     * @param protocol Protocol name and version
+     */
+    @Override
+    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
+     */
+    @Override
+    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 value to be returned by <code>isSecure()</code>
+     * for this Request.
+     *
+     * @param secure The new isSecure value
+     */
+    @Override
+    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
+     */
+    @Override
+    public void setServerName(String name) {
+        coyoteRequest.setServerName(name);
+    }
+
+    /**
+     * Set the port number of the server to process this request.
+     *
+     * @param port The server port
+     */
+    @Override
+    public void setServerPort(int port) {
+        coyoteRequest.setServerPort(port);
+    }
+
+    // START CR 6415120
+    /**
+     * Set whether or not access to resources under WEB-INF or META-INF
+     * needs to be checked.
+     */
+    @Override
+    public void setCheckRestrictedResources(boolean check) {
+        this.checkRestrictedResources = check;
+    }
+
+    /**
+     * Return whether or not access to resources under WEB-INF or META-INF
+     * needs to be checked.
+     */
+    @Override
+    public boolean getCheckRestrictedResources() {
+        return this.checkRestrictedResources;
+    }
+    // END CR 6415120
+
+    // ------------------------------------------------- ServletRequest Methods
+    /**
+     * Return the specified request attribute if it exists; otherwise, return
+     * <code>null</code>.
+     *
+     * @param name Name of the request attribute to return
+     */
+    @Override
+    public Object getAttribute(String name) {
+
+        if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
+            return dispatcherTypeAttr == null
+                    ? DispatcherType.REQUEST
+                    : dispatcherTypeAttr;
+        } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
+            return requestDispatcherPath == null
+                    ? getRequestPathMB().toString()
+                    : requestDispatcherPath.toString();
+        } else if (name.equals(Globals.CONSTRAINT_URI)) {
+            return getRequestPathMB() != null
+                    ? getRequestPathMB().toString() : null;
+        }
+
+        Object attr = attributes.get(name);
+
+        if (attr != null) {
+            return attr;
+        }
+
+        attr = coyoteRequest.getAttribute(name);
+        if (attr != null) {
+            return attr;
+        }
+        if (Globals.SSL_CERTIFICATE_ATTR.equals(name)) {
+            // @TODO Implement SSL rehandshake
+            RequestUtils.populateCertificateAttribute(coyoteRequest);
+            attr = getAttribute(Globals.CERTIFICATES_ATTR);
+            if (attr != null) {
+                attributes.put(name, attr);
+            }
+        } else if (isSSLAttribute(name)) {
+            /* SJSAS 6419950
+            coyoteRequest.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE,
+            coyoteRequest);
+            attr = coyoteRequest.getAttribute(Globals.CERTIFICATES_ATTR);
+            if( attr != null) {
+            attributes.put(Globals.CERTIFICATES_ATTR, attr);
+            }
+            attr = coyoteRequest.getAttribute(Globals.CIPHER_SUITE_ATTR);
+            if(attr != null) {
+            attributes.put(Globals.CIPHER_SUITE_ATTR, attr);
+            }
+            attr = coyoteRequest.getAttribute(Globals.KEY_SIZE_ATTR);
+            if(attr != null) {
+            attributes.put(Globals.KEY_SIZE_ATTR, attr);
+            }
+             */
+            // START SJSAS 6419950
+            RequestUtils.populateSSLAttributes(coyoteRequest);
+            // END SJSAS 6419950
+            attr = attributes.get(name);
+        }
+        return attr;
+    }
+
+    /**
+     * Test if a given name is one of the special Servlet-spec SSL attributes.
+     */
+    static boolean isSSLAttribute(String name) {
+        return Globals.CERTIFICATES_ATTR.equals(name) ||
+                Globals.CIPHER_SUITE_ATTR.equals(name) ||
+                Globals.KEY_SIZE_ATTR.equals(name) ||
+                Globals.SSL_SESSION_ID_ATTR.equals(name);
+    }
+
+    /**
+     * Return the names of all request attributes for this Request, or an
+     * empty <code>Enumeration</code> if there are none.
+     */
+    @Override
+    public Enumeration<String> getAttributeNames() {
+        if (isSecure()) {
+            populateSSLAttributes();
+        }
+        return new Enumerator<String>(attributes.keySet(), true);
+    }
+
+    /**
+     * Return the character encoding for this Request.
+     */
+    @Override
+    public String getCharacterEncoding() {
+        return coyoteRequest.getCharacterEncoding();
+    }
+
+    /**
+     * Return the content length for this Request.
+     */
+    @Override
+    public int getContentLength() {
+        return coyoteRequest.getContentLength();
+    }
+
+    /**
+     * Return the content length for this Request.
+     */
+    @Override
+    public long getContentLengthLong() {
+        return coyoteRequest.getContentLengthLong();
+    }
+
+    /**
+     * Return the content type for this Request.
+     */
+    @Override
+    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
+     */
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+
+        if (usingReader) {
+            throw new IllegalStateException(rb.getString(LogFacade.GETREADER_BEEN_CALLED_EXCEPTION));
+        }
+
+        usingInputStream = true;
+        if (inputStream == null) {
+            inputStream = new CoyoteInputStream(inputBuffer);
+        }
+        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.
+     */
+    @Override
+    public Locale getLocale() {
+        return coyoteRequest.getLocale();
+//        if (!localesParsed) {
+//            parseLocales();
+//        }
+//
+//        if (locales.size() > 0) {
+//            return 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.
+     */
+    @Override
+    public Enumeration<Locale> getLocales() {
+        return new Enumerator<Locale>(coyoteRequest.getLocales());
+//        if (!localesParsed) {
+//            parseLocales();
+//        }
+//
+//        if (locales.size() > 0) {
+//            return (new Enumerator<Locale>(locales));
+//        }
+//        ArrayList<Locale> results = new ArrayList<Locale>();
+//        results.add(defaultLocale);
+//        return (new Enumerator<Locale>(results));
+    }
+
+    private void processParameters() {
+        if (parametersProcessed) {
+            return;
+        }
+        getCharacterEncoding();
+        if (isMultipartConfigured() && getMethod().equalsIgnoreCase("POST")) {
+            String contentType = getContentType();
+            if (contentType != null && 
+                        contentType.startsWith("multipart/form-data")) {
+                getMultipart().init();
+            }
+        }
+        parametersProcessed = true;
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public String getParameter(String name) {
+
+/*
+        if (!requestParametersParsed) {
+            parseRequestParameters();
+        }
+*/
+        processParameters();
+
+        return coyoteRequest.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.
+     */
+    @Override
+    public Map<String, String[]> getParameterMap() {
+
+        if (parameterMap.isLocked()) {
+            return parameterMap;
+        }
+
+        Enumeration<String> e = getParameterNames();
+        while (e.hasMoreElements()) {
+            String name = e.nextElement();
+            String[] values = getParameterValues(name);
+            parameterMap.put(name, values);
+        }
+
+        parameterMap.setLocked(true);
+
+        return parameterMap;
+
+    }
+
+    /**
+     * Return the names of all defined request parameters for this request.
+     */
+    @Override
+    public Enumeration<String> getParameterNames() {
+/*
+        if (!requestParametersParsed) {
+            parseRequestParameters();
+        }
+*/
+        processParameters();
+
+        return new Enumerator<String>(coyoteRequest.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
+     */
+    @Override
+    public String[] getParameterValues(String name) {
+/*
+        if (!requestParametersParsed) {
+            parseRequestParameters();
+        }
+*/
+        processParameters();
+
+        return coyoteRequest.getParameterValues(name);
+    }
+
+    /**
+     * Return the protocol and version used to make this Request.
+     */
+    @Override
+    public String getProtocol() {
+        return coyoteRequest.getProtocol().getProtocolString();
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public BufferedReader getReader() throws IOException {
+
+        if (usingInputStream) {
+            throw new IllegalStateException(rb.getString(LogFacade.GETINPUTSTREAM_BEEN_CALLED_EXCEPTION));
+        }
+
+        usingReader = true;
+        try {
+            inputBuffer.checkConverter();
+        } catch (UnsupportedCharsetException uce) {
+            UnsupportedEncodingException uee =
+                    new UnsupportedEncodingException(uce.getMessage());
+            uee.initCause(uce);
+            throw uee;
+        }
+
+        if (reader == null) {
+            reader = new CoyoteReader(inputBuffer);
+        }
+        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>.
+     */
+    @Override
+    public String getRealPath(String path) {
+        if (servletContext == null) {
+            return null;
+        } else {
+            try {
+                return servletContext.getRealPath(path);
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Return the remote IP address making this Request.
+     */
+    @Override
+    public String getRemoteAddr() {
+        if (remoteAddr == null) {
+
+            // START SJSAS 6347215
+            if (connector.getAuthPassthroughEnabled() && connector.getProxyHandler() != null) {
+                remoteAddr = connector.getProxyHandler().getRemoteAddress(
+                        getRequest());
+                if (remoteAddr == null && log.isLoggable(Level.FINEST)) {
+                    log.log(Level.FINEST, LogFacade.UNABLE_DETERMINE_CLIENT_ADDRESS);
+                }
+                return remoteAddr;
+            }
+            // END SJSAS 6347215
+
+            if (socket != null) {
+                InetAddress inet = socket.getInetAddress();
+                remoteAddr = inet.getHostAddress();
+            } else {
+//                coyoteRequest.action(ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE, coyoteRequest);
+                remoteAddr = coyoteRequest.getRemoteAddr();
+            }
+        }
+        return remoteAddr;
+    }
+
+    /**
+     * Return the remote host name making this Request.
+     */
+    @Override
+    public String getRemoteHost() {
+        if (remoteHost == null) {
+            if (!connector.getEnableLookups()) {
+                remoteHost = getRemoteAddr();
+                // START SJSAS 6347215
+            } else if (connector.getAuthPassthroughEnabled() && connector.getProxyHandler() != null) {
+                String addr =
+                        connector.getProxyHandler().getRemoteAddress(getRequest());
+                if (addr != null) {
+                    try {
+                        remoteHost = InetAddress.getByName(addr).getHostName();
+                    } catch (UnknownHostException e) {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_RESOLVE_IP_EXCEPTION), addr);
+                        log.log(Level.WARNING, msg, e);
+                    }
+                } else if (log.isLoggable(Level.FINEST)) {
+                    log.log(Level.FINEST, LogFacade.UNABLE_DETERMINE_CLIENT_ADDRESS);
+                }
+                // END SJSAS 6347215
+            } else if (socket != null) {
+                InetAddress inet = socket.getInetAddress();
+                remoteHost = inet.getHostName();
+            } else {
+//                coyoteRequest.action(ActionCode.ACTION_REQ_HOST_ATTRIBUTE, coyoteRequest);
+                remoteHost = coyoteRequest.getRemoteHost();
+            }
+        }
+        return remoteHost;
+    }
+
+    /**
+     * Returns the Internet Protocol (IP) source port of the client
+     * or last proxy that sent the request.
+     */
+    @Override
+    public int getRemotePort() {
+        if (remotePort == -1) {
+            if (socket != null) {
+                remotePort = socket.getPort();
+            } else {
+//                coyoteRequest.action(ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE, coyoteRequest);
+                remotePort = coyoteRequest.getRemotePort();
+            }
+        }
+        return remotePort;
+    }
+
+    /**
+     * Returns the host name of the Internet Protocol (IP) interface on
+     * which the request was received.
+     */
+    @Override
+    public String getLocalName() {
+        if (localName == null) {
+            if (socket != null) {
+                InetAddress inet = socket.getLocalAddress();
+                localName = inet.getHostName();
+            } else {
+//                coyoteRequest.action(ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE, coyoteRequest);
+                localName = coyoteRequest.getLocalName();
+            }
+        }
+        return localName;
+    }
+
+    /**
+     * Returns the Internet Protocol (IP) address of the interface on
+     * which the request  was received.
+     */
+    @Override
+    public String getLocalAddr() {
+        if (localAddr == null) {
+            if (socket != null) {
+                InetAddress inet = socket.getLocalAddress();
+                localAddr = inet.getHostAddress();
+            } else {
+//                coyoteRequest.action(ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE, coyoteRequest);
+                localAddr = coyoteRequest.getLocalAddr();
+            }
+        }
+        return localAddr;
+    }
+
+    /**
+     * Returns the Internet Protocol (IP) port number of the interface
+     * on which the request was received.
+     */
+    @Override
+    public int getLocalPort() {
+        if (localPort == -1) {
+            if (socket != null) {
+                localPort = socket.getLocalPort();
+            } else {
+//                coyoteRequest.action(ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE, coyoteRequest);
+                localPort = coyoteRequest.getLocalPort();
+            }
+        }
+        return localPort;
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        if (servletContext == null) {
+            return null;
+        }
+
+        // If the path is already context-relative, just pass it through
+        if (path == null) {
+            return null;
+        } else if (path.startsWith("/")) {
+            return servletContext.getRequestDispatcher(path);
+        }
+
+        // Convert a request-relative path to a context-relative one
+        String servPath = (String) getAttribute(
+                RequestDispatcher.INCLUDE_SERVLET_PATH);
+        if (servPath == null) {
+            servPath = getServletPath();
+        }
+
+        // Add the path info, if there is any
+        String pInfo = getPathInfo();
+        String requestPath = null;
+
+        if (pInfo == null) {
+            requestPath = servPath;
+        } else {
+            requestPath = servPath + pInfo;
+        }
+
+        int pos = requestPath.lastIndexOf('/');
+        String relative = null;
+        if (pos >= 0) {
+            relative = requestPath.substring(0, pos + 1) + path;
+        } else {
+            relative = requestPath + path;
+        }
+
+        return servletContext.getRequestDispatcher(relative);
+
+    }
+
+    /**
+     * Return the scheme used to make this Request.
+     */
+    @Override
+    public String getScheme() {
+        // START S1AS 6170450
+        if (getConnector() != null && getConnector().getAuthPassthroughEnabled()) {
+            ProxyHandler proxyHandler = getConnector().getProxyHandler();
+            if (proxyHandler != null && proxyHandler.getSSLKeysize(getRequest()) > 0) {
+                return "https";
+            }
+        }
+        // END S1AS 6170450
+
+        return coyoteRequest.getScheme();
+    }
+
+    /**
+     * Return the server name responding to this Request.
+     */
+    @Override
+    public String getServerName() {
+        return coyoteRequest.getServerName();
+    }
+
+    /**
+     * Return the server port responding to this Request.
+     */
+    @Override
+    public int getServerPort() {
+        /* SJSAS 6586658
+        return (coyoteRequest.getServerPort());
+         */
+        // START SJSAS 6586658
+        if (isSecure()) {
+            String host = getHeader("host");
+            if (host != null && host.indexOf(':') == -1) {
+                // No port number provided with Host header, use default
+                return 443;
+            } else {
+                return coyoteRequest.getServerPort();
+            }
+        } else {
+            return coyoteRequest.getServerPort();
+        }
+        // END SJSAS 6586658
+    }
+
+    /**
+     * Was this request received on a secure connection?
+     */
+    @Override
+    public boolean isSecure() {
+        return secure;
+    }
+
+    /**
+     * Remove the specified request attribute if it exists.
+     *
+     * @param name Name of the request attribute to remove
+     */
+    @Override
+    public void removeAttribute(String name) {
+        Object value = null;
+        boolean found = attributes.containsKey(name);
+        if (found) {
+            value = attributes.get(name);
+            attributes.remove(name);
+        } else {
+            return;
+        }
+
+        // Notify interested application event listeners
+        List<EventListener> listeners = context.getApplicationEventListeners();
+        if (listeners.isEmpty()) {
+            return;
+        }
+        ServletRequestAttributeEvent event =
+                new ServletRequestAttributeEvent(servletContext, getRequest(),
+                name, value);
+        Iterator<EventListener> iter = listeners.iterator();
+        while (iter.hasNext()) {
+            EventListener eventListener = iter.next();
+            if (!(eventListener instanceof ServletRequestAttributeListener)) {
+                continue;
+            }
+            ServletRequestAttributeListener listener =
+                    (ServletRequestAttributeListener) eventListener;
+            try {
+                listener.attributeRemoved(event);
+            } catch (Throwable t) {
+                log(rb.getString(LogFacade.ATTRIBUTE_EVENT_LISTENER_EXCEPTION), t);
+                // Error valve will pick this exception up and display it to user
+                attributes.put(RequestDispatcher.ERROR_EXCEPTION, t);
+            }
+        }
+    }
+
+    /**
+     * Set the specified request attribute to the specified value.
+     *
+     * @param name Name of the request attribute to set
+     * @param value The associated value
+     */
+    @Override
+    public void setAttribute(String name, Object value) {
+
+        // Name cannot be null
+        if (name == null) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_ATTRIBUTE_NAME_EXCEPTION));
+        }
+
+        // Null value is the same as removeAttribute()
+        if (value == null) {
+            removeAttribute(name);
+            return;
+        }
+
+        if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
+            dispatcherTypeAttr = value;
+            return;
+        } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
+            requestDispatcherPath = value;
+            return;
+        }
+
+        boolean replaced = false;
+
+        // Do the security check before any updates are made
+        if (Globals.IS_SECURITY_ENABLED &&
+                name.equals("org.apache.tomcat.sendfile.filename")) {
+            // Use the canonical file name to avoid any possible symlink and
+            // relative path issues
+            String canonicalPath;
+            try {
+                canonicalPath = new File(value.toString()).getCanonicalPath();
+            } catch (IOException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_DETERMINE_CANONICAL_NAME), value);
+                throw new SecurityException(msg, e);
+            }
+            // Sendfile is performed in Tomcat's security context so need to
+            // check if the web app is permitted to access the file while still
+            // in the web app's security context
+            System.getSecurityManager().checkRead(canonicalPath);
+            // Update the value so the canonical path is used
+            value = canonicalPath;
+        }
+
+        Object oldValue = attributes.put(name, value);
+        if (oldValue != null) {
+            replaced = true;
+        }
+
+        // START SJSAS 6231069
+        // Pass special attributes to the ngrizzly layer
+        if (name.startsWith("grizzly.")) {
+            coyoteRequest.setAttribute(name, value);
+        }
+        // END SJSAS 6231069
+
+        // Notify interested application event listeners
+        List<EventListener> listeners = context.getApplicationEventListeners();
+        if (listeners.isEmpty()) {
+            return;
+        }
+        ServletRequestAttributeEvent event = null;
+        if (replaced) {
+            event = new ServletRequestAttributeEvent(servletContext,
+                    getRequest(), name,
+                    oldValue);
+        } else {
+            event = new ServletRequestAttributeEvent(servletContext,
+                    getRequest(), name,
+                    value);
+        }
+
+        Iterator<EventListener> iter = listeners.iterator();
+        while (iter.hasNext()) {
+            EventListener eventListener = iter.next();
+            if (!(eventListener instanceof ServletRequestAttributeListener)) {
+                continue;
+            }
+            ServletRequestAttributeListener listener =
+                    (ServletRequestAttributeListener) eventListener;
+            try {
+                if (replaced) {
+                    listener.attributeReplaced(event);
+                } else {
+                    listener.attributeAdded(event);
+                }
+            } catch (Throwable t) {
+                log(rb.getString(LogFacade.ATTRIBUTE_EVENT_LISTENER_EXCEPTION), t);
+                // Error valve will pick this exception up and display it to user
+                attributes.put(RequestDispatcher.ERROR_EXCEPTION, t);
+            }
+        }
+    }
+
+    /**
+     * 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>. Otherwise, it has no
+     * effect.
+     * 
+     * @param enc      <code>String</code> containing the name of
+     *                 the character encoding.
+     * @throws         UnsupportedEncodingException if this
+     *                 ServletRequest is still in a state where a
+     *                 character encoding may be set, but the specified
+     *                 encoding is invalid
+     *
+     * @since Servlet 2.3
+     */
+    @Override
+    public void setCharacterEncoding(String enc)
+            throws UnsupportedEncodingException {
+
+        // START SJSAS 4936855
+        if (parametersProcessed || usingReader) {
+            String contextName =
+                getContext() != null ? getContext().getName() : "UNKNOWN";
+            log.log(Level.WARNING, LogFacade.UNABLE_SET_REQUEST_CHARS, new Object[] {enc, contextName});
+            return;
+        }
+        // END SJSAS 4936855
+
+        // Ensure that the specified encoding is valid
+        byte buffer[] = new byte[1];
+        buffer[0] = (byte) 'a';
+
+        // START S1AS 6179607: Workaround for 6181598. Workaround should be
+        // removed once the underlying issue in J2SE has been fixed.
+        /*
+         * String dummy = new String(buffer, enc);
+         */
+        // END S1AS 6179607
+        // START S1AS 6179607
+        final byte[] finalBuffer = buffer;
+        final String finalEnc = enc;
+        if (Globals.IS_SECURITY_ENABLED) {
+            try {
+                AccessController.doPrivileged(new PrivilegedExceptionAction<String>() {
+
+                    @Override
+                    public String run() throws UnsupportedEncodingException {
+                        return new String(finalBuffer, RequestUtil.lookupCharset(finalEnc));
+                    }
+                });
+            } catch (PrivilegedActionException pae) {
+                throw (UnsupportedEncodingException) pae.getCause();
+            }
+        } else {
+            new String(buffer, RequestUtil.lookupCharset(enc));
+        }
+        // END S1AS 6179607
+
+        // Save the validated encoding
+        coyoteRequest.setCharacterEncoding(enc);
+
+    }
+
+    // START S1AS 4703023
+    /**
+     * Static setter method for the maximum dispatch depth
+     */
+    public static void setMaxDispatchDepth(int depth) {
+        maxDispatchDepth = depth;
+    }
+
+    public static int getMaxDispatchDepth() {
+        return maxDispatchDepth;
+    }
+
+    /**
+     * Increment the depth of application dispatch
+     */
+    public int incrementDispatchDepth() {
+        return ++dispatchDepth;
+    }
+
+    /**
+     * Decrement the depth of application dispatch
+     */
+    public int decrementDispatchDepth() {
+        return --dispatchDepth;
+    }
+
+    /**
+     * Check if the application dispatching has reached the maximum
+     */
+    public boolean isMaxDispatchDepthReached() {
+        return dispatchDepth > maxDispatchDepth;
+    }
+    // END S1AS 4703023
+
+    // ---------------------------------------------------- HttpRequest Methods
+    @Override
+    public boolean authenticate(HttpServletResponse response)
+            throws IOException, ServletException {
+
+        //Issue 9650 - COmmenting this as required
+      /*  if (getUserPrincipal() != null) {
+        throw new ServletException("Attempt to re-login while the " +
+        "user identity already exists");
+        }*/
+
+        if (context == null) {//TODO: throw an exception
+            throw new ServletException("Internal error: Context null");
+        }
+
+        final AuthenticatorBase authBase = (AuthenticatorBase) context.getAuthenticator();
+
+        if (authBase == null) {
+            throw new ServletException("Internal error: Authenticator null");
+        }
+
+        byte[] alreadyCalled = (byte[]) reentrancyStatus.get();
+        if (alreadyCalled[0] == 1) {
+            //Re-entrancy from a JSR 196  module, so call the authenticate directly
+            try {
+                return authBase.authenticate(this, (HttpResponse) getResponse(),
+                        context.getLoginConfig());
+            } catch (Exception ex) {
+                throw new ServletException("Exception thrown while attempting to authenticate", ex);
+            }
+
+        } else {
+            //No re-entrancy, so call invokeAuthenticateDelegate to check if 
+            //JSR196 module is present
+            alreadyCalled[0] = 1;
+            try {
+                final Realm realm = context.getRealm();
+                final Request req = this;
+                if (realm == null) {
+                    throw new ServletException("Internal error: realm null");
+                }
+                try {
+                    if (Globals.IS_SECURITY_ENABLED) {
+                        Boolean ret = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+                            @Override
+                            public Boolean run() {
+                                try {
+                                    return Boolean.valueOf(realm.invokeAuthenticateDelegate(req, (HttpResponse) getResponse(), context, (AuthenticatorBase) authBase, true));
+                                } catch (IOException ex) {
+                                    throw new RuntimeException("Exception thrown while attempting to authenticate", ex);
+                                }
+                            }
+                        });
+                        return ret.booleanValue();
+                    } else {
+                        return realm.invokeAuthenticateDelegate(req, (HttpResponse) getResponse(), context, (AuthenticatorBase) authBase, true);
+                    }
+
+                } catch (Exception ex) {
+                    throw new ServletException("Exception thrown while attempting to authenticate", ex);
+                }
+
+            } finally {
+                //Reset the threadlocal re-entrancy check variable
+                alreadyCalled[0] = 0;
+            }
+        }
+    }
+
+    @Override
+    public void login(final String username, final String password)
+            throws ServletException {
+        login(username, password != null ? password.toCharArray() : null);
+    }
+
+    public void login(final String username, final char[] password)
+            throws ServletException {
+        final Realm realm = context.getRealm();
+        if (realm != null && realm.isSecurityExtensionEnabled(getServletContext())) {
+            throw new ServletException
+               (rb.getString(LogFacade.LOGIN_WITH_AUTH_CONFIG));
+ 	}
+        
+        if (getAuthType() != null || getRemoteUser() != null ||
+                getUserPrincipal() != null) {
+            throw new ServletException(
+                    rb.getString(LogFacade.ALREADY_AUTHENTICATED));
+        }
+
+        if (context.getAuthenticator() == null) {
+            throw new ServletException(rb.getString(LogFacade.NO_AUTHENTICATOR));
+        }
+
+        context.getAuthenticator().login(username, password, this);
+    }
+
+    @Override
+    public void logout() throws ServletException {
+
+        Realm realm = (context == null ? null : context.getRealm());
+        if (realm == null) {
+            if (getUserPrincipal() != null || getAuthType() != null) {
+                throw new ServletException(
+                        rb.getString(LogFacade.INTERNAL_LOGOUT_ERROR));
+            }
+            return;
+        }
+        /*
+         * Pass the request (this).
+         */
+        realm.logout(this);
+    }
+
+    /**
+     * Add a Cookie to the set of Cookies associated with this Request.
+     *
+     * @param cookie The new cookie
+     */
+    @Override
+    public void addCookie(Cookie cookie) {
+
+        // For compatibility only
+        if (!cookiesParsed) {
+            parseCookies();
+        }
+
+        cookies.add(cookie);
+    }
+
+    /**
+     * Add a Header to the set of Headers associated with this Request.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    @Override
+    public void addHeader(String name, String value) {
+        coyoteRequest.getRequest().getHeaders().addValue(name).setString(value);
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    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
+     */
+    @Override
+    public void addParameter(String name, String values[]) {
+        coyoteRequest.addParameter(name, values);
+    }
+
+    /**
+     * Clear the collection of Cookies associated with this Request.
+     */
+    @Override
+    public void clearCookies() {
+        cookiesParsed = true;
+        cookies.clear();
+    }
+
+    /**
+     * Clear the collection of Headers associated with this Request.
+     */
+    @Override
+    public void clearHeaders() {
+        coyoteRequest.getRequest().getHeaders().recycle();
+    }
+
+    /**
+     * Clear the collection of Locales associated with this Request.
+     */
+    @Override
+    public void clearLocales() {
+        locales.clear();
+    }
+
+    /**
+     * Clear the collection of parameters associated with this Request
+     * and reset the query string encoding charset.
+     */
+    @Override
+    public void clearParameters() {
+        coyoteRequest.getParameters().recycle();
+        coyoteRequest.getParameters().setQueryStringEncoding(
+                Charsets.lookupCharset(getConnector().getURIEncoding()));
+    }
+
+    @Override
+    public void replayPayload(byte[] payloadByteArray) {
+        if (payloadByteArray == null) {
+            return;
+        }
+
+        coyoteRequest.replayPayload(Buffers.wrap(
+                coyoteRequest.getContext().getMemoryManager(), payloadByteArray));
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public void setAuthType(String type) {
+        this.authType = type;
+    }
+
+    /**
+     * Set the HTTP request method used for this Request.
+     *
+     * <p>Used by FBL when the original request is restored after
+     * successful authentication.
+     *
+     * @param method The request method
+     */
+    @Override
+    public void setMethod(String method) {
+        coyoteRequest.setMethod(method);
+    }
+
+    /**
+     * Sets the query string for this Request.
+     *
+     * <p>Used by FBL when the original request is restored after
+     * successful authentication.
+     *
+     * @param query The query string
+     */
+    @Override
+    public void setQueryString(String query) {
+        coyoteRequest.setQueryString(query);
+    }
+
+    /**
+     * Set the path information for this Request.
+     *
+     * @param pathInfo The path information
+     */
+    @Override
+    public void setPathInfo(String pathInfo) {
+        mappingData.pathInfo.setString(pathInfo);
+        this.pathInfo = pathInfo;
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public void setRequestedSessionCookie(boolean flag) {
+        this.requestedSessionCookie = flag;
+    }
+
+    /**
+     * Sets the requested session cookie path, see IT 7426
+     */
+    @Override
+    public void setRequestedSessionCookiePath(String cookiePath) {
+        requestedSessionCookiePath = cookiePath;
+    }
+
+    /**
+     * Set the requested session ID for this request.  This is normally called
+     * by the HTTP Connector, when it parses the request headers.
+     *
+     * This method, which is called when the session id is sent as a cookie,
+     * or when it is encoded in the request URL, removes a jvmRoute
+     * (if present) from the given id.
+     *
+     * @param id The new session id
+     */
+    @Override
+    public void setRequestedSessionId(String id) {
+        requestedSessionId = id;
+        if (id != null && connector.getJvmRoute() != null) {
+            // Remove jvmRoute. The assumption is that the first dot in the
+            // passed in id is the separator between the session id and the
+            // jvmRoute. Therefore, the session id, however generated, must
+            // never have any dots in it if the jvmRoute mechanism has been
+            // enabled. There is no choice to use a separator other than dot
+            // because this is the semantics mandated by the mod_jk LB for it
+            // to function properly.
+            // We can't use StandardContext.getJvmRoute() to determine whether
+            // jvmRoute has been enabled, because this CoyoteRequest may not
+            // have been associated with any context yet.
+            int index = id.indexOf(".");
+            if (index > 0) {
+                requestedSessionId = id.substring(0, index);
+            }
+        }
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    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.
+     *
+     * Used by FBL when restoring original request after successful 
+     * authentication.
+     *
+     * @param uri The request URI
+     */
+    @Override
+    public void setRequestURI(String uri) {
+        coyoteRequest.setRequestURI(uri);
+    }
+
+    /**
+     * Get the decoded request URI.
+     * 
+     * @return the URL decoded request URI
+     */
+    @Override
+    public String getDecodedRequestURI() {
+        return getDecodedRequestURI(false);
+    }
+
+    /**
+     * Gets the decoded request URI.
+     *
+     * @param maskDefaultContextMapping true if the fact that a request
+     * received at the root context was mapped to a default-web-module will
+     * be masked, false otherwise
+     */
+    public String getDecodedRequestURI(boolean maskDefaultContextMapping) {
+        try {
+            if (maskDefaultContextMapping || !isDefaultContext) {
+                return coyoteRequest.getDecodedRequestURI();
+            } else {
+                return getContextPath() + coyoteRequest.getDecodedRequestURI();
+            }
+        } catch (CharConversionException e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Sets the servlet path for this Request.
+     *
+     * @param servletPath The servlet path
+     */
+    @Override
+    public void setServletPath(String servletPath) {
+        mappingData.wrapperPath.setString(servletPath);
+        this.servletPath = servletPath;
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public void setUserPrincipal(Principal principal) {
+
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            HttpSession session = getSession(false);
+            if (subject != null &&
+                !subject.getPrincipals().contains(principal)) {
+                subject.getPrincipals().add(principal);
+            } else if (session != null &&
+                    session.getAttribute(Globals.SUBJECT_ATTR) == null) {
+                subject = new Subject();
+                subject.getPrincipals().add(principal);
+            }
+            if (session != null) {
+                session.setAttribute(Globals.SUBJECT_ATTR, subject);
+            }
+        }
+
+        this.userPrincipal = principal;
+    }
+
+    // --------------------------------------------- HttpServletRequest Methods
+    /**
+     * Return the authentication type used for this Request.
+     */
+    @Override
+    public String getAuthType() {
+        return authType;
+    }
+
+    /**
+     * Return the portion of the request URI used to select the Context
+     * of the Request.
+     */
+    @Override
+    public String getContextPath() {
+        return getContextPath(false);
+    }
+
+    /**
+     * Gets the portion of the request URI used to select the Context
+     * of the Request.
+     *
+     * @param maskDefaultContextMapping true if the fact that a request
+     * received at the root context was mapped to a default-web-module will
+     * be masked, false otherwise
+     */
+    public String getContextPath(boolean maskDefaultContextMapping) {
+        if (isDefaultContext && maskDefaultContextMapping) {
+            return "";
+        } else {
+            return contextPath;
+        }
+    }
+
+    /**
+     * Return the set of Cookies received with this Request.
+     */
+    @Override
+    public Cookie[] getCookies() {
+
+        if (!cookiesParsed) {
+            parseCookies();
+        }
+
+        if (cookies.size() == 0) {
+            return null;
+        }
+
+        return cookies.toArray(new Cookie[cookies.size()]);
+    }
+
+    /**
+     * Set the set of cookies received with this Request.
+     */
+    public void setCookies(Cookie[] cookies) {
+
+        this.cookies.clear();
+        if (cookies != null) {
+            for (int i = 0; i < cookies.length; i++) {
+                this.cookies.add(cookies[i]);
+            }
+        }
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public long getDateHeader(String name) {
+
+        String value = getHeader(name);
+        if (value == null) {
+            return -1L;
+        }
+
+        // Attempt to convert the date header in a variety of formats
+        long result = FastHttpDateFormat.parseDate(value, formats);
+        if (result != -1L) {
+            return result;
+        }
+        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
+     */
+    @Override
+    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
+     */
+    @Override
+    public Enumeration<String> getHeaders(String name) {
+        return new Enumerator<String>(coyoteRequest.getHeaders(name).iterator());
+    }
+
+    /**
+     * Return the names of all headers received with this request.
+     */
+    @Override
+    public Enumeration<String> getHeaderNames() {
+        return new Enumerator<String>(coyoteRequest.getHeaderNames().iterator());
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public int getIntHeader(String name) {
+
+        String value = getHeader(name);
+        if (value == null) {
+            return -1;
+        } else {
+            return Integer.parseInt(value);
+        }
+
+    }
+
+    @Override
+    public Map<String, String> getTrailerFields() {
+        return coyoteRequest.getTrailers();
+    }
+
+    @Override
+    public boolean isTrailerFieldsReady() {
+        return coyoteRequest.areTrailersAvailable();
+    }
+
+    /**
+     * Return the HTTP request method used in this Request.
+     */
+    @Override
+    public String getMethod() {
+        return coyoteRequest.getMethod().getMethodString();
+    }
+
+    /**
+     * Return the path information associated with this Request.
+     */
+    @Override
+    public String getPathInfo() {
+        return pathInfo;
+    }
+
+    /**
+     * Return the extra path information for this request, translated
+     * to a real path.
+     */
+    @Override
+    public String getPathTranslated() {
+
+        if (servletContext == null) {
+            return null;
+        }
+
+        if (getPathInfo() == null) {
+            return null;
+        } else {
+            return servletContext.getRealPath(getPathInfo());
+        }
+
+    }
+
+    @Override
+    public PushBuilder newPushBuilder() {
+        Http2Stream http2Stream = null;
+        if (coyoteRequest != null) {
+            http2Stream = (Http2Stream)coyoteRequest.getAttribute(Http2Stream.HTTP2_STREAM_ATTRIBUTE);
+        }
+        if (http2Stream != null && http2Stream.isPushEnabled()) {
+            return new ApplicationPushBuilder(this);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Return the query string associated with this request.
+     */
+    @Override
+    public String getQueryString() {
+        String queryString = coyoteRequest.getQueryString();
+
+        if (queryString == null || "".equals(queryString)) {
+            return null;
+        } else {
+            return queryString;
+        }
+    }
+
+    /**
+     * Return the name of the remote user that has been authenticated
+     * for this Request.
+     */
+    @Override
+    public String getRemoteUser() {
+        if (userPrincipal != null) {
+            return userPrincipal.getName();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Get the request path.
+     * 
+     * @return the request path
+     */
+    @Override
+    public DataChunk getRequestPathMB() {
+        return mappingData.requestPath;
+    }
+
+    /**
+     * Return the session identifier included in this request, if any.
+     */
+    @Override
+    public String getRequestedSessionId() {
+        return requestedSessionId;
+    }
+
+    /**
+     * Return the request URI for this request.
+     */
+    @Override
+    public String getRequestURI() {
+        return getRequestURI(false);
+    }
+
+    /**
+     * Gets the request URI for this request.
+     *
+     * @param maskDefaultContextMapping true if the fact that a request
+     * received at the root context was mapped to a default-web-module will
+     * be masked, false otherwise
+     */
+    public String getRequestURI(boolean maskDefaultContextMapping) {
+        if (maskDefaultContextMapping) {
+            return coyoteRequest.getRequestURI();
+        } else {
+            if (requestURI == null) {
+                // START GlassFish 1024
+                if (isDefaultContext) {
+                    requestURI = getContextPath() +
+                        coyoteRequest.getRequestURI();
+                } else {
+                    // END GlassFish 1024
+                    requestURI = coyoteRequest.getRequestURI();
+                    // START GlassFish 1024
+                }
+                // END GlassFish 1024
+            }
+            return requestURI;
+        }
+    }
+
+    /**
+     * 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
+     */
+    @Override
+    public StringBuffer getRequestURL() {
+        return getRequestURL(false);
+    }
+
+    public StringBuffer getRequestURL(boolean maskDefaultContextMapping) {
+        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(maskDefaultContextMapping));
+
+        return url;
+
+    }
+
+    /**
+     * Return the portion of the request URI used to select the servlet
+     * that will process this request.
+     */
+    @Override
+    public String getServletPath() {
+        return servletPath;
+    }
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary.
+     */
+    @Override
+    public HttpSession getSession() {
+        Session session = doGetSession(true);
+        if (session != null) {
+            return session.getSession();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary and requested.
+     *
+     * @param create Create a new session if one does not exist
+     */
+    @Override
+    public HttpSession getSession(boolean create) {
+        Session session = doGetSession(create);
+        if (session != null) {
+            return session.getSession();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * set the session - this method is not for general use
+     *
+     * @param newSess the new session
+     */
+    public void setSession(Session newSess) {
+        session = newSess;
+    }
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request came from a cookie.
+     */
+    @Override
+    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.
+     */
+    @Override
+    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.
+     */
+    @Override
+    public boolean isRequestedSessionIdFromUrl() {
+        return isRequestedSessionIdFromURL();
+    }
+
+    /**
+     * Marks (or unmarks) this request as having a JSESSIONID cookie
+     * that is marked as secure
+     *
+     * @param secure true if this request has a JSESSIONID cookie that is
+     * marked as secure, false otherwise
+     */
+    public void setRequestedSessionIdFromSecureCookie(boolean secure) {
+        isRequestedSessionIdFromSecureCookie = secure;
+    }
+
+
+    /**
+     * @return true if this request contains a JSESSIONID cookie that is
+     * marked as secure, false otherwise
+     */
+    public boolean isRequestedSessionIdFromSecureCookie() {
+        return isRequestedSessionIdFromSecureCookie;
+    }
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request identifies a valid session.
+     */
+    @Override
+    public boolean isRequestedSessionIdValid() {
+        if (requestedSessionId == null) {
+            return false;
+        }
+        if (context == null) {
+            return false;
+        }
+
+        if (session != null &&
+                requestedSessionId.equals(session.getIdInternal())) {
+            return session.isValid();
+        }
+
+        Manager manager = context.getManager();
+        if (manager == null) {
+            return false;
+        }
+        Session localSession = null;
+        try {
+            if (manager.isSessionVersioningSupported()) {
+                localSession = manager.findSession(requestedSessionId,
+                                                   requestedSessionVersion);
+            } else {
+                localSession = manager.findSession(requestedSessionId, this);
+            }
+        } catch (IOException e) {
+            localSession = null;
+        }
+        if (localSession != null && localSession.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
+     */
+    @Override
+    public boolean isUserInRole(String role) {
+
+        // BEGIN RIMOD 4949842
+        /*
+         * Must get userPrincipal through getUserPrincipal(), can't assume
+         * it has already been set since it may be coming from core.
+         */
+        Principal userPrincipal = this.getUserPrincipal();
+        // END RIMOD 4949842
+
+        // Have we got an authenticated principal at all?
+        if (userPrincipal == null) {
+            return false;
+        }
+
+        // Identify the Realm we will use for checking role assignments
+        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);
+
+            //START SJSAS 6232464
+            if (realRole != null &&
+                    //realm.hasRole(userPrincipal, realRole))
+                    realm.hasRole(this, (HttpResponse) response,
+                    userPrincipal, realRole)) {
+                return true;
+            }
+        }
+
+        // Check for a role defined directly as a <security-role>
+
+        //return (realm.hasRole(userPrincipal, role));
+        return realm.hasRole(this, (HttpResponse) response,
+                userPrincipal, role);
+        //END SJSAS 6232464
+    }
+
+    /**
+     * Return the principal that has been authenticated for this Request.
+     */
+    @Override
+    public Principal getUserPrincipal() {
+        return userPrincipal;
+    }
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary.
+     */
+    public Session getSessionInternal() {
+        return doGetSession(true);
+    }
+
+    /**
+     * Gets the session associated with this Request, creating one
+     * if necessary and requested.
+     *
+     * @param create true if a new session is to be created if one does not
+     * already exist, false otherwise
+     */
+    @Override
+    public Session getSessionInternal(boolean create) {
+        return doGetSession(create);
+    }
+
+    /**
+     * Change the session id of the current session associated with this
+     * request and return the new session id. 
+     *
+     * @return the new session id
+     *
+     * @throws IllegalStateException if there is no session associated
+     * with the request
+     *
+     * @since Servlet 3.1
+     */
+    @Override
+    public String changeSessionId() {
+        Manager manager = context.getManager();
+        if (manager == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CHANGE_SESSION_ID_BEEN_CALLED_EXCEPTION));
+        }
+        Session session = getSessionInternal(false);
+        if (session == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CHANGE_SESSION_ID_BEEN_CALLED_EXCEPTION));
+        }
+
+        manager.changeSessionId(session);
+        String newSessionId = session.getId();
+        // This should only ever be called if there was an old session ID but
+        // double check to be sure
+        if (requestedSessionId != null && requestedSessionId.length() > 0) {
+            requestedSessionId = newSessionId;
+        }
+
+        addSessionCookie();
+
+        return newSessionId;
+    }
+
+    /**
+     * This object does not implement a session ID generator. Provide
+     * a dummy implementation so that the default one will be used.
+     */
+    @Override
+    public String generateSessionId() {
+        return null;
+    }
+
+    /**
+     * Gets the servlet context to which this servlet request was last
+     * dispatched.
+     *
+     * @return the servlet context to which this servlet request was last
+     * dispatched
+     */
+    @Override
+    public ServletContext getServletContext() {
+        return servletContext;
+    }
+
+    /**
+     * Create an instance of <code>HttpUpgradeHandler</code> for an given
+     * class and uses it for the http protocol upgrade processing.
+     *
+     * @param handlerClass The <code>HttpUpgradeHandler</code> class used for the upgrade.
+     *
+     * @return an instance of the <code>HttpUpgradeHandler</code>
+     *
+     * @exception IOException if an I/O error occurred during the upgrade
+     * @exception ServletException if the given <tt>clazz</tt> fails to be
+     * instantiated
+     *
+     * @see javax.servlet.http.HttpUpgradeHandler
+     * @see javax.servlet.http.WebConnection
+     *
+     * @since Servlet 3.1
+     */
+    @Override
+    public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass)
+            throws IOException, ServletException {
+        upgrade = true;
+        T handler = null;
+        try {
+            handler = ((StandardContext) getContext()).createHttpUpgradeHandlerInstance(handlerClass);
+        } catch(IOException | ServletException ise) {
+            throw ise;
+        } catch(Throwable t) {
+            throw new ServletException(t);
+        }
+        httpUpgradeHandler = handler;
+        coyoteRequest.getResponse().suspend();
+        return handler;
+    }
+
+    public WebConnection getWebConnection() {
+        return webConnection;
+    }
+
+    public void setWebConnection(WebConnection wc) {
+        webConnection = wc;
+    }
+
+    // ------------------------------------------------------ Protected Methods
+    
+    protected Session 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;
+        }
+
+        // Return the requested session if it exists and is valid
+        Manager manager = context.getManager();
+        if (manager == null) {
+            return null;      // Sessions are not supported
+        }
+        if (requestedSessionId != null) {
+            if (!checkUnsuccessfulSessionFind || !unsuccessfulSessionFind) {
+                try {
+                    if (manager.isSessionVersioningSupported()) {
+                        session = manager.findSession(requestedSessionId,
+                                                      requestedSessionVersion);
+                        //XXX need to revisit
+                        if (session instanceof StandardSession) {
+                            incrementSessionVersion((StandardSession) session,
+                                                    context);
+                        }
+                    } else {
+                        session = manager.findSession(requestedSessionId, this);
+                    }
+                    if (session == null) {
+                        unsuccessfulSessionFind = true;
+                    }
+                } catch (IOException e) {
+                    session = null;
+                }
+            }
+            if (session != null && !session.isValid()) {
+                session = null;
+            }
+            if (session != null) {
+                session.access();
+                return session;
+            }
+        }
+
+        // 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(rb.getString(LogFacade.CANNOT_CREATE_SESSION_EXCEPTION));
+        }
+
+        // START S1AS8PE 4817642
+        if (requestedSessionId != null && context.getReuseSessionID()) {
+            session = manager.createSession(requestedSessionId);
+            if (manager instanceof PersistentManagerBase) {
+                ((PersistentManagerBase) manager).removeFromInvalidatedSessions(requestedSessionId);
+            }
+            // END S1AS8PE 4817642
+            // START GlassFish 896
+        } else if (sessionTracker.getActiveSessions() > 0) {
+            synchronized (sessionTracker) {
+                if (sessionTracker.getActiveSessions() > 0) {
+                    String id = sessionTracker.getSessionId();
+                    session = manager.createSession(id);
+                    if (manager instanceof PersistentManagerBase) {
+                        ((PersistentManagerBase) manager).removeFromInvalidatedSessions(id);
+                    }
+                }
+            }
+            // END GlassFish 896
+            // START S1AS8PE 4817642
+        } else {
+            // END S1AS8PE 4817642
+            // Use the connector's random number generator (if any) to generate
+            // a session ID. Fallback to the default session ID generator if
+            // the connector does not implement one.
+            String id = generateSessionId();
+            if (id != null) {
+                session = manager.createSession(id);
+            } else {
+                session = manager.createSession();
+            }
+            // START S1AS8PE 4817642
+        }
+        // END S1AS8PE 4817642
+
+        StandardHost reqHost = (StandardHost) getHost();
+        if (reqHost != null) {
+            SingleSignOn sso = reqHost.getSingleSignOn();
+            if (sso != null) {
+                String ssoId = (String) getNote(
+                        org.apache.catalina.authenticator.Constants.REQ_SSOID_NOTE);
+                if (ssoId != null) {
+                    long ssoVersion = 0L;
+                    Long ssoVersionObj = (Long)getNote(
+                            org.apache.catalina.authenticator.Constants.REQ_SSO_VERSION_NOTE);
+                    if (ssoVersionObj != null) {
+                        ssoVersion = ssoVersionObj.longValue();
+                    }
+                    sso.associate(ssoId, ssoVersion, session);
+                    removeNote(
+                            org.apache.catalina.authenticator.Constants.REQ_SSOID_NOTE);
+                }
+            }
+        }
+
+        // START GlassFish 896
+        sessionTracker.track(session);
+        // END GlassFish 896
+
+        // Creating a new session cookie based on the newly created session
+        if (session != null && getContext() != null) {
+            if (manager.isSessionVersioningSupported()) {
+                incrementSessionVersion((StandardSession) session, context);
+            }
+
+            addSessionCookie();
+        }
+
+        if (session != null) {
+            session.access();
+            return session;
+        } else {
+            return null;
+        }
+
+    }
+
+    /**
+     * Configures the given JSESSIONID cookie.
+     *
+     * @param cookie The JSESSIONID cookie to be configured
+     */
+    protected void configureSessionCookie(Cookie cookie) {
+        cookie.setHttpOnly(true);
+        cookie.setMaxAge(-1);
+        String contextPath = null;
+        // START GlassFish 1024
+        if (isDefaultContext) {
+            cookie.setPath("/");
+        } else {
+            // END GlassFish 1024
+            if (context != null) {
+                // START OF SJSAS 6231069
+                // contextPath = getContext().getEncodedPath();
+                contextPath = context.getPath();
+                // END OF SJSAS 6231069
+            }
+            if (contextPath != null && contextPath.length() > 0) {
+                cookie.setPath(contextPath);
+            } else {
+                cookie.setPath("/");
+            }
+            // START GlassFish 1024
+        }
+        // END GlassFish 1024
+        if (isSecure()) {
+            cookie.setSecure(true);
+        }
+
+        // Override the default config with servlet context
+        // sessionCookieConfig
+        if (context != null) {
+            SessionCookieConfig sessionCookieConfig =
+                    context.getSessionCookieConfig();
+            if (sessionCookieConfig.getDomain() != null) {
+                cookie.setDomain(sessionCookieConfig.getDomain());
+            }
+            if (sessionCookieConfig.getPath() != null) {
+                cookie.setPath(sessionCookieConfig.getPath());
+            }
+            if (sessionCookieConfig.getComment() != null) {
+                cookie.setVersion(1);
+                cookie.setComment(sessionCookieConfig.getComment());
+            }
+            // do nothing if it is already secure
+            if (!cookie.getSecure()) {
+                cookie.setSecure(sessionCookieConfig.isSecure());
+            }
+            cookie.setHttpOnly(sessionCookieConfig.isHttpOnly());
+            cookie.setMaxAge(sessionCookieConfig.getMaxAge());
+        }
+
+        if (requestedSessionCookiePath != null) {
+            cookie.setPath(requestedSessionCookiePath);
+        }
+    }
+
+    /**
+     * Parse cookies.
+     */
+    protected void parseCookies() {
+
+        cookiesParsed = true;
+
+        org.glassfish.grizzly.http.Cookie[] serverCookies = coyoteRequest.getCookies();
+        int count = serverCookies.length;
+        if (count <= 0) {
+            return;
+        }
+
+        cookies.clear();
+
+        for (int i = 0; i < count; i++) {
+            org.glassfish.grizzly.http.Cookie scookie = serverCookies[i];
+            try {
+                /* GlassFish 898
+                Cookie cookie = new Cookie(scookie.getName().toString(),
+                scookie.getValue().toString());
+                 */
+                // START GlassFish 898
+                Cookie cookie = makeCookie(scookie);
+                // END GlassFish 898
+                cookie.setPath(scookie.getPath());
+                cookie.setVersion(scookie.getVersion());
+                String domain = scookie.getDomain();
+                if (domain != null) {
+                    cookie.setDomain(scookie.getDomain());
+                }
+                cookies.add(cookie);
+            } catch (IllegalArgumentException e) {
+                ; // Ignore bad cookie.
+            }
+        }
+    }
+
+    // START GlassFish 898
+    protected Cookie makeCookie(org.glassfish.grizzly.http.Cookie scookie) {
+        return makeCookie(scookie, false);
+    }
+
+    protected Cookie makeCookie(org.glassfish.grizzly.http.Cookie scookie, boolean decode) {
+
+        String name = scookie.getName();
+        String value = scookie.getValue();
+
+        if (decode) {
+            try {
+                name = URLDecoder.decode(name, "UTF-8");
+                value = URLDecoder.decode(value, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                name = URLDecoder.decode(name);
+                value = URLDecoder.decode(value);
+            }
+        }
+
+        return new Cookie(name, value);
+    }
+    // END GlassFish 898
+
+    /**
+     * Parse request parameters.
+     */
+//    protected void parseRequestParameters() {
+//
+//        /* SJSAS 4936855
+//        requestParametersParsed = true;
+//         */
+//
+//        Parameters parameters = coyoteRequest.getParameters();
+//
+//        // getCharacterEncoding() may have been overridden to search for
+//        // hidden form field containing request encoding
+//        String enc = getCharacterEncoding();
+//        // START SJSAS 4936855
+//        // Delay updating requestParametersParsed to TRUE until
+//        // after getCharacterEncoding() has been called, because
+//        // getCharacterEncoding() may cause setCharacterEncoding() to be
+//        // called, and the latter will ignore the specified encoding if
+//        // requestParametersParsed is TRUE
+//        requestParametersParsed = true;
+//        // END SJSAS 4936855
+//        if (enc != null) {
+//            parameters.setEncoding(enc);
+//            parameters.setQueryStringEncoding(enc);
+//        } else {
+//            parameters.setEncoding(org.glassfish.grizzly.http.server.Constants.DEFAULT_CHARACTER_ENCODING);
+//            parameters.setQueryStringEncoding(org.glassfish.grizzly.http.server.Constants.DEFAULT_CHARACTER_ENCODING);
+//        }
+//
+//        parameters.handleQueryParameters();
+//
+//        if (usingInputStream || usingReader) {
+//            return;
+//        }
+//
+//        if (!"POST".equalsIgnoreCase(getMethod())) {
+//            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) {
+//            int maxPostSize = ((Connector) connector).getMaxPostSize();
+//            if (maxPostSize > 0 && len > maxPostSize) {
+//                log(sm.getString("coyoteRequest.postTooLarge"));
+
+                  //coyoteRequest.postTooLarge=PWC4004: Parameters were not parsed because the size of the posted data was too big.
+                  // Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs.
+//
+//                throw new IllegalStateException("Post too large");
+//            }
+//            try {
+//                /* SJSAS 6346738
+//                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);
+//                }
+//                 */
+//                // START SJSAS 6346738
+//                byte[] formData = getPostBody();
+//                if (formData != null) {
+//                    parameters.processParameters(formData, 0, len);
+//                }
+//                // END SJSAS 6346738
+//            } catch (Throwable t) {
+//                ; // Ignore
+//            }
+//        }
+//
+//    }
+
+    // START SJSAS 6346738
+    /**
+     * Gets the POST body of this request.
+     *
+     * @return The POST body of this request
+     */
+    protected byte[] getPostBody() throws IOException {
+
+        int len = getContentLength();
+        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) {
+            return formData;
+        }
+
+        return null;
+    }
+    // END SJSAS 6346738
+
+    /**
+     * Read post body in an array.
+     */
+    protected int readPostBody(byte body[], int len)
+            throws IOException {
+
+        Buffer b = coyoteRequest.getPostBody(len).duplicate();
+        final int length = b.limit() - b.position();
+        b.get(body, b.position(), length);
+        return length;
+
+    }
+
+    /**
+     * Parse request locales.
+     */
+//    protected void parseLocales() {
+//
+//        localesParsed = true;
+//        for (String value : getHeaders("accept-language")) {
+//            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<Double, ArrayList<Locale>> locales = new TreeMap<Double, ArrayList<Locale>>();
+//
+//        // Preprocess the value to remove all whitespace
+//        int white = value.indexOf(' ');
+//        if (white < 0) {
+//            white = value.indexOf('\t');
+//        }
+//        if (white >= 0) {
+//            StringBuilder sb = new StringBuilder();
+//            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 {
+//                    String strQuality = entry.substring(semi + 3);
+//                    if (strQuality.length() <= 5) {
+//                        quality = Double.parseDouble(strQuality);
+//                    } else {
+//                        quality = 0.0;
+//                    }
+//                } 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 = Double.valueOf(-quality);  // Reverse the order
+//            ArrayList<Locale> values = locales.get(key);
+//            if (values == null) {
+//                values = new ArrayList<Locale>();
+//                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)
+//        for (ArrayList<Locale> list : locales.values()) {
+//            Iterator<Locale> values = list.iterator();
+//            while (values.hasNext()) {
+//                Locale locale = values.next();
+//                addLocale(locale);
+//            }
+//        }
+//
+//    }
+
+    /*
+     * Returns true if the given string is composed of upper- or lowercase
+     * letters only, false otherwise.
+     *
+     * @return true if the given string is composed of upper- or lowercase
+     * letters only, false otherwise.
+     */
+    protected static final boolean isAlpha(String value) {
+
+        if (value == null) {
+            return false;
+        }
+
+        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;
+    }
+
+    /**
+     * Take the session id from Grizzly Request
+     */
+    protected void obtainSessionId() {
+        setRequestedSessionURL(true);
+        setJrouteId(coyoteRequest.getJrouteId());
+        setRequestedSessionId(coyoteRequest.getRequestedSessionId());
+    }
+
+    // START CR 6309511
+    /**
+     * Parse session id in URL.
+     */
+    protected void parseSessionId(String sessionParameterName, CharChunk uriBB) {
+        //START GLASSFISH-15508
+        /*
+        if (coyoteRequest.isRequestedSessionIdFromURL() &&
+                sessionParam.equals(Globals.SESSION_PARAMETER_NAME)) {
+            setRequestedSessionURL(true);
+            setRequestedSessionId(coyoteRequest.getRequestedSessionId());
+            setJrouteId(coyoteRequest.getJrouteId());
+
+            return;
+        }
+
+        sessionParam = ";" + sessionParam + "=";
+        int semicolon = uriBB.indexOf(sessionParam, 0, sessionParam.length(),
+                0);
+        if (semicolon >= 0) {
+
+            // Parse session ID, and extract it from the decoded request URI
+            int start = uriBB.getStart();
+            int end = uriBB.getEnd();
+
+            int sessionIdStart = start + semicolon + sessionParam.length();
+            int semicolon2 = uriBB.indexOf(';', sessionIdStart);
+        */
+        //END GLASSFISH-15508
+            /* SJSAS 6346226
+            if (semicolon2 >= 0) {
+            setRequestedSessionId
+            (new String(uriBB.getBuffer(), sessionIdStart,
+            semicolon2 - semicolon - match.length()));
+            } else {
+            setRequestedSessionId
+            (new String(uriBB.getBuffer(), sessionIdStart,
+            end - sessionIdStart));
+            }
+             */
+            //START GLASSFISH-15508
+            /*
+            // START SJSAS 6346226
+            String sessionId = null;
+            if (semicolon2 >= 0) {
+                sessionId = new String(uriBB.getBuffer(), sessionIdStart,
+                        semicolon2 - semicolon - sessionParam.length());
+            } else {
+                sessionId = new String(uriBB.getBuffer(), sessionIdStart,
+                        end - sessionIdStart);
+            }
+            */
+            //END GLASSFISH-15508
+        
+        // Parse session ID, and extract it from the decoded request URI
+        String sessionParam = ";" + sessionParameterName + "=";
+        String sessionId =
+            parseParameterFromRequestURI(uriBB, sessionParam);
+
+        if (sessionId != null) {
+            // START SJSAS 6346226
+            int jrouteIndex = sessionId.lastIndexOf(':');
+            if (jrouteIndex > 0) {
+                setRequestedSessionId(sessionId.substring(0, jrouteIndex));
+                if (jrouteIndex < sessionId.length() - 1) {
+                    setJrouteId(sessionId.substring(jrouteIndex + 1));
+                }
+            } else {
+                setRequestedSessionId(sessionId);
+            }
+            // END SJSAS 6346226
+
+            setRequestedSessionURL(true);
+
+            /* SJSWS 6376484
+            // Extract session ID from request URI
+            ByteChunk uriBC = coyoteRequest.requestURI().getByteChunk();
+            start = uriBC.getStart();
+            end = uriBC.getEnd();
+            semicolon = uriBC.indexOf(match, 0, match.length(), 0);
+
+            if (semicolon > 0) {
+            sessionIdStart = start + semicolon;
+            semicolon2 = uriBB.indexOf
+            (';', start + semicolon + match.length());
+            uriBC.setEnd(start + semicolon);
+            byte[] buf = uriBC.getBuffer();
+            if (semicolon2 >= 0) {
+            for (int i = 0; i < end - start - semicolon2; i++) {
+            buf[start + semicolon + i]
+            = buf[start + i + semicolon2];
+            }
+            uriBC.setBytes(buf, start, semicolon
+            + (end - start - semicolon2));
+            }
+            }
+             */
+            // START SJSWS 6376484
+            /*
+             * Parse the session id from the encoded URI only if the encoded
+             * URI is not null, to allow for lazy evaluation
+             */
+            if (coyoteRequest.getRequestURI() != null) {
+                removeParameterFromRequestURI(sessionParam);
+            }
+            // END SJSWS 6376484
+
+        } else {
+            setRequestedSessionId(null);
+            setRequestedSessionURL(false);
+        }
+    }
+    // END CR 6309511
+
+    /**
+     * Parses and removes any session version (if present) from the request
+     * URI.
+     *
+     */
+    protected void parseSessionVersion(CharChunk uriCC) {
+        String sessionVersionString =
+            parseParameterFromRequestURI(uriCC, Globals.SESSION_VERSION_PARAMETER);
+
+        if (sessionVersionString != null) {
+            parseSessionVersionString(sessionVersionString);
+
+//            if (!coyoteRequest.requestURI().getByteChunk().isNull()) {
+                removeParameterFromRequestURI(Globals.SESSION_VERSION_PARAMETER);
+//            }
+        }
+    }
+
+    /**
+     * Parses and removes jreplica (if present) from the request URI.
+     */
+    protected void parseJReplica(CharChunk uriCC) {
+        String jreplica =
+            parseParameterFromRequestURI(uriCC, Globals.JREPLICA_PARAMETER);
+
+        if (jreplica != null) {
+            Session session = getSessionInternal(false);
+            if (session != null) {
+                session.setNote(Globals.JREPLICA_SESSION_NOTE, jreplica);
+            }
+//            if (!coyoteRequest.requestURI().getByteChunk().isNull()) {
+                removeParameterFromRequestURI(Globals.JREPLICA_PARAMETER);
+//            }
+        }
+
+    }
+
+    private void addSessionCookie() {
+        if (context != null && context.getCookies() && response != null) {
+            String jvmRoute = ((StandardContext) getContext()).getJvmRoute();
+            /*
+             * Check if context has been configured with jvmRoute for
+             * Apache LB. If it has, do not add the JSESSIONID cookie
+             * here, but rely on OutputBuffer#addSessionCookieWithJvmRoute
+             * to add the jvmRoute enhanced JSESSIONID as a cookie right
+             * before the response is flushed.
+             */
+            if (jvmRoute == null) {
+                Cookie newCookie = new Cookie(
+                        getSafeHeaderValue(getContext().getSessionCookieName()), getSafeHeaderValue(session.getId()));
+                configureSessionCookie(newCookie);
+                ((HttpResponse)response).addSessionCookieInternal(newCookie);
+            }
+        }
+    }
+
+    /**
+     * @param parameter  of the form ";" + parameterName + "="
+     * @return parameterValue
+     */
+    private String parseParameterFromRequestURI(CharChunk uriCC, String parameter) {
+
+        String parameterValue = null;
+
+        int semicolon = uriCC.indexOf(parameter, 0, parameter.length(), 0);
+        if (semicolon >= 0) {
+
+            int start = uriCC.getStart();
+            int end = uriCC.getEnd();
+
+            int parameterStart = start + semicolon + parameter.length();
+            int semicolon2 = uriCC.indexOf(';', semicolon + parameter.length());
+            if (semicolon2 >= 0) {
+                parameterValue = new String(
+                    uriCC.getBuffer(),
+                    parameterStart, 
+                    semicolon2 - semicolon - parameter.length());
+            } else {
+                parameterValue = new String(
+                    uriCC.getBuffer(),
+                    parameterStart, 
+                    end - parameterStart);
+            }
+
+        }
+
+        return parameterValue;
+    }
+
+    // START SJSWS 6376484
+    /**
+     * Removes the session version from the request URI.
+     * @param parameter   of the form ";" + parameterName + "="
+     */
+    private void removeParameterFromRequestURI(String parameter) {
+
+        int semicolon, semicolon2;
+
+        final DataChunk uriBC =
+                coyoteRequest.getRequest().getRequestURIRef().getRequestURIBC();
+        
+//        start = uriBC.getStart();
+//        end = uriBC.getEnd();
+        semicolon = uriBC.indexOf(parameter, 0);
+
+        if (semicolon > 0) {
+            semicolon2 = uriBC.indexOf(';', semicolon + parameter.length());
+
+            final int end;
+            if (semicolon2 >= 0) {
+                end = semicolon2;
+                uriBC.notifyDirectUpdate();
+            } else {
+                end = uriBC.getLength();
+            }
+            
+            uriBC.delete(semicolon, end);
+//            uriBC.setEnd(start + semicolon);
+//            byte[] buf = uriBC.getBuffer();
+//            if (semicolon2 >= 0) {
+//                for (int i = 0; i < end - start - semicolon2; i++) {
+//                    buf[start + semicolon + i] = buf[start + i + semicolon2];
+//                }
+//                uriBC.setBytes(buf, start, semicolon
+//                               + (end - start - semicolon2));
+//            }
+        }
+    }
+    // END SJSWS 6376484
+
+    /*
+     * Parses the given session version string into its components. Each
+     * component is stored as an entry in a HashMap, which maps a context
+     * path to its session version number. The HashMap is stored as a 
+     * request attribute, to make it available to any target contexts to which
+     * this request may be dispatched.
+     *
+     * This method also sets the session version number for the context with
+     * which this request has been associated.
+     */
+    void parseSessionVersionString(String sessionVersionString) {
+        if (sessionVersionString == null || !isSessionVersioningSupported()) {
+            return;
+        }
+
+        HashMap<String, String> sessionVersions =
+            RequestUtil.parseSessionVersionString(sessionVersionString);
+        if (sessionVersions != null) {
+            attributes.put(Globals.SESSION_VERSIONS_REQUEST_ATTRIBUTE,
+                           sessionVersions);
+            if (context != null) {
+                String path = context.getPath();
+                if ("".equals(path)) {
+                    path = "/";
+                }
+                this.requestedSessionVersion = sessionVersions.get(path);
+            }
+        }
+    }
+
+    /**
+     * Parses the value of the JROUTE cookie, if present.
+     */
+    void parseJrouteCookie() {
+        org.glassfish.grizzly.http.Cookie[] serverCookies = coyoteRequest.getCookies();
+        int count = serverCookies.length;
+        if (count <= 0) {
+            return;
+        }
+
+        for (int i = 0; i < count; i++) {
+            org.glassfish.grizzly.http.Cookie scookie = serverCookies[i];
+            if (scookie.getName().equals(Constants.JROUTE_COOKIE)) {
+                setJrouteId(scookie.getValue());
+                break;
+            }
+        }
+    }
+
+    /**
+     * Sets the jroute id of this request.
+     * 
+     * @param jrouteId The jroute id
+     */
+    void setJrouteId(String jrouteId) {
+        this.jrouteId = jrouteId;
+    }
+
+    /**
+     * Gets the jroute id of this request, which may have been 
+     * sent as a separate <code>JROUTE</code> cookie or appended to the
+     * session identifier encoded in the URI (if cookies have been disabled).
+     * 
+     * @return The jroute id of this request, or null if this request does not
+     * carry any jroute id
+     */
+    @Override
+    public String getJrouteId() {
+        return jrouteId;
+    }
+    // END SJSAS 6346226
+
+    // START CR 6309511
+    /**
+     * Parse session id in URL.
+     */
+    protected void parseSessionCookiesId() {
+
+        // If session tracking via cookies has been disabled for the current
+        // context, don't go looking for a session ID in a cookie as a cookie
+        // from a parent context with a session ID may be present which would
+        // overwrite the valid session ID encoded in the URL
+        Context context = (Context) getMappingData().context;
+        if (context != null && !context.getCookies()) {
+            return;
+        }
+
+        // Parse session id from cookies
+        org.glassfish.grizzly.http.Cookie[] serverCookies = coyoteRequest.getCookies();
+        int count = serverCookies.length;
+        if (count <= 0) {
+            return;
+        }
+
+        String sessionCookieName = Globals.SESSION_COOKIE_NAME;
+        if (context != null) {
+            sessionCookieName = context.getSessionCookieName();
+        }
+        for (int i = 0; i < count; i++) {
+            org.glassfish.grizzly.http.Cookie scookie = serverCookies[i];
+            if (scookie.getName().equals(sessionCookieName)) {
+                // Override anything requested in the URL
+                if (!isRequestedSessionIdFromCookie()) {
+                    // Accept only the first session id cookie
+                    setRequestedSessionId(scookie.getValue());
+                    // TODO: Pass cookie path into
+                    // getSessionVersionFromCookie()
+                    String sessionVersionString = getSessionVersionFromCookie();
+                    parseSessionVersionString(sessionVersionString);
+                    setRequestedSessionCookie(true);
+                    // TBD: ServerCookie#getSecure currently always returns
+                    // false. 
+                    setRequestedSessionIdFromSecureCookie(scookie.isSecure());
+                    setRequestedSessionURL(false);
+                } else {
+                    if (!isRequestedSessionIdValid()) {
+                        // Replace the session id until one is valid
+                        setRequestedSessionId(scookie.getValue());
+                        // TODO: Pass cookie path into
+                        // getSessionVersionFromCookie()
+                        String sessionVersionString =
+                            getSessionVersionFromCookie();
+                        parseSessionVersionString(sessionVersionString);
+                    }
+                }
+            }
+        }
+    }
+    // END CR 6309511
+
+    /*
+     * Returns the value of the first JSESSIONIDVERSION cookie, or null
+     * if no such cookie present in the request.
+     *
+     * TODO: Add cookie path argument, and return value of JSESSIONIDVERSION
+     * cookie with the specified path.
+     */
+    private String getSessionVersionFromCookie() {
+        if (!isSessionVersioningSupported()) {
+            return null;
+        }
+
+        org.glassfish.grizzly.http.Cookie[] serverCookies = coyoteRequest.getCookies();
+        int count = serverCookies.length;
+        if (count <= 0) {
+            return null;
+        }
+
+        for (int i = 0; i < count; i++) {
+            org.glassfish.grizzly.http.Cookie scookie = serverCookies[i];
+            if (scookie.getName().equals(
+                                Globals.SESSION_VERSION_COOKIE_NAME)) {
+                return scookie.getValue();
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * @return temporary holder for URI params from which session id is parsed
+     */
+    CharChunk getURIParams() {
+        return uriParamsCC;
+    }
+
+    // START CR 6309511
+    /**
+     * Character conversion of the URI.
+     */
+    protected void convertURI(MessageBytes uri)
+            throws Exception {
+
+        ByteChunk bc = uri.getByteChunk();
+        CharChunk cc = uri.getCharChunk();
+        int length = bc.getLength();
+        cc.allocate(length, -1);
+
+        String enc = connector.getURIEncoding();
+        if (enc != null && !enc.isEmpty() &&
+                !Globals.ISO_8859_1_ENCODING.equalsIgnoreCase(enc)) {
+            B2CConverter conv = getURIConverter();
+            try {
+                if (conv == null) {
+                    conv = new B2CConverter(enc);
+                    setURIConverter(conv);
+                }
+            } catch (IOException e) {
+                // Ignore
+                log.log(Level.SEVERE, LogFacade.INVALID_URI_ENCODING);
+                connector.setURIEncoding(null);
+            }
+            if (conv != null) {
+                try {
+                    conv.convert(bc, cc, cc.getBuffer().length - cc.getEnd());
+                    uri.setChars(cc.getBuffer(), cc.getStart(),
+                            cc.getLength());
+                    return;
+                } catch (IOException e) {
+                    log.log(Level.SEVERE, LogFacade.INVALID_URI_CHAR_ENCODING);
+                    cc.recycle();
+                }
+            }
+        }
+
+        // Default encoding: fast conversion
+        byte[] bbuf = bc.getBuffer();
+        char[] cbuf = cc.getBuffer();
+        int start = bc.getStart();
+        for (int i = 0; i < length; i++) {
+            cbuf[i] = (char) (bbuf[i + start] & 0xff);
+        }
+        uri.setChars(cbuf, 0, length);
+
+    }
+    // END CR 6309511
+
+    @Override
+    public DispatcherType getDispatcherType() {
+        DispatcherType dispatcher = (DispatcherType) getAttribute(
+                Globals.DISPATCHER_TYPE_ATTR);
+        if (dispatcher == null) {
+            dispatcher = DispatcherType.REQUEST;
+        }
+        return dispatcher;
+    }
+
+    /**
+     * Starts async processing on this request.
+     */
+    @Override
+    public AsyncContext startAsync() throws IllegalStateException {
+        return startAsync(getRequest(), getResponse().getResponse(), true);
+    }
+
+    /**
+     * Starts async processing on this request.
+     *
+     * @param servletRequest the ServletRequest with which to initialize
+     * the AsyncContext
+     * @param servletResponse the ServletResponse with which to initialize
+     * the AsyncContext
+     */
+    @Override
+    public AsyncContext startAsync(ServletRequest servletRequest,
+                                   ServletResponse servletResponse)
+            throws IllegalStateException {
+        return startAsync(servletRequest, servletResponse, false);
+    }
+
+    /**
+     * Starts async processing on this request.
+     *
+     * @param servletRequest the ServletRequest with which to initialize
+     * the AsyncContext
+     * @param servletResponse the ServletResponse with which to initialize
+     * the AsyncContext
+     * @param isStartAsyncWithZeroArg true if the zero-arg version of
+     * startAsync was called, false otherwise
+     */
+    private AsyncContext startAsync(ServletRequest servletRequest,
+                ServletResponse servletResponse,
+                boolean isStartAsyncWithZeroArg)
+            throws IllegalStateException {
+
+        if (servletRequest == null || servletResponse == null) {
+            throw new IllegalArgumentException("Null request or response");
+        }
+
+        if (!isAsyncSupported()) {
+            throw new IllegalStateException(rb.getString(LogFacade.REQUEST_WITHIN_SCOPE_OF_FILTER_OR_SERVLET_EXCEPTION));
+        }
+
+        final AsyncContextImpl asyncContextLocal = asyncContext;
+
+        if (asyncContextLocal != null) {
+            if (isAsyncStarted()) {
+                throw new IllegalStateException(rb.getString(LogFacade.START_ASYNC_CALLED_AGAIN_EXCEPTION));
+            }
+            if (asyncContextLocal.isAsyncComplete()) {
+                throw new IllegalStateException(rb.getString(LogFacade.ASYNC_ALREADY_COMPLETE_EXCEPTION));
+            }
+            if (!asyncContextLocal.isStartAsyncInScope()) {
+                throw new IllegalStateException(rb.getString(LogFacade.START_ASYNC_CALLED_OUTSIDE_SCOPE_EXCEPTION));
+            }
+
+            // Reinitialize existing AsyncContext
+            asyncContextLocal.reinitialize(servletRequest, servletResponse,
+                    isStartAsyncWithZeroArg);
+        } else {
+            final AsyncContextImpl asyncContextFinal =
+                    new AsyncContextImpl(this,
+                                         servletRequest,
+                                         (Response) getResponse(),
+                                         servletResponse,
+                                         isStartAsyncWithZeroArg);
+            asyncContext = asyncContextFinal;
+
+            final CompletionHandler<org.glassfish.grizzly.http.server.Response> requestCompletionHandler =
+                    new EmptyCompletionHandler<org.glassfish.grizzly.http.server.Response>() {
+
+                        @Override
+                        public void completed(org.glassfish.grizzly.http.server.Response response) {
+                            asyncContextFinal.notifyAsyncListeners(
+                                    AsyncContextImpl.AsyncEventType.COMPLETE,
+                                    null);
+                        }
+                    };
+
+            final TimeoutHandler timeoutHandler = new TimeoutHandler() {
+
+                @Override
+                public boolean onTimeout(final org.glassfish.grizzly.http.server.Response response) {
+                    return processTimeout();
+                }
+            };
+
+            coyoteRequest.getResponse().suspend(-1, TimeUnit.MILLISECONDS,
+                    requestCompletionHandler, timeoutHandler);
+            asyncStartedThread = Thread.currentThread();
+        }
+
+        asyncStarted.set(true);
+
+        return asyncContext;
+    }
+
+    /**
+     * Checks whether async processing has started on this request.
+     */
+    @Override
+    public boolean isAsyncStarted() {
+        return asyncStarted.get();
+    }
+
+    void setAsyncStarted(boolean asyncStarted) {
+        this.asyncStarted.set(asyncStarted);
+    }
+
+    /**
+     * Disables async support for this request.
+     *
+     * Async support is disabled as soon as this request has passed a filter
+     * or servlet that does not support async (either via the designated
+     * annotation or declaratively).
+     */
+    @Override
+    public void disableAsyncSupport() {
+        isAsyncSupported = false;
+    }
+
+    void setAsyncTimeout(long timeout) {
+        coyoteRequest.getResponse().getSuspendContext().setTimeout(
+                timeout, TimeUnit.MILLISECONDS);;
+    }
+
+    /**
+     * Checks whether this request supports async.
+     */
+    @Override
+    public boolean isAsyncSupported() {
+        return isAsyncSupported;
+    }
+
+    /**
+     * Gets the AsyncContext of this request.
+     */
+    @Override
+    public AsyncContext getAsyncContext() {
+        if (!isAsyncStarted()) {
+            throw new IllegalStateException(rb.getString(LogFacade.REQUEST_NOT_PUT_INTO_ASYNC_MODE_EXCEPTION));
+        }
+
+        return asyncContext;
+    }
+
+    /*
+     * Invokes any registered AsyncListener instances at their
+     * <tt>onComplete</tt> method
+     */
+    void asyncComplete() {
+        asyncStarted.set(false);
+        
+        if (asyncStartedThread != Thread.currentThread() ||
+                !asyncContext.isOkToConfigure()) {
+            // it's not safe to just mark response as resumed
+            coyoteRequest.getResponse().resume();
+        } else {
+            // This code is called if we startAsync and complete in the service() thread.
+            // So instead of resuming the suspendedContext (which will finish the response processing),
+            // we just have to mark the context as resumed like it has never been suspended.
+            final SuspendedContextImpl suspendContext =
+                    (SuspendedContextImpl) coyoteRequest.getResponse().getSuspendContext();
+
+            suspendContext.markResumed();
+            suspendContext.getSuspendStatus().reset();
+        }
+    }
+
+    /*
+     * Invokes all registered AsyncListener instances at their
+     * <tt>onTimeout</tt> method.
+     *
+     * This method also performs an error dispatch and completes the response
+     * if none of the listeners have done so.
+     */
+    void asyncTimeout() {
+        if (asyncContext != null) {
+            asyncContext.notifyAsyncListeners(
+                    AsyncContextImpl.AsyncEventType.TIMEOUT, null);
+        }
+        inputBuffer.disableReadHandler();
+        if (response instanceof Response) {
+            ((Response)response).disableWriteHandler();
+        }
+        errorDispatchAndComplete(null);
+    }
+
+    /**
+     * Notifies this Request that the container-initiated dispatch
+     * during which ServletRequest#startAsync was called is about to
+     * return to the container
+     */
+    void onExitService() {
+        final AsyncContextImpl ac = asyncContext;
+        
+        if (ac != null) {
+            ac.setOkToConfigure(false);
+
+            if (asyncStarted.get()) {
+                coyoteRequest.getResponse().getSuspendContext().setTimeout(
+                        ac.getTimeout(), TimeUnit.MILLISECONDS);
+            }
+
+            ac.onExitService();
+        }
+        afterService = true;
+        if (resume) {
+            coyoteRequest.getResponse().resume();
+        }
+
+    }
+
+    void resumeAfterService() {
+        if (afterService) {
+            coyoteRequest.getResponse().resume();
+        } else {
+            resume = true;
+        }
+    }
+
+    private boolean processTimeout() {
+        boolean result = true;
+        final AsyncContextImpl asyncContextLocal = this.asyncContext;
+        try {
+            asyncTimeout();
+        } finally {
+            result = asyncContextLocal != null && !asyncContextLocal.getAndResetDispatchInScope();
+        }
+        
+        return result;
+    }
+
+    void errorDispatchAndComplete(Throwable t) {
+        /*
+         * If no listeners, or none of the listeners called
+         * AsyncContext#complete or any of the AsyncContext#dispatch
+         * methods (in which case asyncStarted would have been set to false),
+         * perform an error dispatch with a status code equal to 500.
+         */
+        final AsyncContextImpl ac = asyncContext;
+        if (ac != null
+                && !ac.isDispatchInScope()
+                && !ac.isAsyncComplete()) {
+            ((HttpServletResponse) response).setStatus(
+                HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            response.setError();
+            if (t != null) {
+                setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
+            }
+            try {
+                if (hostValve != null) {
+                    hostValve.postInvoke(this, response);
+                }
+            } catch (Exception e) {
+                log.log(Level.SEVERE, LogFacade.UNABLE_PERFORM_ERROR_DISPATCH, e);
+            } finally {
+                /*
+                 * If no matching error page was found, or the error page
+                 * did not call AsyncContext#complete or any of the
+                 * AsyncContext#dispatch methods, call AsyncContext#complete
+                 */
+                if (!ac.isAsyncComplete()) {
+                    ac.complete();
+                }
+            }
+        }
+    }
+
+    private Multipart getMultipart() {
+        if (multipart == null) {
+            multipart = new Multipart(this,
+                    wrapper.getMultipartLocation(),
+                    wrapper.getMultipartMaxFileSize(),
+                    wrapper.getMultipartMaxRequestSize(),
+                    wrapper.getMultipartFileSizeThreshold());
+        }
+        return multipart;
+    }
+
+    private boolean isMultipartConfigured() {
+        if (wrapper instanceof StandardWrapper) {
+            return ((StandardWrapper)wrapper).isMultipartConfigured();
+        }
+        return false;
+    }
+
+    private void checkMultipartConfiguration(String name) {
+        if (! isMultipartConfigured()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.REQUEST_CALLED_WITHOUT_MULTIPART_CONFIG_EXCEPTION), name);
+            throw new IllegalStateException(msg);
+        }
+    } 
+
+    @Override
+    public Collection<Part> getParts() throws IOException, ServletException {
+        checkMultipartConfiguration("getParts");
+        return getMultipart().getParts();
+    }
+
+    @Override
+    public Part getPart(String name) throws IOException, ServletException {
+        checkMultipartConfiguration("getPart");
+        return getMultipart().getPart(name);
+    }
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     *
+    private void log(String message) {
+        org.apache.catalina.Logger logger = null;
+        if (connector != null && connector.getContainer() != null) {
+            logger = connector.getContainer().getLogger();
+        }
+        String localName = "Request";
+        if (logger != null) {
+            logger.log(localName + " " + message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.info(localName + " " + message);
+            }
+        }
+    }*/
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    private void log(String message, Throwable t) {
+        org.apache.catalina.Logger logger = null;
+        if (connector != null && connector.getContainer() != null) {
+            logger = connector.getContainer().getLogger();
+        }
+        String localName = "Request";
+        if (logger != null) {
+            logger.log(localName + " " + message, t,
+                    org.apache.catalina.Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, localName + " " + message, t);
+        }
+    }
+
+    // START SJSAS 6419950
+    private void populateSSLAttributes() {
+        RequestUtils.populateSSLAttributes(coyoteRequest);
+        Object attr = coyoteRequest.getAttribute(Globals.CERTIFICATES_ATTR);
+        if (attr != null) {
+            attributes.put(Globals.CERTIFICATES_ATTR, attr);
+        }
+        attr = coyoteRequest.getAttribute(Globals.CIPHER_SUITE_ATTR);
+        if (attr != null) {
+            attributes.put(Globals.CIPHER_SUITE_ATTR, attr);
+        }
+        attr = coyoteRequest.getAttribute(Globals.KEY_SIZE_ATTR);
+        if (attr != null) {
+            attributes.put(Globals.KEY_SIZE_ATTR, attr);
+        }
+        attr = coyoteRequest.getAttribute(Globals.SSL_SESSION_ID_ATTR);
+        if (attr != null) {
+            attributes.put(Globals.SSL_SESSION_ID_ATTR, attr);
+        }
+    }
+    // END SJSAS 6419950
+
+    // START GlassFish 896
+    private void initSessionTracker() {
+        notes.put(Globals.SESSION_TRACKER, sessionTracker);
+    }
+    // END GlassFish 896
+
+    /** 
+     * lock the session associated with this request
+     * this will be a foreground lock
+     * checks for background lock to clear
+     * and does a decay poll loop to wait until
+     * it is clear; after 5 times it takes control for 
+     * the foreground
+     *
+     * @return the session that's been locked
+     */     
+    @Override
+    public Session lockSession() {
+        Session sess = getSessionInternal(false);
+        // Now lock the session
+        if (sess != null) {
+            long pollTime = 200L;
+            int maxNumberOfRetries = 7;
+            int tryNumber = 0;
+            boolean keepTrying = true;
+            boolean lockResult = false;
+            // Try to lock up to maxNumberOfRetries times.
+            // Poll and wait starting with 200 ms.
+            while(keepTrying) {
+                lockResult = sess.lockForeground();
+                if(lockResult) {
+                    keepTrying = false;
+                    break;
+                }
+                tryNumber++;
+                if(tryNumber < maxNumberOfRetries) {
+                    pollTime = pollTime * 2L;
+                    threadSleep(pollTime);
+                } else {
+                    // Tried to wait and lock maxNumberOfRetries times.
+                    // Unlock the background so we can take over.
+                    log.log(Level.WARNING, LogFacade.BREAKING_BACKGROUND_LOCK_EXCEPTION, sess);
+                    if (sess instanceof StandardSession) {
+                        ((StandardSession)sess).unlockBackground();
+                    }
+                }              
+            }
+        }
+
+        return sess;
+    }    
+    
+    private void threadSleep(long sleepTime) {
+        try {
+            Thread.sleep(sleepTime);
+        } catch (InterruptedException e) {
+            ;
+        }
+    }    
+
+    /** 
+     * unlock the session associated with this request
+     */
+    @Override
+    public void unlockSession() {
+        Session sess = getSessionInternal(false);
+        // Now unlock the session
+        if (sess != null) {
+            sess.unlockForeground();
+        }        
+    }     
+
+    /**
+     * Increments the version of the given session, and stores it as a
+     * request attribute, so it can later be included in a response cookie.
+     */
+    private void incrementSessionVersion(StandardSession ss,
+                                         Context context) {
+        if (ss == null || context == null) {
+            return;
+        }
+
+        String versionString = Long.toString(ss.incrementVersion());
+
+        Map<String, String> sessionVersions = getSessionVersionsRequestAttribute();
+        if (sessionVersions == null) {
+            sessionVersions = new HashMap<String, String>();
+            setAttribute(Globals.SESSION_VERSIONS_REQUEST_ATTRIBUTE,
+                         sessionVersions);
+        }
+        String path = context.getPath();
+        if ("".equals(path)) {
+            path = "/";
+        }
+        sessionVersions.put(path, versionString);
+    }
+
+    @SuppressWarnings("unchecked")
+    Map<String, String> getSessionVersionsRequestAttribute() {
+        return (Map<String, String>) getAttribute(
+                Globals.SESSION_VERSIONS_REQUEST_ATTRIBUTE);
+    }
+
+    private boolean isSessionVersioningSupported() {
+        return context != null &&
+            context.getManager() != null &&
+            context.getManager().isSessionVersioningSupported();
+    }
+
+    /**
+     * This class will be invoked by Grizzly when a suspended operation is
+     * resumed {@link org.glassfish.grizzly.http.server.Response#resume} or has timed out.
+     * See {@link org.glassfish.grizzly.http.server.Response.ResponseAttachment} for details.
+     */
+//    private final static class RequestAttachment<A> extends
+//            org.glassfish.grizzly.http.server.Response.ResponseAttachment {
+//
+//        private Response res;
+//
+//        public RequestAttachment(Long timeout, A attachment,
+//                CompletionHandler<? super A> completionHandler,
+//                Response res) {
+//            super(timeout, attachment, completionHandler, res.getCoyoteResponse());
+//            this.res = res;
+//        }
+//
+//        @Override
+//        public void resume() {
+//            getCompletionHandler().resumed(getAttachment());
+//            if (log.isLoggable(Level.FINE)) {
+//                log.log(Level.FINE, "RequestAttachement.resume: " + res);
+//            }
+//            completeProcessing();
+//        }
+//
+//        /**
+//         * {@inheritDoc}
+//         */
+//        //@Override
+//        public void handleSelectedKey(SelectionKey selectionKey) {
+//            if (!selectionKey.isValid() || discardDisconnectEvent){
+//                selectionKey.cancel();
+//                return;
+//            }
+//            try {
+//                ((Request)getAttachment()).clientClosedConnection = ((SocketChannel)selectionKey.channel()).
+//                    read(ByteBuffer.allocate(1)) == -1;
+//            } catch (IOException ex) {
+//
+//            } finally{
+//                if (((Request)getAttachment()).clientClosedConnection){
+//                   selectionKey.cancel();
+//                   getCompletionHandler().cancelled(getAttachment());
+//                }
+//            }
+//        }
+//
+//        void completeProcessing() {
+//            try {
+//                res.finishResponse();
+//            } catch (IOException ex) {
+//                if (log.isLoggable(Level.FINE)) {
+//                    log.log(Level.FINE, "res.finishResponse()" + res);
+//                }
+//            }
+//            res.recycle();
+//            res.getRequest().recycle();
+//        }
+//
+//        @Override
+//        public boolean timeout() {
+//            // If the buffers are empty, commit the response header
+//            boolean result = true;
+//
+//            try {
+//                if (log.isLoggable(Level.FINE)) {
+//                    log.log(Level.FINE, "RequestAttachement.timeout: " + res);
+//                }
+//                cancel();
+//            } finally {
+//                Request req = (Request)getAttachment();
+//                final AsyncContextImpl asyncContext = req.asyncContext;
+//                if (asyncContext != null && !asyncContext.getAndResetDispatchInScope()) {
+//                completeProcessing();
+//                } else {
+//                    result = false;
+//            }
+//        }
+//
+//            return result;
+//    }
+//}
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/RequestFacade.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/RequestFacade.java
new file mode 100644
index 0000000..526ced5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/RequestFacade.java
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Globals;
+import org.apache.catalina.core.RequestFacadeHelper;
+import org.apache.catalina.security.SecurityUtil;
+
+import javax.servlet.*;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpUpgradeHandler;
+import javax.servlet.http.Part;
+import javax.servlet.http.PushBuilder;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.SecurityPermission;
+import java.util.*;
+import javax.servlet.http.HttpServletMapping;
+
+
+/**
+ * Facade class that wraps a Coyote request object.  
+ * All methods are delegated to the wrapped request.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @author Jean-Francois Arcand
+ * @version $Revision: 1.7 $ $Date: 2007/08/01 19:04:28 $
+ */
+public class RequestFacade 
+    implements HttpServletRequest {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+        
+    // ----------------------------------------------------------- DoPrivileged
+    
+    private final class GetAttributePrivilegedAction
+            implements PrivilegedAction<Enumeration<String>> {
+        
+        public Enumeration<String> run() {
+            return request.getAttributeNames();
+        }            
+    }
+     
+    
+    private final class GetParameterMapPrivilegedAction
+            implements PrivilegedAction<Map<String, String[]>> {
+        
+        public Map<String, String[]> run() {
+            return request.getParameterMap();
+        }        
+    }    
+    
+    
+    private final class GetRequestDispatcherPrivilegedAction
+            implements PrivilegedAction<RequestDispatcher> {
+
+        private String path;
+
+        public GetRequestDispatcherPrivilegedAction(String path){
+            this.path = path;
+        }
+        
+        public RequestDispatcher run() {   
+            return request.getRequestDispatcher(path);
+        }           
+    }    
+    
+    
+    private final class GetParameterPrivilegedAction
+            implements PrivilegedAction<String> {
+
+        public String name;
+
+        public GetParameterPrivilegedAction(String name){
+            this.name = name;
+        }
+
+        public String run() {       
+            return request.getParameter(name);
+        }           
+    }    
+    
+     
+    private final class GetParameterNamesPrivilegedAction
+            implements PrivilegedAction<Enumeration<String>> {
+        
+        public Enumeration<String> run() {          
+            return request.getParameterNames();
+        }           
+    } 
+    
+    
+    private final class GetParameterValuePrivilegedAction
+            implements PrivilegedAction<String[]> {
+
+        public String name;
+
+        public GetParameterValuePrivilegedAction(String name){
+            this.name = name;
+        }
+
+        public String[] run() {       
+            return request.getParameterValues(name);
+        }           
+    }    
+  
+    
+    private final class GetCookiesPrivilegedAction
+            implements PrivilegedAction<Cookie[]> {
+        
+        public Cookie[] run() {       
+            return request.getCookies();
+        }           
+    }      
+    
+    
+    private final class GetCharacterEncodingPrivilegedAction
+            implements PrivilegedAction<String> {
+        
+        public String run() {       
+            return request.getCharacterEncoding();
+        }           
+    }   
+        
+    
+    private final class GetHeadersPrivilegedAction
+            implements PrivilegedAction<Enumeration<String>> {
+
+        private String name;
+
+        public GetHeadersPrivilegedAction(String name){
+            this.name = name;
+        }
+        
+        public Enumeration<String> run() {       
+            return request.getHeaders(name);
+        }           
+    }    
+        
+    
+    private final class GetHeaderNamesPrivilegedAction
+            implements PrivilegedAction<Enumeration<String>> {
+
+        public Enumeration<String> run() {       
+            return request.getHeaderNames();
+        }           
+    }  
+            
+    
+    private final class GetLocalePrivilegedAction
+            implements PrivilegedAction<Locale> {
+
+        public Locale run() {       
+            return request.getLocale();
+        }           
+    }    
+            
+    
+    private final class GetLocalesPrivilegedAction
+            implements PrivilegedAction<Enumeration<Locale>> {
+
+        public Enumeration<Locale> run() {       
+            return request.getLocales();
+        }           
+    }    
+    
+    private final class GetSessionPrivilegedAction
+            implements PrivilegedAction<HttpSession> {
+
+        private boolean create;
+        
+        public GetSessionPrivilegedAction(boolean create){
+            this.create = create;
+        }
+                
+        public HttpSession run() {  
+            return request.getSession(create);
+        }           
+    }
+
+    private final class ChangeSessionIdPrivilegedAction
+            implements PrivilegedAction<String> {
+
+        public String run() {
+            return request.changeSessionId();
+        }
+    }
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a wrapper for the specified request.
+     *
+     * @param request The request to be wrapped
+     */
+    public RequestFacade(Request request) {
+        this(request, false);
+    }
+
+
+    /**
+     * Construct a wrapper for the specified request.
+     *
+     * @param request The request to be wrapped
+     * @param maskDefaultContextMapping true if the fact that a request
+     * received at the root context was mapped to a default-web-module will
+     * be masked, false otherwise
+     */
+    public RequestFacade(Request request,
+                         boolean maskDefaultContextMapping) {
+        this.request = request;
+        this.maskDefaultContextMapping = maskDefaultContextMapping;
+        this.reqFacHelper = new RequestFacadeHelper(request);
+    }
+
+
+    // ----------------------------------------------- Class/Instance Variables
+
+
+    private static final SecurityPermission
+        GET_UNWRAPPED_COYOTE_REQUEST_PERMISSION =
+            new SecurityPermission("getUnwrappedCoyoteRequest");
+
+
+
+
+    /**
+     * The wrapped request.
+     */
+    protected Request request = null;
+
+
+    /*
+     * True if the fact that a request received at the root context was
+     * mapped to a default-web-module will be masked, false otherwise.
+     *
+     * For example, if set to true, this request facade's getContextPath()
+     * method will return "/", rather than the context root of the
+     * default-web-module, for requests received at the root context that
+     * were mapped to a default-web-module.
+     */
+    private boolean maskDefaultContextMapping = false;
+
+    private RequestFacadeHelper reqFacHelper = null;
+ 
+
+    // --------------------------------------------------------- Public Methods
+   
+    
+    /**
+    * Prevent cloning the facade.
+    */
+    protected Object clone()
+        throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+    
+    
+    /**
+     * Clear facade.
+     */
+    public void clear() {
+        request = null;
+        if (reqFacHelper != null) {
+            reqFacHelper.clear();
+        }
+        reqFacHelper = null;
+    }
+
+
+    RequestFacadeHelper getRequestFacadeHelper() {
+        return reqFacHelper;
+    }
+    
+
+    // ------------------------------------------------- ServletRequest Methods
+
+    @Override
+    public Object getAttribute(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getAttribute(name);
+    }
+
+    @Override
+    public Enumeration<String> getAttributeNames() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetAttributePrivilegedAction());        
+        } else {
+            return request.getAttributeNames();
+        }
+    }
+
+    @Override
+    public String getCharacterEncoding() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetCharacterEncodingPrivilegedAction());
+        } else {
+            return request.getCharacterEncoding();
+        }         
+    }
+
+    @Override
+    public void setCharacterEncoding(String env)
+            throws java.io.UnsupportedEncodingException {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        request.setCharacterEncoding(env);
+    }
+
+    @Override
+    public int getContentLength() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getContentLength();
+    }
+
+    @Override
+    public long getContentLengthLong() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getContentLengthLong();
+    }
+
+    @Override
+    public String getContentType() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getContentType();
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getInputStream();
+    }
+    
+    public HttpServletMapping getHttpServletMapping() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getHttpServletMapping();
+    }
+    
+    public String getParameter(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetParameterPrivilegedAction(name));
+        } else {
+            return request.getParameter(name);
+        }
+    }
+
+    @Override
+    public Enumeration<String> getParameterNames() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetParameterNamesPrivilegedAction());
+        } else {
+            return request.getParameterNames();
+        }
+    }
+
+    @Override
+    public String[] getParameterValues(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        String[] ret = null;
+
+        /*
+         * Clone the returned array only if there is a security manager
+         * in place, so that performance won't suffer in the non-secure case
+         */
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            ret = AccessController.doPrivileged(
+                new GetParameterValuePrivilegedAction(name));
+            if (ret != null) {
+                ret = (String[]) ret.clone();
+            }
+        } else {
+            ret = request.getParameterValues(name);
+        }
+
+        return ret;
+    }
+
+    @Override
+    public Map<String, String[]> getParameterMap() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetParameterMapPrivilegedAction());        
+        } else {
+            return request.getParameterMap();
+        }
+    }
+
+    @Override
+    public String getProtocol() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getProtocol();
+    }
+
+    @Override
+    public String getScheme() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getScheme();
+    }
+
+    @Override
+    public String getServerName() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getServerName();
+    }
+
+    @Override
+    public int getServerPort() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getServerPort();
+    }
+
+    @Override
+    public BufferedReader getReader() throws IOException {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getReader();
+    }
+
+    @Override
+    public String getRemoteAddr() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getRemoteAddr();
+    }
+
+    @Override
+    public String getRemoteHost() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getRemoteHost();
+    }
+
+    @Override
+    public void setAttribute(String name, Object o) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        request.setAttribute(name, o);
+    }
+
+    @Override
+    public void removeAttribute(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        request.removeAttribute(name);
+    }
+
+    @Override
+    public Locale getLocale() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetLocalePrivilegedAction());
+        } else {
+            return request.getLocale();
+        }        
+    }
+
+    @Override
+    public Enumeration<Locale> getLocales() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetLocalesPrivilegedAction());
+        } else {
+            return request.getLocales();
+        }        
+    }
+
+    @Override
+    public boolean isSecure() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.isSecure();
+    }
+
+    @Override
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetRequestDispatcherPrivilegedAction(path));
+        } else {
+            return request.getRequestDispatcher(path);
+        }
+    }
+
+    @Override
+    public String getRealPath(String path) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getRealPath(path);
+    }
+
+    @Override
+    public String getAuthType() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getAuthType();
+    }
+
+    @Override
+    public Cookie[] getCookies() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        Cookie[] ret = null;
+
+        /*
+         * Clone the returned array only if there is a security manager
+         * in place, so that performance won't suffer in the non-secure case
+         */
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            ret = AccessController.doPrivileged(
+                new GetCookiesPrivilegedAction());
+            if (ret != null) {
+                ret = (Cookie[]) ret.clone();
+            }
+        } else {
+            ret = request.getCookies();
+        }
+
+        return ret;
+    }
+
+    @Override
+    public long getDateHeader(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getDateHeader(name);
+    }
+
+    @Override
+    public String getHeader(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getHeader(name);
+    }
+
+    @Override
+    public Enumeration<String> getHeaders(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetHeadersPrivilegedAction(name));
+        } else {
+            return request.getHeaders(name);
+        }         
+    }
+
+    @Override
+    public Enumeration<String> getHeaderNames() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.doPrivileged(
+                new GetHeaderNamesPrivilegedAction());
+        } else {
+            return request.getHeaderNames();
+        }             
+    }
+
+    @Override
+    public int getIntHeader(String name) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getIntHeader(name);
+    }
+
+    @Override
+    public Map<String, String> getTrailerFields() {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getTrailerFields();
+    }
+
+    @Override
+    public boolean isTrailerFieldsReady() {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.isTrailerFieldsReady();
+    }
+
+    @Override
+    public String getMethod() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getMethod();
+    }
+
+    @Override
+    public String getPathInfo() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getPathInfo();
+    }
+
+    @Override
+    public String getPathTranslated() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getPathTranslated();
+    }
+
+
+    /**
+     * Gets the servlet context to which this servlet request was last
+     * dispatched.
+     *
+     * @return the servlet context to which this servlet request was last
+     * dispatched
+     */
+    @Override
+    public ServletContext getServletContext() {
+        return request.getServletContext();
+    }
+
+    @Override
+    public String getContextPath() {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.getContextPath(maskDefaultContextMapping);
+    }
+
+
+    public String getContextPath(boolean maskDefaultContextMapping) {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.getContextPath(maskDefaultContextMapping);
+    }
+
+    @Override
+    public String getQueryString() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getQueryString();
+    }
+
+    @Override
+    public String getRemoteUser() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getRemoteUser();
+    }
+
+    @Override
+    public boolean isUserInRole(String role) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.isUserInRole(role);
+    }
+
+    @Override
+    public java.security.Principal getUserPrincipal() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getUserPrincipal();
+    }
+
+    @Override
+    public String getRequestedSessionId() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getRequestedSessionId();
+    }
+
+    @Override
+    public String getRequestURI() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getRequestURI(maskDefaultContextMapping);
+    }
+
+    @Override
+    public StringBuffer getRequestURL() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getRequestURL(maskDefaultContextMapping);
+    }
+
+    @Override
+    public String getServletPath() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getServletPath();
+    }
+
+    @Override
+    public HttpSession getSession(boolean create) {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.
+                doPrivileged(new GetSessionPrivilegedAction(create));
+        } else {
+            return request.getSession(create);
+        }
+    }
+
+    @Override
+    public HttpSession getSession() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return getSession(true);
+    }
+
+    @Override
+    public String changeSessionId() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return AccessController.
+                    doPrivileged(new ChangeSessionIdPrivilegedAction());
+        } else {
+            return request.changeSessionId();
+        }
+    }
+
+    @Override
+    public boolean isRequestedSessionIdValid() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.isRequestedSessionIdValid();
+    }
+
+    @Override
+    public boolean isRequestedSessionIdFromCookie() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.isRequestedSessionIdFromCookie();
+    }
+
+    @Override
+    public boolean isRequestedSessionIdFromURL() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.isRequestedSessionIdFromURL();
+    }
+
+    @Override
+    public boolean isRequestedSessionIdFromUrl() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.isRequestedSessionIdFromURL();
+    }
+
+    @Override
+    public String getLocalAddr() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getLocalAddr();
+    }
+
+    @Override
+    public String getLocalName() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getLocalName();
+    }
+
+    @Override
+    public int getLocalPort() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getLocalPort();
+    }
+
+    @Override
+    public int getRemotePort() {
+
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+
+        return request.getRemotePort();
+    }
+
+    @Override
+    public DispatcherType getDispatcherType() {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.getDispatcherType();
+    }
+
+
+    /**
+     * Starts async processing on this request.
+     */
+    @Override
+    public AsyncContext startAsync() throws IllegalStateException {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.startAsync();
+    }
+
+
+    /**
+     * Starts async processing on this request.
+     */
+    @Override
+    public AsyncContext startAsync(ServletRequest sreq,
+                                   ServletResponse sresp)
+            throws IllegalStateException {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.startAsync(sreq, sresp);
+    }
+        
+
+    /**
+     * Checks whether async processing has started on this request.
+     */
+    @Override
+    public boolean isAsyncStarted() {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.isAsyncStarted();
+    }
+
+
+    /**
+     * Checks whether this request supports async.
+     */
+    @Override
+    public boolean isAsyncSupported() {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.isAsyncSupported();
+    }
+
+
+    /**
+     * Gets the AsyncContext of this request.
+     */
+    @Override
+    public AsyncContext getAsyncContext() {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.getAsyncContext();
+    }
+
+
+    @Override
+    public Collection<Part> getParts() throws IOException, ServletException {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.getParts();
+    }
+
+
+    @Override
+    public Part getPart(String name) throws IOException, ServletException {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.getPart(name);
+    }
+
+    @Override
+    public boolean authenticate(HttpServletResponse response)
+            throws IOException, ServletException {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.authenticate(response);
+    }
+
+    @Override
+    public void login(String username, String password)
+            throws ServletException {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        request.login(username, password);
+    }
+
+    @Override
+    public void logout() throws ServletException {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        request.logout();
+    }
+
+    @Override
+    public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_USE_REQUEST_OBJECT_OUTSIDE_SCOPE_EXCEPTION));
+        }
+        return request.upgrade(handlerClass);
+    }
+
+    @Override
+    public PushBuilder newPushBuilder() {
+        return request.newPushBuilder();
+    }
+
+
+    //START S1AS 4703023
+    /**
+     * Return the original <code>CoyoteRequest</code> object.
+     */
+    public Request getUnwrappedCoyoteRequest()
+        throws AccessControlException {
+
+        // tomcat does not have any Permission types so instead of
+        // creating a TomcatPermission for this, use SecurityPermission.
+        if (Globals.IS_SECURITY_ENABLED) {
+            AccessController.checkPermission(
+                GET_UNWRAPPED_COYOTE_REQUEST_PERMISSION);
+        }
+
+        return request;
+    }
+    //END S1AS 4703023
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Response.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Response.java
new file mode 100644
index 0000000..2e19145
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/Response.java
@@ -0,0 +1,2083 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.function.Supplier;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import com.sun.appserv.ProxyHandler;
+import org.apache.catalina.Connector;
+import org.apache.catalina.Context;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Globals;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.Session;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.CharsetMapper;
+import org.apache.catalina.util.RequestUtil;
+import org.glassfish.common.util.InputValidationUtil;
+import org.glassfish.grizzly.http.util.CharChunk;
+import org.glassfish.grizzly.http.util.CookieSerializerUtils;
+import org.glassfish.grizzly.http.util.CookieUtils;
+import org.glassfish.grizzly.http.util.FastHttpDateFormat;
+import org.glassfish.grizzly.http.util.MimeHeaders;
+import org.glassfish.grizzly.http.util.UEncoder;
+import org.glassfish.web.util.HtmlEntityEncoder;
+// START S1AS 6170450
+
+// END S1AS 6170450
+
+/**
+ * Wrapper object for the Coyote response.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.22 $ $Date: 2007/05/05 05:32:43 $
+ */
+
+public class Response
+    implements HttpResponse, HttpServletResponse {
+
+    // ------------------------------------------------------ Static variables
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+    /**
+     * Whether or not to enforce scope checking of this object.
+     */
+    private static boolean enforceScope = false;
+
+    public static final String HTTP_RESPONSE_DATE_HEADER =
+        "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+    /**
+     * Descriptive information about this Response implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.connector.Response/1.0";
+
+
+    // ----------------------------------------------------------- Constructors
+
+    public Response() {
+        // START OF SJSAS 6231069
+        outputBuffer = new OutputBuffer();
+        outputStream = new CoyoteOutputStream(outputBuffer);
+        writer = createWriter(outputBuffer);
+        // END OF SJSAS 6231069
+        urlEncoder.addSafeCharacter('/');
+    }
+    
+    // START OF SJSAS 6231069
+    public Response(boolean chunkingDisabled) {
+        outputBuffer = new OutputBuffer();
+        outputStream = new CoyoteOutputStream(outputBuffer);
+        writer = createWriter(outputBuffer);
+        urlEncoder.addSafeCharacter('/');
+    }
+    // END OF SJSAS 6231069
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    // BEGIN S1AS 4878272
+    private String detailErrorMsg;
+    // END S1AS 4878272
+
+    /**
+     * The date format we will use for creating date headers.
+     */
+    protected SimpleDateFormat format = null;
+
+    /**
+     * Associated context.
+     */
+    protected Context context = null;
+
+    protected boolean upgrade = false;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Set whether or not to enforce scope checking of this object.
+     */
+    public static void setEnforceScope(boolean enforce) {
+
+        enforceScope = enforce;
+
+    }
+
+
+    /**
+     * 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 org.glassfish.grizzly.http.server.Response coyoteResponse;
+
+    /**
+     * Set the Coyote response.
+     * 
+     * @param coyoteResponse The Coyote response
+     */
+    public void setCoyoteResponse(org.glassfish.grizzly.http.server.Response coyoteResponse) {
+        this.coyoteResponse = coyoteResponse;
+        outputBuffer.setCoyoteResponse(this);
+    }
+
+    /**
+     * Get the Coyote response.
+     */
+    public org.glassfish.grizzly.http.server.Response getCoyoteResponse() {
+        return coyoteResponse;
+    }
+
+
+    /**
+     * Return the Context within which this Request is being processed.
+     */
+    public Context getContext() {
+        /*
+         * Ideally, we would call CoyoteResponse.setContext() from
+         * CoyoteAdapter (the same way we call it for CoyoteRequest), and
+         * have getContext() return this context. However, for backwards
+         * compatibility with WS 7.0's NSAPIProcessor, which does not call
+         * CoyoteResponse.setContext(), we must delegate to the getContext()
+         * method of the linked request object.
+         */
+        return request.getContext();
+    }
+
+    /**
+     * 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.
+     */
+    // START OF SJSAS 6231069    
+    //protected OutputBuffer outputBuffer = new OutputBuffer();
+    protected OutputBuffer outputBuffer;
+    // END OF SJSAS 6231069    
+
+    /**
+     * The associated output stream.
+     */    
+    // START OF SJSAS 6231069 
+    /*protected CoyoteOutputStream outputStream =
+        new CoyoteOutputStream(outputBuffer);*/
+    protected CoyoteOutputStream outputStream;    
+    // END OF SJSAS 6231069    
+
+    /**
+     * The associated writer.
+     */
+    // START OF SJSAS 6231069 
+    // protected CoyoteWriter writer = new CoyoteWriter(outputBuffer);
+    protected CoyoteWriter writer;
+    // END OF SJSAS 6231069    
+    
+
+    /**
+     * The application commit flag.
+     */
+    protected boolean appCommitted = false;
+
+
+    /**
+     * The included flag.
+     */
+    protected boolean included = false;
+
+    
+    /**
+     * The characterEncoding flag
+     */
+    private boolean isCharacterEncodingSet = false;
+    
+    /**
+     * The contextType flag
+     */    
+    private boolean isContentTypeSet = false;
+
+    
+    /**
+     * The error flag.
+     */
+    protected boolean error = false;
+
+
+    /**
+     * 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() {
+
+        if (request != null && request.isAsyncStarted()) {
+            return;
+        }
+
+        context = null;
+        outputBuffer.recycle();
+        usingOutputStream = false;
+        usingWriter = false;
+        appCommitted = false;
+        included = false;
+        error = false;
+        isContentTypeSet = false;
+        isCharacterEncodingSet = false;
+        detailErrorMsg = null;
+
+        if (enforceScope) {
+            if (facade != null) {
+                facade.clear();
+                facade = null;
+            }
+            if (outputStream != null) {
+                outputStream.clear();
+                outputStream = null;
+            }
+            if (writer != null) {
+                writer.clear();
+                writer = null;
+            }
+        } else {
+            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()
+                || getContentLength() > 0
+                    && getContentCount() >= getContentLength();
+    }
+
+
+    /**
+     * 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 Request 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) {
+        if (request instanceof Request) {
+            this.request = (Request) request;
+        }
+    }
+
+
+    /**
+     * The facade associated with this response.
+     */
+    protected ResponseFacade facade = null;
+
+    /**
+     * Return the <code>ServletResponse</code> for which this object
+     * is the facade.
+     */
+    public HttpServletResponse getResponse() {
+        if (facade == null) {
+            facade = new ResponseFacade(this);
+        }
+        return facade;
+    }
+
+
+    /**
+     * Return the output stream associated with this Response.
+     */
+    public OutputStream getStream() {
+        if (outputStream == null) {
+            outputStream = new CoyoteOutputStream(outputBuffer);
+        }
+        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;
+    }
+
+
+    // BEGIN S1AS 4878272
+    /**
+     * Sets detail error message.
+     *
+     * @param message detail error message
+     */
+    public void setDetailMessage(String message) {
+        this.detailErrorMsg = message;
+    }
+
+
+    /**
+     * Gets detail error message.
+     *
+     * @return the detail error message
+     */
+    public String getDetailMessage() {
+        return this.detailErrorMsg;
+    }
+    // END S1AS 4878272
+
+
+    /**
+     * 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
+        if (outputStream == null) {
+            outputStream = new CoyoteOutputStream(outputBuffer);
+        }
+        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) {
+	    log(rb.getString(LogFacade.ERROR_DURING_FINISH_RESPONSE), t);
+        }
+    }
+
+
+    /**
+     * 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.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public PrintWriter getReporter() throws IOException {
+        if (outputBuffer.isNew()) {
+            outputBuffer.checkConverter();
+            if (writer == null) {
+                writer = createWriter(outputBuffer);
+            }
+            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();
+    }
+
+    
+    /*
+     * Overrides the name of the character encoding used in the body
+     * of the request. This method must be called prior to reading
+     * request parameters or reading input using getReader().
+     *
+     * @param charset String containing the name of the character encoding.
+     */
+    public void setCharacterEncoding(String charset) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        // Ignore any call made after the getWriter has been invoked
+        // The default should be used
+        if (usingWriter)
+            return;
+
+        coyoteResponse.setCharacterEncoding(charset);
+        isCharacterEncodingSet = true;
+    }
+
+    
+    /**
+     * 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
+                (rb.getString(LogFacade.GET_WRITER_BEEN_CALLED_EXCEPTION));
+
+        usingOutputStream = true;
+        if (outputStream == null) {
+            outputStream = new CoyoteOutputStream(outputBuffer);
+        }
+        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(rb.getString(LogFacade.GET_OUTPUT_STREAM_BEEN_CALLED_EXCEPTION));
+
+        /*
+         * If the response's character encoding has not been specified as
+         * described in <code>getCharacterEncoding</code> (i.e., the method
+         * just returns the default value <code>ISO-8859-1</code>),
+         * <code>getWriter</code> updates it to <code>ISO-8859-1</code>
+         * (with the effect that a subsequent call to getContentType() will
+         * include a charset=ISO-8859-1 component which will also be
+         * reflected in the Content-Type response header, thereby satisfying
+         * the Servlet spec requirement that containers must communicate the
+         * character encoding used for the servlet response's writer to the
+         * client).
+         */
+        setCharacterEncoding(getCharacterEncoding());
+
+        usingWriter = true;
+        outputBuffer.checkConverter();
+        if (writer == null) {
+            writer = createWriter(outputBuffer);
+        }
+        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 Grizzly duplicated internal attributes
+        coyoteResponse.resetBuffer(true);
+        usingOutputStream = false;
+        usingWriter = false;
+        isCharacterEncodingSet = false;
+    }
+
+
+    /**
+     * Reset the data buffer but not any status or header information.
+     *
+     * @exception IllegalStateException if the response has already
+     *  been committed
+     */
+    public void resetBuffer() {
+        resetBuffer(false);
+    }
+
+    
+    /**
+     * Reset the data buffer and the using Writer/Stream flags but not any
+     * status or header information.
+     *
+     * @param resetWriterStreamFlags <code>true</code> if the internal
+     *        <code>usingWriter</code>, <code>usingOutputStream</code>,
+     *        <code>isCharacterEncodingSet</code> flags should also be reset
+     * 
+     * @exception IllegalStateException if the response has already
+     *  been committed
+     */
+    public void resetBuffer(boolean resetWriterStreamFlags) {
+
+        if (isCommitted())
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_RESET_BUFFER_EXCEPTION));
+
+        outputBuffer.reset();
+                
+        if(resetWriterStreamFlags) {
+            usingOutputStream = false;
+            usingWriter = false;
+            isCharacterEncodingSet = false;
+        }
+
+    }
+
+
+    /**
+     * 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(rb.getString(LogFacade.CANNOT_CHANGE_BUFFER_SIZE_EXCEPTION));
+
+        outputBuffer.setBufferSize(size);
+
+    }
+
+
+    /**
+     * Set the content length (in bytes) for this Response.
+     *
+     * @param length The new content length
+     */
+    public void setContentLength(int length) {
+
+        setContentLengthLong(length);
+
+    }
+
+
+    /**
+     * Sets the length of the content body in the response In HTTP servlets,
+     * this method sets the HTTP Content-Length header.
+     *
+     * @param length The new content length
+     */
+    public void setContentLengthLong(long length) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        if (usingWriter)
+            return;
+
+        coyoteResponse.setContentLengthLong(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;
+
+        // Ignore charset if getWriter() has already been called
+        if (usingWriter) {
+            if (type != null) {
+                int index = type.indexOf(";");
+                if (index != -1) {
+                    type = type.substring(0, index);
+                }
+            }
+        }
+
+        coyoteResponse.setContentType(type);
+
+        // Check to see if content type contains charset
+        if (type != null) {
+            int index = type.indexOf(";");
+            if (index != -1) {
+                int len = type.length();
+                index++;
+                while (index < len && Character.isWhitespace(type.charAt(index))) {
+                    index++;
+                }
+                if (index+7 < 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) == '=') {
+                    isCharacterEncodingSet = true;
+                }
+            }
+        }
+
+        isContentTypeSet = true;    
+    }
+
+
+    /**
+     * 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);
+
+        // Ignore any call made after the getWriter has been invoked.
+        // The default should be used
+        if (usingWriter)
+            return;
+
+        if (isCharacterEncodingSet) {
+            return;
+        }
+
+        CharsetMapper cm = getContext().getCharsetMapper();
+        String charset = cm.getCharset( locale );
+        if ( charset != null ){
+            coyoteResponse.setCharacterEncoding(charset);
+        }
+
+    }
+
+
+    // --------------------------------------------------- HttpResponse Methods
+
+
+    /**
+     * 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 {@link #getHeaders(String)} to
+     * retrieve all of them.
+     *
+     * @param name Header name to look up
+     */
+    public String getHeader(String name) {
+        return coyoteResponse.getHeader(name);
+    }
+
+
+    /**
+     * @return a (possibly empty) <code>Collection</code> of the names
+     * of the headers of this response
+     */
+    public Collection<String> getHeaderNames() {
+        final Collection<String> result = new ArrayList<String>();
+        for (final String headerName : coyoteResponse.getResponse().getHeaders().names()) {
+            result.add(headerName);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * @param name the name of the response header whose values to return
+     *
+     * @return a (possibly empty) <code>Collection</code> of the values
+     * of the response header with the given name
+     */
+    public Collection<String> getHeaders(String name) {
+        final Collection<String> result = new ArrayList<String>();
+        for (final String headerValue : coyoteResponse.getResponse().getHeaders().values(name)) {
+            result.add(headerValue);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * 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(final Cookie cookie) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        /* GlassFish 898
+        final StringBuilder sb = new StringBuilder();
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                public Void run(){
+                    ServerCookie.appendCookieValue
+                        (sb, cookie.getVersion(), cookie.getName(), 
+                         cookie.getValue(), cookie.getPath(), 
+                         cookie.getDomain(), cookie.getComment(), 
+                         cookie.getMaxAge(), cookie.getSecure());
+                    return null;
+                }
+            });
+        } else {
+            ServerCookie.appendCookieValue
+                (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
+                     cookie.getPath(), cookie.getDomain(), cookie.getComment(), 
+                     cookie.getMaxAge(), cookie.getSecure());
+        }
+        */
+        // START GlassFish 898
+        String cookieValue = getCookieString(cookie);
+        // END GlassFish 898
+
+        // 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.
+        /* GlassFish 898
+        addHeader("Set-Cookie", sb.toString());
+        */
+        // START GlassFish 898
+        addHeader("Set-Cookie", cookieValue);
+        // END GlassFish 898
+    }
+
+    /**
+     * Special method for adding a session cookie as we should be overriding 
+     * any previous 
+     * @param cookie
+     */
+    public void addSessionCookieInternal(final Cookie cookie) {
+        if (isCommitted())
+            return;
+
+        String name = cookie.getName();
+        final String headername = "Set-Cookie";
+        final String startsWith = name + "=";
+        final String cookieString = getCookieString(cookie);
+        boolean set = false;
+        MimeHeaders headers = coyoteResponse.getResponse().getHeaders();
+        int n = headers.size();
+        for (int i = 0; i < n; i++) {
+            if (headers.getName(i).toString().equals(headername)) {
+                if (headers.getValue(i).toString().startsWith(startsWith)) {
+                    headers.getValue(i).setString(cookieString);
+                    set = true;
+                }
+            }
+        }
+        if (!set) {
+            addHeader(headername, cookieString);
+        }
+
+
+    }
+
+    /**
+     * 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 (name == null || name.length() == 0) {
+            return;
+        }
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included) {
+            return;
+        }
+
+        if (format == null) {
+            format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER,
+                                          Locale.US);
+            format.setTimeZone(TimeZone.getTimeZone("GMT"));
+        }
+
+        addHeader(name, FastHttpDateFormat.formatDate(value, format));
+
+    }
+
+
+    /**
+     * 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 (name == null || name.length() == 0 || value == null) {
+            return;
+        }
+
+        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 (name == null || name.length() == 0) {
+            return;
+        }
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        addHeader(name, "" + value);
+
+    }
+
+
+    /**
+     * Has the specified header been set already in this response?
+     *
+     * @param name Name of the header to check
+     */
+    public boolean containsHeader(String name) {
+        return coyoteResponse.containsHeader(name);
+    }
+
+
+    @Override
+    public Supplier<Map<String, String>> getTrailerFields() {
+        return coyoteResponse.getTrailers();
+    }
+
+
+    @Override
+    public void setTrailerFields(Supplier<Map<String, String>> supplier) {
+        coyoteResponse.setTrailers(supplier);
+    }
+
+
+    /**
+     * 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))) {
+            String sessionVersion = null;
+            Map<String, String> sessionVersions = 
+                request.getSessionVersionsRequestAttribute();
+            if (sessionVersions != null) {
+                sessionVersion = RequestUtil.createSessionVersionString(
+                    sessionVersions);
+            }
+            return toEncoded(url,
+                              request.getSessionInternal().getIdInternal(),
+                              sessionVersion);
+        } 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)) {
+            // W3c spec clearly said 
+            if (url.equalsIgnoreCase("")){
+                url = absolute;
+            } else if (url.equals(absolute) && !hasPath(url)) {
+                url += '/';
+            }
+            String sessionVersion = null;
+            Map<String, String> sessionVersions = 
+                request.getSessionVersionsRequestAttribute();
+            if (sessionVersions != null) {
+                sessionVersion = RequestUtil.createSessionVersionString(
+                    sessionVersions);
+            }
+            return toEncoded(url,
+                              request.getSessionInternal().getIdInternal(),
+                              sessionVersion);
+        } 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);
+    }
+
+
+    /**
+     * Apply URL Encoding to the given URL without adding session identifier
+     * et al associated to this response.
+     *
+     * @param url URL to be encoded
+     */
+    public String encode(String url) {
+        return urlEncoder.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.sendAcknowledgement();
+
+    }
+
+
+    /**
+     * 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(rb.getString(LogFacade.CANNOT_CALL_SEND_ERROR_EXCEPTION));
+
+        // Ignore any call from an included servlet
+        if (included) {
+            return; 
+        }
+
+        setError();
+
+        coyoteResponse.setStatus(status);
+        // use encoding in GlassFish
+        coyoteResponse.getResponse().setHtmlEncodingCustomReasonPhrase(false);
+        coyoteResponse.setDetailMessage(HtmlEntityEncoder.encodeXSS(message));
+
+        // Clear any data content that has been buffered
+        resetBuffer();
+
+        // Cause the response to be finished (from the application perspective)
+        setSuspended(true);
+
+    }
+
+
+    /**
+     * Sends a temporary redirect to the specified redirect location URL.
+     *
+     * @param location Location URL to redirect to
+     *
+     * @throws IllegalStateException if this response has
+     *  already been committed
+     * @throws IOException if an input/output error occurs
+     */
+    public void sendRedirect(String location) throws IOException {
+        sendRedirect(location, true);
+    }
+
+
+    /**
+     * Sends a temporary or permanent redirect to the specified redirect
+     * location URL.
+     *
+     * @param location Location URL to redirect to
+     * @param isTemporary true if the redirect is supposed to be temporary,
+     * false if permanent
+     *
+     * @throws IllegalStateException if this response has
+     *  already been committed
+     * @throws IOException if an input/output error occurs
+     */    
+    public void sendRedirect(String location, boolean isTemporary)
+            throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException(rb.getString(LogFacade.CANNOT_CALL_SEND_REDIRECT_EXCEPTION));
+
+        // 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 {
+            /* RIMOD 4642650
+            String absolute = toAbsolute(location);
+            */
+            // START RIMOD 4642650
+            String absolute;
+            if (getContext().getAllowRelativeRedirect())
+                absolute = location;
+            else
+                absolute = toAbsolute(location);
+            // END RIMOD 4642650
+            if (isTemporary) {
+                setStatus(SC_MOVED_TEMPORARILY);
+            } else {
+                setStatus(SC_MOVED_PERMANENTLY);
+            }
+            setHeader("Location", absolute);
+
+            // According to RFC2616 section 10.3.3 302 Found,
+            // the response SHOULD contain a short hypertext note with
+            // a hyperlink to the new URI.
+            setContentType("text/html");
+            setLocale(Locale.getDefault());
+
+            String href = HtmlEntityEncoder.encodeXSS(absolute);
+            StringBuilder sb = new StringBuilder(150 + href.length());
+
+            sb.append("<html>\r\n");
+            sb.append("<head><title>Document moved</title></head>\r\n");
+            sb.append("<body><h1>Document moved</h1>\r\n");
+            sb.append("This document has moved <a href=\"");
+            sb.append(href);
+            sb.append("\">here</a>.<p>\r\n");
+            sb.append("</body>\r\n");
+            sb.append("</html>\r\n");
+
+            try {
+                getWriter().write(sb.toString());
+            } catch (IllegalStateException ise1) {
+                try {
+                    getOutputStream().print(sb.toString());
+                } catch (IllegalStateException ise2) {
+                    // ignore; the RFC says "SHOULD" so it is acceptable
+                    // to omit the body in case of an error
+                }
+            }
+        } 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 (name == null || name.length() == 0) {
+            return;
+        }
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included) {
+            return;
+        }
+
+        if (format == null) {
+            format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER,
+                                          Locale.US);
+            format.setTimeZone(TimeZone.getTimeZone("GMT"));
+        }
+
+        setHeader(name, FastHttpDateFormat.formatDate(value, format));
+
+    }
+
+
+    /**
+     * 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 (name == null || name.length() == 0 || value == null) {
+            return;
+        }
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        try {
+            String safeName = InputValidationUtil.getSafeHeaderName(name);
+            String safeValue = InputValidationUtil.getSafeHeaderValue(value);
+            coyoteResponse.setHeader(safeName, safeValue);
+        } catch (Exception e) {
+            try {
+                coyoteResponse.sendError(403, "Forbidden");
+            } catch (IOException ex) {
+                // just return
+            }
+        }
+
+    }
+
+
+    /**
+     * 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 (name == null || name.length() == 0) {
+            return;
+        }
+
+        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);
+        // use encoding in GlassFish
+        coyoteResponse.getResponse().setHtmlEncodingCustomReasonPhrase(false);
+        coyoteResponse.setDetailMessage(HtmlEntityEncoder.encodeXSS(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(final 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?
+        final Request hreq = request;
+        final Session session = hreq.getSessionInternal(false);
+        if (session == null) {
+            return false;
+        }
+        if (hreq.isRequestedSessionIdFromCookie() ||
+            getContext() != null && !getContext().isEnableURLRewriting()) {
+            return false;
+        }
+
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (
+                AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+
+                @Override
+                public Boolean run(){
+                    return Boolean.valueOf(doIsEncodeable(hreq, session, location));
+                }
+            })).booleanValue();
+        } else {
+            return doIsEncodeable(hreq, session, location);
+        }
+    }
+
+    private boolean doIsEncodeable(Request hreq, Session session,
+                                   String location){
+        // 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;
+
+        Context ctx = getContext();
+        if (ctx != null) {
+            String contextPath = ctx.getPath();
+            if (contextPath != null) {
+                String file = url.getFile();
+                if (file == null || !file.startsWith(contextPath)) {
+                    return false;
+                }
+                String sessionParamName = ctx.getSessionParameterName();
+                if (file.contains(";" + sessionParamName + "=" + session.getIdInternal())) {
+                    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
+     */
+    protected String toAbsolute(String location) {
+
+        if (location == null)
+            return location;
+
+        boolean leadingSlash = location.startsWith("/");
+
+        if (location.startsWith("//")) {
+            // Scheme relative, network-path reference in RFC 3986
+            redirectURLCC.recycle();
+            // Add the scheme
+            String scheme = getRedirectScheme();
+            try {
+                redirectURLCC.append(scheme, 0, scheme.length());
+                redirectURLCC.append(':');
+                redirectURLCC.append(location, 0, location.length());
+                return redirectURLCC.toString();
+            } catch (IOException e) {
+                IllegalArgumentException iae =
+                    new IllegalArgumentException(location);
+                iae.initCause(e);
+                throw iae;
+            }
+
+        } else if (leadingSlash || (location.indexOf("://") == -1)) {
+
+            redirectURLCC.recycle();
+
+            String scheme = getRedirectScheme();
+
+            String name = request.getServerName();
+            int port = request.getServerPort();
+
+            try {
+                redirectURLCC.append(scheme, 0, scheme.length());
+                redirectURLCC.append("://", 0, 3);
+                redirectURLCC.append(name, 0, name.length());
+                if (scheme.equals("http") && port != 80
+                    || scheme.equals("https") && port != 443) {
+                    redirectURLCC.append(':');
+                    String portS = port + "";
+                    redirectURLCC.append(portS, 0, portS.length());
+                }
+                if (!leadingSlash) {
+                    String relativePath = request.getDecodedRequestURI();
+                    int pos = relativePath.lastIndexOf('/');
+                    relativePath = relativePath.substring(0, pos);
+                    
+                    String encodedURI = null;
+                    final String frelativePath = relativePath;
+                    
+                     if (SecurityUtil.isPackageProtectionEnabled() ){
+                        try{
+                            encodedURI = AccessController.doPrivileged( 
+                                new PrivilegedExceptionAction<String>(){                                
+                                    public String run() throws IOException{
+                                        return urlEncoder.encodeURL(frelativePath);
+                                    }
+                           });   
+                        } catch (PrivilegedActionException pae){
+                            IllegalArgumentException iae =
+                                new IllegalArgumentException(location);
+                            iae.initCause(pae.getCause());
+                            throw iae;
+                        }
+                    } else {
+                        encodedURI = urlEncoder.encodeURL(relativePath);
+                    }
+                          
+                    redirectURLCC.append(encodedURI, 0, encodedURI.length());
+                    redirectURLCC.append('/');
+                }
+                redirectURLCC.append(location, 0, location.length());
+                normalize(redirectURLCC);
+            } catch (IOException e) {
+                IllegalArgumentException iae =
+                    new IllegalArgumentException(location);
+                iae.initCause(e);
+                throw iae;
+            }
+
+            return redirectURLCC.toString();
+
+        } else {
+
+            return location;
+
+        }
+
+    }
+
+
+    /**
+     * Returns the scheme for a redirect if it is not specified.
+     */
+    private String getRedirectScheme() {
+        String scheme = request.getScheme();
+
+        // START S1AS 6170450
+        if (getConnector() != null
+                && getConnector().getAuthPassthroughEnabled()) {
+            ProxyHandler proxyHandler = getConnector().getProxyHandler();
+            if (proxyHandler != null
+                    && proxyHandler.getSSLKeysize(request) > 0) {
+                scheme = "https";
+            }
+        }
+        // END S1AS 6170450
+
+        return scheme;
+    }
+
+
+    /**
+     * 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
+     */
+    protected String toEncoded(String url, String sessionId) {
+        return toEncoded(url, sessionId, null);
+    }
+
+
+    /**
+     * 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
+     * @param sessionVersion Session version to be included in the encoded URL
+     */
+    private String toEncoded(String url, String sessionId,
+                             String sessionVersion) {
+        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);
+        }
+
+        StringBuilder sb = new StringBuilder(path);
+        if( sb.length() > 0 ) { // jsessionid can't be first.
+            StandardContext ctx = (StandardContext) getContext();
+            String sessionParamName =
+                ctx != null ? ctx.getSessionParameterName() :
+                    Globals.SESSION_PARAMETER_NAME;
+            sb.append(";" + sessionParamName + "=");
+            sb.append(sessionId);
+            if (ctx != null && ctx.getJvmRoute() != null) {
+                sb.append('.').append(ctx.getJvmRoute());
+            }                    
+
+            // START SJSAS 6337561
+            String jrouteId = request.getHeader(Constants.PROXY_JROUTE);
+            if (jrouteId != null) {
+                sb.append(":");
+                sb.append(jrouteId);
+            }
+            // END SJSAS 6337561
+
+            final Session session = request.getSessionInternal(false);
+            if (session != null) {
+                String replicaLocation =
+                    (String) session.getNote(Globals.JREPLICA_SESSION_NOTE);
+                if (replicaLocation != null) {
+                    sb.append(Globals.JREPLICA_PARAMETER);
+                    sb.append(replicaLocation);
+                }
+            }
+
+            if (sessionVersion != null) {
+                sb.append(Globals.SESSION_VERSION_PARAMETER);
+                sb.append(sessionVersion);
+            }
+        }
+
+        sb.append(anchor);
+        sb.append(query);
+        return sb.toString();
+
+    }
+
+    /**
+     * Create an instance of CoyoteWriter
+     */
+    protected CoyoteWriter createWriter(OutputBuffer outbuf) {
+        return new CoyoteWriter(outbuf);
+    }
+
+    // START GlassFish 898
+    /**
+     * Gets the string representation of the given cookie.
+     *
+     * @param cookie The cookie whose string representation to get
+     *
+     * @return The cookie's string representation
+     */
+    protected String getCookieString(final Cookie cookie) {
+        String cookieValue = null;
+        final StringBuilder sb = new StringBuilder();
+
+        // TODO:  default these values for now.  update later.
+        final boolean versionOneStrictCompliance = CookieUtils.COOKIE_VERSION_ONE_STRICT_COMPLIANCE;
+        final boolean alwaysAddExpires = CookieUtils.ALWAYS_ADD_EXPIRES;
+        final boolean rfc6265Support = CookieUtils.RFC_6265_SUPPORT_ENABLED;
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            cookieValue = AccessController.doPrivileged(
+                new PrivilegedAction<String>() {
+                    public String run(){
+                        CookieSerializerUtils.serializeServerCookie(
+                            sb, versionOneStrictCompliance, rfc6265Support,
+                            alwaysAddExpires, cookie.getName(),
+                            cookie.getValue(), cookie.getVersion(), cookie.getPath(),
+                            cookie.getDomain(), cookie.getComment(),
+                            cookie.getMaxAge(), cookie.getSecure(),
+                            cookie.isHttpOnly());
+                        return sb.toString();
+                    }
+                });
+        } else {
+            CookieSerializerUtils.serializeServerCookie(
+                sb, versionOneStrictCompliance, rfc6265Support,
+                alwaysAddExpires, cookie.getName(),
+                cookie.getValue(), cookie.getVersion(), cookie.getPath(),
+                cookie.getDomain(), cookie.getComment(),
+                cookie.getMaxAge(), cookie.getSecure(),
+                cookie.isHttpOnly());
+            cookieValue = sb.toString();
+        }
+
+        return cookieValue;
+    }
+    // END GlassFish 898
+
+
+    // START GlassFish 896
+    /**
+     * Removes any Set-Cookie response headers whose value contains the
+     * string JSESSIONID
+     */
+    public void removeSessionCookies() {
+        String matchExpression = "^" + getContext().getSessionCookieName() + "=.*";
+        coyoteResponse.getResponse().getHeaders().removeHeaderMatches("Set-Cookie", matchExpression);
+        matchExpression = "^" +
+            org.apache.catalina.authenticator.Constants.SINGLE_SIGN_ON_COOKIE + "=.*";
+        coyoteResponse.getResponse().getHeaders().removeHeaderMatches("Set-Cookie", matchExpression);
+    }
+    // END GlassFish 896
+
+    /*
+     * Removes /./ and /../ sequences from absolute URLs.
+     * Code borrowed heavily from CoyoteAdapter.normalize()
+     */
+    private void normalize(CharChunk cc) {
+        // Strip query string and/or fragment first as doing it this way makes
+        // the normalization logic a lot simpler
+        int truncate = cc.indexOf('?');
+        if (truncate == -1) {
+            truncate = cc.indexOf('#');
+        }
+        char[] truncateCC = null;
+        if (truncate > -1) {
+            truncateCC = Arrays.copyOfRange(cc.getBuffer(),
+                    cc.getStart() + truncate, cc.getEnd());
+            cc.setEnd(cc.getStart() + truncate);
+        }
+
+        if (cc.endsWith("/.") || cc.endsWith("/..")) {
+            try {
+                cc.append('/');
+            } catch (IOException e) {
+                throw new IllegalArgumentException(cc.toString(), e);
+            }
+        }
+
+        char[] c = cc.getChars();
+        int start = cc.getStart();
+        int end = cc.getEnd();
+        int index = 0;
+        int startIndex = 0;
+
+        // Advance past the first three / characters (should place index just
+        // scheme://host[:port]
+
+        for (int i = 0; i < 3; i++) {
+            startIndex = cc.indexOf('/', startIndex + 1);
+        }
+
+        // Remove /./
+        index = startIndex;
+        while (true) {
+            index = cc.indexOf("/./", 0, 3, index);
+            if (index < 0) {
+                break;
+            }
+            copyChars(c, start + index, start + index + 2,
+                      end - start - index - 2);
+            end = end - 2;
+            cc.setEnd(end);
+        }
+
+        // Remove /../
+        index = startIndex;
+        int pos;
+        while (true) {
+            index = cc.indexOf("/../", 0, 4, index);
+            if (index < 0) {
+                break;
+            }
+            // Can't go above the server root
+            if (index == startIndex) {
+                throw new IllegalArgumentException();
+            }
+            int index2 = -1;
+            for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
+                if (c[pos] == (byte) '/') {
+                    index2 = pos;
+                }
+            }
+            copyChars(c, start + index2, start + index + 3,
+                      end - start - index - 3);
+            end = end + index2 - index - 3;
+            cc.setEnd(end);
+            index = index2;
+        }
+
+        // Add the query string and/or fragment (if present) back in
+        if (truncateCC != null) {
+            try {
+                cc.append(truncateCC, 0, truncateCC.length);
+            } catch (IOException ioe) {
+                throw new IllegalArgumentException(ioe);
+            }
+        }
+    }
+
+    private void copyChars(char[] c, int dest, int src, int len) {
+        for (int pos = 0; pos < len; pos++) {
+            c[pos + dest] = c[pos + src];
+        }
+    }
+
+    /**
+     * Determine if an absolute URL has a path component
+     */
+    private boolean hasPath(String uri) {
+        int pos = uri.indexOf("://");
+        if (pos < 0) {
+            return false;
+        }
+        pos = uri.indexOf('/', pos + 3);
+        if (pos < 0) {
+            return false;
+        }
+        return true;
+    }
+
+    private void log(String message, Throwable t) {
+        org.apache.catalina.Logger logger = null;
+        if (connector != null && connector.getContainer() != null) {
+            logger = connector.getContainer().getLogger();
+        }
+        String localName = "Response";
+        if (logger != null) {
+            logger.log(localName + " " + message, t,
+                org.apache.catalina.Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, localName + " " + message, t);
+        }
+    }
+
+
+    public void setUpgrade(boolean upgrade) {
+        this.upgrade = upgrade;
+    }
+
+    void disableWriteHandler() {
+        outputBuffer.disableWriteHandler();
+    }
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ResponseFacade.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ResponseFacade.java
new file mode 100644
index 0000000..87c3f8a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/ResponseFacade.java
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.security.SecurityUtil;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.*;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+
+
+/**
+ * Facade class that wraps a Coyote response object. 
+ * All methods are delegated to the wrapped response.
+ *
+ * @author Remy Maucherat
+ * @author Jean-Francois Arcand
+ * @version $Revision: 1.9 $ $Date: 2007/05/05 05:32:43 $
+ */
+
+
+public class ResponseFacade 
+    implements HttpServletResponse {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+
+
+    // ----------------------------------------------------------- DoPrivileged
+    
+    private final class SetContentTypePrivilegedAction
+            implements PrivilegedAction<Void> {
+
+        private String contentType;
+
+        public SetContentTypePrivilegedAction(String contentType){
+            this.contentType = contentType;
+        }
+
+        @Override
+        public Void run() {
+            response.setContentType(contentType);
+            return null;
+        }            
+    }
+     
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a wrapper for the specified response.
+     *
+     * @param response The response to be wrapped
+     */
+    public ResponseFacade(Response response) {
+        this.response = response;
+    }
+
+
+    // ----------------------------------------------- Class/Instance Variables
+
+
+    /**
+     * The wrapped response.
+     */
+    protected Response response = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    
+    /**
+     * Prevent cloning the facade.
+     */
+    @Override
+    protected Object clone() throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+      
+    
+    /**
+     * Clear facade.
+     */
+    public void clear() {
+        response = null;
+    }
+
+
+    public void finish() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        response.setSuspended(true);
+
+    }
+
+
+    public boolean isFinished() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.isSuspended();
+    }
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+    @Override
+    public String getCharacterEncoding() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.getCharacterEncoding();
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        //        if (isFinished())
+        //            throw new IllegalStateException
+        //                (/*sm.getString("responseFacade.finished")*/);
+
+        ServletOutputStream sos = response.getOutputStream();
+        if (isFinished())
+            response.setSuspended(true);
+        return (sos);
+    }
+
+    @Override
+    public PrintWriter getWriter() throws IOException {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        //        if (isFinished())
+        //            throw new IllegalStateException
+        //                (/*sm.getString("responseFacade.finished")*/);
+
+        PrintWriter writer = response.getWriter();
+        if (isFinished())
+            response.setSuspended(true);
+        return (writer);
+    }
+
+    @Override
+    public void setContentLength(int len) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.setContentLength(len);
+    }
+
+    @Override
+    public void setContentLengthLong(long len) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.setContentLengthLong(len);
+    }
+
+    @Override
+    public void setContentType(String type) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+        
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            AccessController.doPrivileged(new SetContentTypePrivilegedAction(type));
+        } else {
+            response.setContentType(type);            
+        }
+    }
+
+    @Override
+    public void setBufferSize(int size) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setBufferSize(size);
+    }
+
+    @Override
+    public int getBufferSize() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.getBufferSize();
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isFinished())
+            //            throw new IllegalStateException
+            //                (/*sm.getString("responseFacade.finished")*/);
+            return;
+        
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            try{
+                AccessController.doPrivileged(
+                        new PrivilegedExceptionAction<Void>(){
+
+                    public Void run() throws IOException{
+                        response.setAppCommitted(true);
+
+                        response.flushBuffer();
+                        return null;
+                    }
+                });
+            } catch(PrivilegedActionException e){
+                Exception ex = e.getException();
+                if (ex instanceof IOException){
+                    throw (IOException)ex;
+                }
+            }
+        } else {
+            response.setAppCommitted(true);
+
+            response.flushBuffer();            
+        }
+    }
+
+    @Override
+    public void resetBuffer() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.resetBuffer();
+    }
+
+    @Override
+    public boolean isCommitted() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return (response.isAppCommitted());
+    }
+
+    @Override
+    public void reset() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.reset();
+    }
+
+    @Override
+    public void setLocale(Locale loc) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.setLocale(loc);
+    }
+
+    @Override
+    public Locale getLocale() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.getLocale();
+    }
+
+    @Override
+    public void addCookie(Cookie cookie) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.addCookie(cookie);
+    }
+
+    @Override
+    public boolean containsHeader(String name) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.containsHeader(name);
+    }
+
+    @Override
+    public String encodeURL(String url) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.encodeURL(url);
+    }
+
+    @Override
+    public String encodeRedirectURL(String url) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.encodeRedirectURL(url);
+    }
+
+    @Override
+    public String encodeUrl(String url) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.encodeURL(url);
+    }
+
+    @Override
+    public String encodeRedirectUrl(String url) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.encodeRedirectURL(url);
+    }
+
+    @Override
+    public void sendError(int sc, String msg) throws IOException {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendError(sc, msg);
+    }
+
+    @Override
+    public void sendError(int sc) throws IOException {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendError(sc);
+    }
+
+    @Override
+    public void sendRedirect(String location) throws IOException {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendRedirect(location);
+    }
+
+    @Override
+    public void setDateHeader(String name, long date) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.setDateHeader(name, date);
+    }
+
+    @Override
+    public void addDateHeader(String name, long date) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.addDateHeader(name, date);
+    }
+
+    @Override
+    public void setHeader(String name, String value) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.setHeader(name, value);
+    }
+
+    @Override
+    public void addHeader(String name, String value) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.addHeader(name, value);
+    }
+
+    @Override
+    public void setIntHeader(String name, int value) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.setIntHeader(name, value);
+    }
+
+    @Override
+    public void addIntHeader(String name, int value) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.addIntHeader(name, value);
+    }
+
+    @Override
+    public void setStatus(int sc) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.setStatus(sc);
+    }
+
+    @Override
+    public void setStatus(int sc, String msg) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        if (isCommitted())
+            return;
+
+        response.setStatus(sc, msg);
+    }
+
+    @Override
+    public String getContentType() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.getContentType();
+    }
+
+    @Override
+    public void setCharacterEncoding(String arg0) {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        response.setCharacterEncoding(arg0);
+    }
+
+
+    // START SJSAS 6374990
+    @Override
+    public int getStatus() {
+
+        // Disallow operation if the object has gone out of scope
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+
+        return response.getStatus();
+    }
+
+    // END SJSAS 6374990
+
+    @Override
+    public String getHeader(String name) {
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+        return response.getHeader(name);
+    }
+
+    @Override
+    public Collection<String> getHeaders(String name) {
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+        return response.getHeaders(name);
+    }
+
+    @Override
+    public Collection<String> getHeaderNames() {
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+        return response.getHeaderNames();
+    }
+
+    @Override
+    public Supplier<Map<String, String>> getTrailerFields() {
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+        return response.getTrailerFields();
+    }
+
+    @Override
+    public void setTrailerFields(Supplier<Map<String, String>> supplier) {
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NULL_RESPONSE_OBJECT));
+        }
+        response.setTrailerFields(supplier);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/SessionTracker.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/SessionTracker.java
new file mode 100644
index 0000000..4493bfc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/SessionTracker.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.connector;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.SessionListener;
+
+import java.util.ArrayList;
+
+/**
+ * Class responsible for keeping track of the total number of active sessions
+ * associated with an HTTP request.
+ *
+ * <p>A given HTTP request may be associated with more than one active
+ * session if it has been dispatched to one or more foreign contexts.
+ *
+ * <p>The number of active sessions being tracked is used to determine whether
+ * or not any session information needs to be reflected in the response (e.g.,
+ * in the form of a response cookie). If no active sessions are associated
+ * with the request by the time the response is committed, no session
+ * information will be reflected in the response.
+ *
+ * See GlassFish Issue 896 for additional details.
+ */
+public class SessionTracker implements SessionListener {
+
+    // The number of currently tracked sessions
+    private volatile int count;
+
+    // The session id that is shared by all tracked sessions
+    private volatile String trackedSessionId;
+
+    private Response response;
+
+    /*
+     * The list of contexts whose sessions we're tracking.
+     *
+     * The size of this list will be greater than one only in cross
+     * context request dispatch scenarios
+     */
+    private ArrayList<String> contextNames = new ArrayList<String>(2);
+
+    /**
+     * Processes the given session event, by unregistering this SessionTracker
+     * as a session listener from the session that is the source of the event,
+     * and by decrementing the counter of currently tracked sessions.
+     *
+     * @param event The session event
+     */
+    public void sessionEvent(SessionEvent event) {
+
+        if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType())) {
+            return;
+        }
+
+        Session session = event.getSession();
+
+        synchronized (this) {
+            if (session.getIdInternal() != null
+                    && session.getIdInternal().equals(trackedSessionId)
+                    && session.getManager() != null
+                    && session.getManager().getContainer() != null
+                    && contextNames.contains(
+                            session.getManager().getContainer().getName())) {
+                count--;
+                if (count == 0) {
+                    trackedSessionId = null;
+                    if (response != null) {
+                        response.removeSessionCookies();
+                    }
+                }
+            }
+
+            session.removeSessionListener(this);
+        }
+    }
+
+    /**
+     * Gets the number of active sessions that are being tracked.
+     *
+     * @return The number of active sessions being tracked
+     */
+    public int getActiveSessions() {
+        return count;
+    }
+
+    /**
+     * Gets the id of the sessions that are being tracked.
+     *
+     * Notice that since all sessions are associated with the same request,
+     * albeit in different context, they all share the same id.
+     *
+     * @return The id of the sessions that are being tracked
+     */
+    public String getSessionId() {
+        return trackedSessionId;
+    }
+
+    /**
+     * Tracks the given session, by registering this SessionTracker as a
+     * listener with the given session, and by incrementing the counter of
+     * currently tracked sessions.
+     *
+     * @param session The session to track
+     */
+    public synchronized void track(Session session) {
+
+        if (trackedSessionId == null) {
+            trackedSessionId = session.getIdInternal();
+        } else if (!trackedSessionId.equals(session.getIdInternal())) {
+            throw new IllegalArgumentException("Should never reach here");
+        }
+
+        count++;
+
+        if (session.getManager() != null
+                && session.getManager().getContainer() != null
+                && session.getManager().getContainer().getName() != null) {
+            contextNames.add(session.getManager().getContainer().getName());
+        }
+
+        session.addSessionListener(this);
+    }
+
+    /**
+     * Associates the given response with this SessionTracker.
+     *
+     * If the number of tracked sessions drops to zero, this SessionTracker
+     * will remove the Set-Cookie from the given response.
+     *
+     * @param response The response from which to remove the Set-Cookie
+     * header if the number of tracked sessions drops to zero
+     */
+    public synchronized void setResponse(Response response) {
+        this.response = response;
+    }
+
+    /**
+     * Resets this session tracker.
+     */
+    public synchronized void reset() {
+        count = 0;
+        trackedSessionId = null;
+        contextNames.clear();
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/connector/WebConnectionImpl.java b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/WebConnectionImpl.java
new file mode 100644
index 0000000..ce10922
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/connector/WebConnectionImpl.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.connector;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpUpgradeHandler;
+import javax.servlet.http.WebConnection;
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.Context;
+
+/**
+ * Implementation of WebConnection for Servlet 3.1
+ *
+ * @author Amy Roh
+ * @author Shing Wai Chan
+ * @version $Revision: 1.23 $ $Date: 2007/07/09 20:46:45 $
+ */
+public class WebConnectionImpl implements WebConnection {
+
+    private ServletInputStream inputStream;
+
+    private ServletOutputStream outputStream;
+
+    private Request request;
+
+    private Response response;
+
+    private final AtomicBoolean isClosed = new AtomicBoolean();
+
+    // ----------------------------------------------------------- Constructor
+
+    public WebConnectionImpl(ServletInputStream inputStream, ServletOutputStream outputStream) {
+        this.inputStream = inputStream;
+        this.outputStream = outputStream;
+    }
+
+    /**
+     * Returns an input stream for this web connection.
+     *
+     * @return a ServletInputStream for reading binary data
+     *
+     * @exception java.io.IOException if an I/O error occurs
+     */
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+        return inputStream;
+    }
+
+    /**
+     * Returns an output stream for this web connection.
+     *
+     * @return a ServletOutputStream for writing binary data
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException{
+        return outputStream;
+    }
+
+    @Override
+    public void close() throws Exception {
+        // Make sure we run close logic only once
+        if (isClosed.compareAndSet(false, true)) {
+            if ((request != null) && (request.isUpgrade())) {
+                Context context = request.getContext();
+                HttpUpgradeHandler httpUpgradeHandler =
+                        request.getHttpUpgradeHandler();
+                Exception exception = null;
+                try {
+                    try {
+                        context.fireContainerEvent(
+                            ContainerEvent.BEFORE_UPGRADE_HANDLER_DESTROYED,
+                            httpUpgradeHandler);
+                        httpUpgradeHandler.destroy();
+                    } finally {
+                        context.fireContainerEvent(
+                            ContainerEvent.AFTER_UPGRADE_HANDLER_DESTROYED,
+                            httpUpgradeHandler);
+                    }
+                    request.setUpgrade(false);
+                    if (response != null) {
+                        response.setUpgrade(false);
+                    }
+                } finally {
+                    try {
+                        inputStream.close();
+                    } catch(Exception ex) {
+                        exception = ex;
+                    }
+                    try {
+                        outputStream.close();
+                    } catch(Exception ex) {
+                        exception = ex;
+                    }
+                    context.fireContainerEvent(
+                        ContainerEvent.PRE_DESTROY, httpUpgradeHandler);
+                    request.resumeAfterService();
+                }
+                if (exception != null) {
+                    throw exception;
+                }
+            }
+        }
+    }
+
+    public void setRequest(Request req) {
+        request = req;
+    }
+
+    public void setResponse(Response res) {
+        response = res;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationContext.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationContext.java
new file mode 100644
index 0000000..d1f19e4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationContext.java
@@ -0,0 +1,1059 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.*;
+import org.apache.catalina.deploy.ApplicationParameter;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.ServerInfo;
+
+import javax.naming.directory.DirContext;
+import javax.servlet.*;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Standard implementation of <code>ServletContext</code> that represents
+ * a web application's execution environment.  An instance of this class is
+ * associated with each instance of <code>StandardContext</code>.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.15.2.1 $ $Date: 2008/04/17 18:37:06 $
+ */
+public class ApplicationContext implements ServletContext {
+
+    // ----------------------------------------------------------- Constructors
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+    /**
+     * Construct a new instance of this class, associated with the specified
+     * Context instance.
+     *
+     * @param context The associated Context instance
+     */
+    public ApplicationContext(StandardContext context) {
+        super();
+        this.context = context;
+
+        setAttribute("com.sun.faces.useMyFaces",
+                     Boolean.valueOf(context.isUseMyFaces()));
+    }
+
+    public StandardContext getStandardContext() {
+        return context;
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+    // START PWC 1.2
+    /*
+    private static final SecurityPermission GET_UNWRAPPED_CONTEXT_PERMISSION =
+        new SecurityPermission("getUnwrappedContext");
+    */
+    // END PWC 1.2
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The context attributes for this context.
+     */
+    private Map<String, Object> attributes =
+        new ConcurrentHashMap<String, Object>();
+
+    /**
+     * List of read only attributes for this context.
+     */
+    private HashMap<String, String> readOnlyAttributes =
+        new HashMap<String, String>();
+
+    /**
+     * Lock for synchronizing attributes and readOnlyAttributes
+     */
+    private Object attributesLock = new Object();
+
+    /**
+     * The Context instance with which we are associated.
+     */
+    private StandardContext context = null;
+
+    /**
+     * Empty String collection to serve as the basis for empty enumerations.
+     * <strong>DO NOT ADD ANY ELEMENTS TO THIS COLLECTION!</strong>
+     */
+    private static final List<String> emptyString = Collections.emptyList();
+
+    /**
+     * Empty Servlet collection to serve as the basis for empty enumerations.
+     * <strong>DO NOT ADD ANY ELEMENTS TO THIS COLLECTION!</strong>
+     */
+    private static final List<Servlet> emptyServlet = Collections.emptyList();
+
+    /**
+     * The facade around this object.
+     */
+    private ServletContext facade = new ApplicationContextFacade(this);
+
+    /**
+     * The merged context initialization parameters for this Context.
+     */
+    private ConcurrentMap<String, String> parameters =
+        new ConcurrentHashMap<String, String>();
+
+    private boolean isRestricted;
+
+    
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Return the resources object that is mapped to a specified path.
+     * The path must begin with a "/" and is interpreted as relative to the
+     * current context root.
+     */
+    public DirContext getResources() {
+        return context.getResources();
+    }
+
+
+    // ------------------------------------------------- ServletContext Methods
+
+    /**
+     * Return the value of the specified context attribute, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the context attribute to return
+     */
+    @Override
+    public Object getAttribute(String name) {
+        return attributes.get(name);
+    }
+
+    /**
+     * Return an enumeration of the names of the context attributes
+     * associated with this context.
+     */
+    @Override
+    public Enumeration<String> getAttributeNames() {
+        return new Enumerator<String>(attributes.keySet(), true);
+    }
+
+    /**
+     * Returns the context path of the web application.
+     *
+     * <p>The context path is the portion of the request URI that is used
+     * to select the context of the request. The context path always comes
+     * first in a request URI. The path starts with a "/" character but does
+     * not end with a "/" character. For servlets in the default (root)
+     * context, this method returns "".
+     *
+     * <p>It is possible that a servlet container may match a context by
+     * more than one context path. In such cases the
+     * {@link javax.servlet.http.HttpServletRequest#getContextPath()}
+     * will return the actual context path used by the request and it may
+     * differ from the path returned by this method.
+     * The context path returned by this method should be considered as the
+     * prime or preferred context path of the application.
+     *
+     * @see javax.servlet.http.HttpServletRequest#getContextPath()
+     */
+    @Override
+    public String getContextPath() {
+        return context.getPath();
+    }
+
+    /**
+     * Return a <code>ServletContext</code> object that corresponds to a
+     * specified URI on the server.  This method allows servlets to gain
+     * access to the context for various parts of the server, and as needed
+     * obtain <code>RequestDispatcher</code> objects or resources from the
+     * context.  The given path must be absolute (beginning with a "/"),
+     * and is interpreted based on our virtual host's document root.
+     *
+     * @param uri Absolute URI of a resource on the server
+     */
+    @Override
+    public ServletContext getContext(String uri) {
+        return context.getContext(uri);
+    }
+
+    /**
+     * Return the value of the specified initialization parameter, or
+     * <code>null</code> if this parameter does not exist.
+     *
+     * @param name Name of the initialization parameter to retrieve
+     */
+    @Override
+    public String getInitParameter(final String name) {
+        return parameters.get(name);
+    }
+
+    /**
+     * Return the names of the context's initialization parameters, or an
+     * empty enumeration if the context has no initialization parameters.
+     */
+    @Override
+    public Enumeration<String> getInitParameterNames() {
+        return (new Enumerator<String>(parameters.keySet()));
+    }
+
+    /**
+     * @return true if the context initialization parameter with the given
+     * name and value was set successfully on this ServletContext, and false
+     * if it was not set because this ServletContext already contains a
+     * context initialization parameter with a matching name
+     */
+    @Override
+    public boolean setInitParameter(String name, String value) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+
+        return parameters.putIfAbsent(name, value) == null;
+    }
+
+    /**
+     * Return the major version of the Java Servlet API that we implement.
+     */
+    @Override
+    public int getMajorVersion() {
+        return (Constants.MAJOR_VERSION);
+    }
+
+    /**
+     * Return the minor version of the Java Servlet API that we implement.
+     */
+    @Override
+    public int getMinorVersion() {
+        return (Constants.MINOR_VERSION);
+    }
+
+    /**
+     * Gets the major version of the Servlet specification that the
+     * application represented by this ServletContext is based on.
+     */
+    @Override
+    public int getEffectiveMajorVersion() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getEffectiveMajorVersion();
+    }
+        
+    /**
+     * Gets the minor version of the Servlet specification that the
+     * application represented by this ServletContext is based on.
+     */
+    @Override
+    public int getEffectiveMinorVersion() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getEffectiveMinorVersion();
+    }
+
+    /**
+     * Return the MIME type of the specified file, or <code>null</code> if
+     * the MIME type cannot be determined.
+     *
+     * @param file Filename for which to identify a MIME type
+     */
+    @Override
+    public String getMimeType(String file) {
+        return context.getMimeType(file);
+    }
+
+    /**
+     * Return a <code>RequestDispatcher</code> object that acts as a
+     * wrapper for the named servlet.
+     *
+     * @param name Name of the servlet for which a dispatcher is requested
+     */
+    @Override
+    public RequestDispatcher getNamedDispatcher(String name) {
+        return context.getNamedDispatcher(name);
+    }
+
+    /**
+     * @param path The virtual path to be translated
+     *
+     * @return the real path corresponding to the given virtual path, or
+     * <code>null</code> if the container was unable to perform the
+     * translation
+     */
+    @Override
+    public String getRealPath(String path) {
+        return context.getRealPath(path);
+    }
+
+    /**
+     * Return a <code>RequestDispatcher</code> instance that acts as a
+     * wrapper for the resource at the given path.  The path must begin
+     * with a "/" or be empty, and is interpreted as relative to the current
+     * context root.
+     *
+     * @param path The path to the desired resource.
+     */
+    @Override
+    public RequestDispatcher getRequestDispatcher(String path) {
+        return context.getRequestDispatcher(path);
+    }
+
+    /**
+     * Return the URL to the resource that is mapped to a specified path.
+     * The path must begin with a "/" and is interpreted as relative to the
+     * current context root.
+     *
+     * @param path The path to the desired resource
+     *
+     * @exception MalformedURLException if the path is not given
+     *  in the correct form
+     */
+    @Override
+    public URL getResource(String path)
+        throws MalformedURLException {
+        return context.getResource(path);
+    }
+
+    /**
+     * Return the requested resource as an <code>InputStream</code>.  The
+     * path must be specified according to the rules described under
+     * <code>getResource</code>.  If no such resource can be identified,
+     * return <code>null</code>.
+     *
+     * @param path The path to the desired resource.
+     */
+    @Override
+    public InputStream getResourceAsStream(String path) {
+        return context.getResourceAsStream(path);
+    }
+
+    /**
+     * Return a Set containing the resource paths of resources member of the
+     * specified collection. Each path will be a String starting with
+     * a "/" character. The returned set is immutable.
+     *
+     * @param path Collection path
+     */
+    @Override
+    public Set<String> getResourcePaths(String path) {
+        return context.getResourcePaths(path);
+    }
+
+    /**
+     * Return the name and version of the servlet container.
+     */
+    @Override
+    public String getServerInfo() {
+        return (ServerInfo.getServerInfo());
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    @Override
+    public Servlet getServlet(String name) {
+        return (null);
+    }
+
+    /**
+     * Return the display name of this web application.
+     */
+    @Override
+    public String getServletContextName() {
+        return (context.getDisplayName());
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    @Override
+    public Enumeration<String> getServletNames() {
+        return (new Enumerator<String>(emptyString));
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    @Override
+    public Enumeration<Servlet> getServlets() {
+        return (new Enumerator<Servlet>(emptyServlet));
+    }
+
+    /**
+     * Writes the specified message to a servlet log file.
+     *
+     * @param message Message to be written
+     */
+    @Override
+    public void log(String message) {
+        context.log(message);
+    }
+
+    /**
+     * Writes the specified exception and message to a servlet log file.
+     *
+     * @param exception Exception to be reported
+     * @param message Message to be written
+     *
+     * @deprecated As of Java Servlet API 2.1, use
+     *  <code>log(String, Throwable)</code> instead
+     */
+    @Override
+    public void log(Exception exception, String message) {   
+        context.log(exception, message);
+    }
+
+    /**
+     * Writes the specified message and exception to a servlet log file.
+     *
+     * @param message Message to be written
+     * @param throwable Exception to be reported
+     */
+    @Override
+    public void log(String message, Throwable throwable) {
+        context.log(message, throwable);
+    }
+
+    /**
+     * Remove the context attribute with the specified name, if any.
+     *
+     * @param name Name of the context attribute to be removed
+     */
+    @Override
+    public void removeAttribute(String name) {
+        Object value = null;
+        boolean found = false;
+
+        // Remove the specified attribute
+        synchronized (attributesLock) {
+            // Check for read only attribute
+            if (readOnlyAttributes.containsKey(name))
+                return;
+            value = attributes.remove(name);
+            if (value == null)
+                return;
+        }
+
+        // Notify interested application event listeners
+        List<EventListener> listeners = context.getApplicationEventListeners();
+        if (listeners.isEmpty()) {
+            return;
+        }
+
+        ServletContextAttributeEvent event =
+            new ServletContextAttributeEvent(context.getServletContext(),
+                                             name, value);
+        Iterator<EventListener> iter = listeners.iterator();
+        while (iter.hasNext()) {
+            EventListener eventListener = iter.next();
+            if (!(eventListener instanceof ServletContextAttributeListener)) {
+                continue;
+            }
+            ServletContextAttributeListener listener =
+                (ServletContextAttributeListener) eventListener;
+            try {
+                context.fireContainerEvent(
+                    ContainerEvent.BEFORE_CONTEXT_ATTRIBUTE_REMOVED,
+                    listener);
+                listener.attributeRemoved(event);
+                context.fireContainerEvent(
+                    ContainerEvent.AFTER_CONTEXT_ATTRIBUTE_REMOVED,
+                    listener);
+            } catch (Throwable t) {
+                context.fireContainerEvent(
+                    ContainerEvent.AFTER_CONTEXT_ATTRIBUTE_REMOVED,
+                    listener);
+                // FIXME - should we do anything besides log these?
+                log.log(Level.WARNING,
+                        LogFacade.ATTRIBUTES_EVENT_LISTENER_EXCEPTION, t);
+            }
+        }
+
+    }
+
+    /**
+     * Bind the specified value with the specified context attribute name,
+     * replacing any existing value for that name.
+     *
+     * @param name Attribute name to be bound
+     * @param value New attribute value to be bound
+     */
+    @Override
+    public void setAttribute(String name, Object value) {
+
+        // Name cannot be null
+        if (name == null)
+            throw new NullPointerException
+                    (rb.getString(LogFacade.NULL_NAME_EXCEPTION));
+
+        // Null value is the same as removeAttribute()
+        if (value == null) {
+            removeAttribute(name);
+            return;
+        }
+
+        Object oldValue = null;
+        boolean replaced = false;
+
+        // Add or replace the specified attribute
+        synchronized (attributesLock) {
+            // Check for read only attribute
+            if (readOnlyAttributes.containsKey(name))
+                return;
+            oldValue = attributes.get(name);
+            if (oldValue != null)
+                replaced = true;
+            attributes.put(name, value);
+        }
+        
+        if (name.equals(Globals.CLASS_PATH_ATTR) ||
+                name.equals(Globals.JSP_TLD_URI_TO_LOCATION_MAP)) {
+            setAttributeReadOnly(name);
+        }
+        
+        // Notify interested application event listeners
+        List<EventListener> listeners =
+            context.getApplicationEventListeners();
+        if (listeners.isEmpty()) {
+            return;
+        }
+
+        ServletContextAttributeEvent event = null;
+        if (replaced) {
+            event =
+                new ServletContextAttributeEvent(context.getServletContext(),
+                                                 name, oldValue);
+        } else {
+            event =
+                new ServletContextAttributeEvent(context.getServletContext(),
+                                                 name, value);
+        }
+
+        Iterator<EventListener> iter = listeners.iterator(); 
+        while (iter.hasNext()) {
+            EventListener eventListener = iter.next();
+            if (!(eventListener instanceof ServletContextAttributeListener)) {
+                continue;
+	    }
+            ServletContextAttributeListener listener =
+                (ServletContextAttributeListener) eventListener;
+            try {
+                if (replaced) {
+                    context.fireContainerEvent(
+                        ContainerEvent.BEFORE_CONTEXT_ATTRIBUTE_REPLACED,
+                        listener);
+                    listener.attributeReplaced(event);
+                    context.fireContainerEvent(
+                        ContainerEvent.AFTER_CONTEXT_ATTRIBUTE_REPLACED,
+                        listener);
+                } else {
+                    context.fireContainerEvent(
+                        ContainerEvent.BEFORE_CONTEXT_ATTRIBUTE_ADDED,
+                        listener);
+                    listener.attributeAdded(event);
+                    context.fireContainerEvent(
+                        ContainerEvent.AFTER_CONTEXT_ATTRIBUTE_ADDED,
+                        listener);
+                }
+            } catch (Throwable t) {
+                if (replaced) {
+                    context.fireContainerEvent(
+                        ContainerEvent.AFTER_CONTEXT_ATTRIBUTE_REPLACED,
+                        listener);
+                } else {
+                    context.fireContainerEvent(
+                        ContainerEvent.AFTER_CONTEXT_ATTRIBUTE_ADDED,
+                        listener);
+                }
+                // FIXME - should we do anything besides log these?
+                log.log(Level.WARNING, LogFacade.ATTRIBUTES_EVENT_LISTENER_EXCEPTION, t);
+            }
+        }
+    }
+
+    /*
+     * Adds the servlet with the given name and class name to this
+     * servlet context.
+     */
+    @Override
+    public ServletRegistration.Dynamic addServlet(
+            String servletName, String className) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.addServlet(servletName, className);
+    }
+
+    /*
+     * Registers the given servlet instance with this ServletContext
+     * under the given <tt>servletName</tt>.
+     */
+    @Override
+    public ServletRegistration.Dynamic addServlet(
+            String servletName, Servlet servlet) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.addServlet(servletName, servlet);
+    }
+
+    /*
+     * Adds the servlet with the given name and class type to this
+     * servlet context.
+     */
+    @Override
+    public ServletRegistration.Dynamic addServlet(String servletName,
+            Class <? extends Servlet> servletClass) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.addServlet(servletName, servletClass);
+    }
+
+    /*
+     * Adds the servlet with the given name and jsp file to this
+     * servlet context.
+    */
+    @Override
+    public ServletRegistration.Dynamic addJspFile(
+            String servletName, String jspFile) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.addJspFile(servletName, jspFile);
+    }
+
+    /**
+     * Instantiates the given Servlet class and performs any required
+     * resource injection into the new Servlet instance before returning
+     * it.
+     */
+    @Override
+    public <T extends Servlet> T createServlet(Class<T> clazz)
+            throws ServletException {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.createServlet(clazz);
+    }
+
+    /**
+     * Gets the ServletRegistration corresponding to the servlet with the
+     * given <tt>servletName</tt>.
+     */
+    @Override
+    public ServletRegistration getServletRegistration(String servletName) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getServletRegistration(servletName);
+    }
+
+    /**
+     * Gets a Map of the ServletRegistration objects corresponding to all
+     * currently registered servlets.
+     */
+    @Override
+    public Map<String, ? extends ServletRegistration> getServletRegistrations() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getServletRegistrations();
+    }
+
+    /**
+     * Adds the filter with the given name and class name to this servlet
+     * context.
+     */
+    @Override
+    public FilterRegistration.Dynamic addFilter(
+            String filterName, String className) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.addFilter(filterName, className);
+    }
+    
+    /*
+     * Registers the given filter instance with this ServletContext
+     * under the given <tt>filterName</tt>.
+     */
+    @Override
+    public FilterRegistration.Dynamic addFilter(
+            String filterName, Filter filter) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.addFilter(filterName, filter);
+    }
+
+    /**
+     * Adds the filter with the given name and class type to this servlet
+     * context.
+     */
+    @Override
+    public FilterRegistration.Dynamic addFilter(String filterName,
+            Class <? extends Filter> filterClass) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.addFilter(filterName, filterClass);
+    }
+    
+    /**
+     * Instantiates the given Filter class and performs any required
+     * resource injection into the new Filter instance before returning
+     * it.
+     */
+    @Override
+    public <T extends Filter> T createFilter(Class<T> clazz)
+            throws ServletException {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.createFilter(clazz);
+    }
+
+    /**
+     * Gets the FilterRegistration corresponding to the filter with the
+     * given <tt>filterName</tt>.
+     */
+    @Override
+    public FilterRegistration getFilterRegistration(String filterName) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getFilterRegistration(filterName);
+    }
+
+    /**
+     * Gets a Map of the FilterRegistration objects corresponding to all
+     * currently registered filters.
+     */
+    @Override
+    public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getFilterRegistrations();
+    }
+
+    /**
+     * Gets the <tt>SessionCookieConfig</tt> object through which various
+     * properties of the session tracking cookies created on behalf of this
+     * <tt>ServletContext</tt> may be configured.
+     */
+    @Override
+    public SessionCookieConfig getSessionCookieConfig() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getSessionCookieConfig();        
+    }
+  
+    /**
+     * Sets the session tracking modes that are to become effective for this
+     * <tt>ServletContext</tt>.
+     */
+    @Override
+    public void setSessionTrackingModes(
+            Set<SessionTrackingMode> sessionTrackingModes) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        context.setSessionTrackingModes(sessionTrackingModes);
+    }
+
+    /**
+     * Gets the session tracking modes that are supported by default for this
+     * <tt>ServletContext</tt>.
+     *
+     * @return set of the session tracking modes supported by default for
+     * this <tt>ServletContext</tt>
+     */
+    @Override
+    public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getDefaultSessionTrackingModes();
+    }
+
+    /**
+     * Gets the session tracking modes that are in effect for this
+     * <tt>ServletContext</tt>.
+     *
+     * @return set of the session tracking modes in effect for this
+     * <tt>ServletContext</tt>
+     */
+    @Override
+    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getEffectiveSessionTrackingModes();
+    }
+
+    /**
+     * Adds the listener with the given class name to this ServletContext.
+     */
+    @Override
+    public void addListener(String className) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        context.addListener(className);
+    }
+
+    /**
+     * Adds the given listener to this ServletContext.
+     */
+    @Override
+    public <T extends EventListener> void addListener(T t) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        context.addListener(t);
+    }
+
+    /**
+     * Adds a listener of the given class type to this ServletContext.
+     */
+    @Override
+    public void addListener(Class <? extends EventListener> listenerClass) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        context.addListener(listenerClass);
+    }
+
+    /**
+     * Instantiates the given EventListener class and performs any
+     * required resource injection into the new EventListener instance
+     * before returning it.
+     */
+    @Override
+    public <T extends EventListener> T createListener(Class<T> clazz)
+            throws ServletException {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.createListener(clazz);
+    }
+
+    /**
+     * Gets the <code>&lt;jsp-config&gt;</code> related configuration
+     * that was aggregated from the <code>web.xml</code> and
+     * <code>web-fragment.xml</code> descriptor files of the web application
+     * represented by this ServletContext.
+     */
+    @Override
+    public JspConfigDescriptor getJspConfigDescriptor() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getJspConfigDescriptor();
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getClassLoader();
+    }
+
+    @Override
+    public void declareRoles(String... roleNames) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        context.declareRoles(roleNames);
+    }
+
+    @Override
+    public String getVirtualServerName() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getVirtualServerName();
+    }
+
+    @Override
+    public int getSessionTimeout() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getSessionTimeout();
+    }
+
+    @Override
+    public void setSessionTimeout(int sessionTimeout) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        context.setSessionTimeout(sessionTimeout);
+    }
+
+    @Override
+    public String getRequestCharacterEncoding() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getRequestCharacterEncoding();
+    }
+
+    @Override
+    public void setRequestCharacterEncoding(String encoding) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        context.setRequestCharacterEncoding(encoding);
+    }
+
+    @Override
+    public String getResponseCharacterEncoding() {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        return context.getResponseCharacterEncoding();
+    }
+
+    @Override
+    public void setResponseCharacterEncoding(String encoding) {
+        if (isRestricted) {
+            throw new UnsupportedOperationException(
+                    rb.getString(LogFacade.UNSUPPORTED_OPERATION_EXCEPTION));
+        }
+        context.setResponseCharacterEncoding(encoding);
+    }
+
+    // START PWC 1.2
+    /**
+     * Gets the underlying StandardContext to which this ApplicationContext is
+     * delegating.
+     *
+     * @return The underlying StandardContext
+     */
+    /*
+    public StandardContext getUnwrappedContext() {
+
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(GET_UNWRAPPED_CONTEXT_PERMISSION);
+        }
+
+        return this.context;        
+    }
+    */
+    // END PWC 1.2
+
+
+    // -------------------------------------------------------- Package Methods
+
+    /**
+     * Clear all application-created attributes.
+     */
+    void clearAttributes() {
+
+        // Create list of attributes to be removed
+        ArrayList<String> list = new ArrayList<String>();
+        synchronized (attributesLock) {
+            Iterator<String> iter = attributes.keySet().iterator();
+            while (iter.hasNext()) {
+                list.add(iter.next());
+            }
+        }
+
+        // Remove application originated attributes
+        // (read only attributes will be left in place)
+        Iterator<String> keys = list.iterator();
+        while (keys.hasNext()) {
+            String key = keys.next();
+            removeAttribute(key);
+        }        
+    }
+    
+    /**
+     * Return the facade associated with this ApplicationContext.
+     */
+    protected ServletContext getFacade() {
+        return this.facade;
+    }
+
+    /**
+     * Set an attribute as read only.
+     */
+    void setAttributeReadOnly(String name) {
+        synchronized (attributesLock) {
+            if (attributes.containsKey(name))
+                readOnlyAttributes.put(name, name);
+        }
+    }
+
+    void setRestricted(boolean isRestricted) {
+        this.isRestricted = isRestricted;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationContextFacade.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationContextFacade.java
new file mode 100644
index 0000000..581f811
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationContextFacade.java
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.security.SecurityUtil;
+
+import javax.servlet.*;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Facade object which masks the internal <code>ApplicationContext</code>
+ * object from the web application.
+ *
+ * @author Remy Maucherat
+ * @author Jean-Francois Arcand
+ * @version $Revision: 1.7.6.1 $ $Date: 2008/04/17 18:37:06 $
+ */
+
+public final class ApplicationContextFacade
+    implements ServletContext {
+
+    private static final Logger log = LogFacade.getLogger();
+        
+    // ---------------------------------------------------------- Attributes
+    /**
+     * Cache Class object used for reflection.
+     */
+    private static HashMap<String, Class<?>[]> classCache = new HashMap<String, Class<?>[]>();
+    
+    static {
+        Class<?>[] clazz = new Class[]{String.class};
+        classCache.put("addFilter", new Class[]{String.class, String.class});
+        classCache.put("addListener", clazz);
+        classCache.put("addServlet", new Class[]{String.class, String.class});
+        classCache.put("addJspFile", new Class[]{String.class, String.class});
+        classCache.put("createFilter", new Class[]{Class.class});
+        classCache.put("createListener", new Class[]{Class.class});
+        classCache.put("createServlet", new Class[]{Class.class});
+        classCache.put("declareRoles", new Class<?>[] {(new String[0]).getClass()});
+        classCache.put("getAttribute", clazz);
+        classCache.put("getContext", clazz);
+        classCache.put("getFilterRegistration", clazz);
+        classCache.put("getInitParameter", clazz);
+        classCache.put("getMimeType", clazz);
+        classCache.put("getNamedDispatcher", clazz);
+        classCache.put("getRealPath", clazz);
+        classCache.put("getResourcePaths", clazz);
+        classCache.put("getResource", clazz);
+        classCache.put("getResourceAsStream", clazz);
+        classCache.put("getRequestDispatcher", clazz);
+        classCache.put("getServlet", clazz);
+        classCache.put("getServletRegistration", clazz);
+        classCache.put("log", clazz);        
+        classCache.put("removeAttribute", clazz);
+        classCache.put("setAttribute", new Class[]{String.class, Object.class});
+        classCache.put("setInitParameter", new Class[]{String.class, String.class});
+        classCache.put("setSessionTrackingModes", new Class[]{Set.class});
+        classCache.put("getSessionTimeout", new Class[]{});
+        classCache.put("setSessionTimeout", new Class[]{Integer.class});
+        classCache.put("getRequestCharacterEncoding", new Class[]{});
+        classCache.put("setRequestCharacterEncoding", new Class[]{String.class});
+        classCache.put("getResponseCharacterEncoding", new Class[]{});
+        classCache.put("setResponseCharacterEncoding", new Class[]{String.class});
+    }
+    
+    /**
+     * Cache method object.
+     */
+    private HashMap<String, Method> objectCache;
+    
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class, associated with the specified
+     * Context instance.
+     *
+     * @param context The associated Context instance
+     */
+    public ApplicationContextFacade(ApplicationContext context) {
+        super();
+        this.context = context;
+        
+        objectCache = new HashMap<String, Method>();
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped application context.
+     */
+    private ApplicationContext context = null;
+    
+
+
+    // ------------------------------------------------- ServletContext Methods
+
+    @Override
+    public String getContextPath() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getContextPath", null);
+        } else {
+            return context.getContextPath();
+        }
+    }
+
+
+    @Override
+    public ServletContext getContext(String uripath) {
+        ServletContext theContext = null;
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            theContext = (ServletContext)
+                doPrivileged("getContext", new Object[]{uripath});
+        } else {
+            theContext = context.getContext(uripath);
+        }
+        if ((theContext != null) &&
+            (theContext instanceof ApplicationContext)){
+            theContext = ((ApplicationContext)theContext).getFacade();
+        }
+        return (theContext);
+    }
+
+
+    @Override
+    public int getMajorVersion() {
+        return context.getMajorVersion();
+    }
+
+
+    @Override
+    public int getMinorVersion() {
+        return context.getMinorVersion();
+    }
+
+
+    /**
+     * Gets the major version of the Servlet specification that the
+     * application represented by this ServletContext is based on.
+     */
+    @Override
+    public int getEffectiveMajorVersion() {
+        return context.getEffectiveMajorVersion();
+    }
+    
+    
+    /**
+     * Gets the minor version of the Servlet specification that the
+     * application represented by this ServletContext is based on.
+     */
+    @Override
+    public int getEffectiveMinorVersion() {
+        return context.getEffectiveMinorVersion();
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public String getMimeType(String file) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String)doPrivileged("getMimeType", new Object[]{file});
+        } else {
+            return context.getMimeType(file);
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public Set<String> getResourcePaths(String path) {
+        if (SecurityUtil.isPackageProtectionEnabled()){
+            return (Set<String>)doPrivileged("getResourcePaths",
+                                             new Object[]{path});
+        } else {
+            return context.getResourcePaths(path);
+        }
+    }
+
+
+    @Override
+    public URL getResource(String path)
+        throws MalformedURLException {
+        if (Globals.IS_SECURITY_ENABLED) {
+            try {
+                return (URL) invokeMethod(context, "getResource", 
+                                          new Object[]{path});
+            } catch(Throwable t) {
+                if (t instanceof MalformedURLException){
+                    throw (MalformedURLException)t;
+                }
+                return null;
+            }
+        } else {
+            return context.getResource(path);
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public InputStream getResourceAsStream(String path) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (InputStream) doPrivileged("getResourceAsStream", 
+                                              new Object[]{path});
+        } else {
+            return context.getResourceAsStream(path);
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public RequestDispatcher getRequestDispatcher(final String path) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (RequestDispatcher) doPrivileged("getRequestDispatcher", 
+                                                    new Object[]{path});
+        } else {
+            return context.getRequestDispatcher(path);
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public RequestDispatcher getNamedDispatcher(String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (RequestDispatcher) doPrivileged("getNamedDispatcher", 
+                                                    new Object[]{name});
+        } else {
+            return context.getNamedDispatcher(name);
+        }
+    }
+
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    @Override
+    @Deprecated
+    public Servlet getServlet(String name)
+        throws ServletException {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            try {
+                return (Servlet) invokeMethod(context, "getServlet", 
+                                              new Object[]{name});
+            } catch (Throwable t) {
+                if (t instanceof ServletException) {
+                    throw (ServletException) t;
+                }
+                return null;
+            }
+        } else {
+            return context.getServlet(name);
+        }
+    }
+
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    @Deprecated
+    public Enumeration<Servlet> getServlets() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Enumeration<Servlet>) doPrivileged("getServlets", null);
+        } else {
+            return context.getServlets();
+        }
+    }
+
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    @Deprecated
+    public Enumeration<String> getServletNames() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Enumeration<String>) doPrivileged("getServletNames", null);
+        } else {
+            return context.getServletNames();
+        }
+   }
+
+
+    @Override
+    public void log(String msg) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("log", new Object[]{msg} );
+        } else {
+            context.log(msg);
+        }
+    }
+
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, use
+     *  <code>log(String, Throwable)</code> instead
+     */
+    @Override
+    @Deprecated
+    public void log(Exception exception, String msg) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("log", new Class[]{Exception.class, String.class}, 
+                         new Object[]{exception,msg});
+        } else {
+            context.log(exception, msg);
+        }
+    }
+
+
+    @Override
+    public void log(String message, Throwable throwable) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("log", new Class[]{String.class, Throwable.class}, 
+                         new Object[]{message, throwable});
+        } else {
+            context.log(message, throwable);
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public String getRealPath(String path) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getRealPath", new Object[]{path});
+        } else {
+            return context.getRealPath(path);
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public String getServerInfo() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getServerInfo", null);
+        } else {
+            return context.getServerInfo();
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public String getInitParameter(String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getInitParameter", 
+                                         new Object[]{name});
+        } else {
+            return context.getInitParameter(name);
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public Enumeration<String> getInitParameterNames() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Enumeration<String>) doPrivileged(
+                "getInitParameterNames", null);
+        } else {
+            return context.getInitParameterNames();
+        }
+    }
+
+    /**
+     * @return true if the context initialization parameter with the given
+     * name and value was set successfully on this ServletContext, and false
+     * if it was not set because this ServletContext already contains a
+     * context initialization parameter with a matching name
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public boolean setInitParameter(String name, String value) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return ((Boolean) doPrivileged(
+                "setInitParameter", new Object[]{name, value})).booleanValue();
+        } else {
+            return context.setInitParameter(name, value);
+        }
+    }
+
+
+    @Override
+    public Object getAttribute(String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return doPrivileged("getAttribute", new Object[]{name});
+        } else {
+            return context.getAttribute(name);
+        }
+     }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public Enumeration<String> getAttributeNames() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Enumeration<String>) doPrivileged(
+                "getAttributeNames", null);
+        } else {
+            return context.getAttributeNames();
+        }
+    }
+
+
+    @Override
+    public void setAttribute(String name, Object object) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("setAttribute", new Object[]{name,object});
+        } else {
+            context.setAttribute(name, object);
+        }
+    }
+
+
+    @Override
+    public void removeAttribute(String name) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("removeAttribute", new Object[]{name});
+        } else {
+            context.removeAttribute(name);
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public String getServletContextName() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getServletContextName", null);
+        } else {
+            return context.getServletContextName();
+        }
+    }
+
+
+    /*
+     * Adds the servlet with the given name and class name to this
+     * servlet context.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public ServletRegistration.Dynamic addServlet(
+            String servletName, String className) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (ServletRegistration.Dynamic) doPrivileged(
+                "addServlet", new Object[] {servletName, className});
+        } else {
+            return context.addServlet(servletName, className);
+        }
+    }
+
+
+    /*
+     * Registers the given servlet instance with this ServletContext
+     * under the given <tt>servletName</tt>.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public ServletRegistration.Dynamic addServlet(
+            String servletName, Servlet servlet) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (ServletRegistration.Dynamic) doPrivileged(
+                "addServlet", new Class[]{String.class, Servlet.class},
+                new Object[] {servletName, servlet});
+        } else {
+            return context.addServlet(servletName, servlet);
+        }
+    }
+
+
+    /*
+     * Adds the servlet with the given name and class type to this
+     * servlet context.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public ServletRegistration.Dynamic addServlet(String servletName,
+            Class <? extends Servlet> servletClass) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (ServletRegistration.Dynamic) doPrivileged(
+                "addServlet", new Class[]{String.class, Class.class},
+                new Object[] {servletName, servletClass});
+        } else {
+            return context.addServlet(servletName, servletClass);
+        }
+    }
+
+    /*
+     * Adds the servlet with the given name and jsp file to this
+     * servlet context.
+    */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public ServletRegistration.Dynamic addJspFile(
+            String servletName, String jspFile) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (ServletRegistration.Dynamic) doPrivileged(
+                    "addJspFile", new Object[] {servletName, jspFile});
+        } else {
+            return context.addJspFile(servletName, jspFile);
+        }
+    }
+
+
+    /**
+     * Instantiates the given Servlet class and performs any required
+     * resource injection into the new Servlet instance before returning
+     * it.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public <T extends Servlet> T createServlet(Class<T> clazz)
+            throws ServletException {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (T) doPrivileged(
+                "createServlet", new Object[] {clazz});
+        } else {
+            return context.createServlet(clazz);
+        }
+    }
+
+
+    /**
+     * Gets the ServletRegistration corresponding to the servlet with the
+     * given <tt>servletName</tt>.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public ServletRegistration getServletRegistration(String servletName) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (ServletRegistration) doPrivileged(
+                "getServletRegistration", new Object[] {servletName});
+        } else {
+            return context.getServletRegistration(servletName);
+        }
+    }
+
+
+    /**
+     * Gets a Map of the ServletRegistration objects corresponding to all
+     * currently registered servlets.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public Map<String, ? extends ServletRegistration> getServletRegistrations() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Map<String, ServletRegistration>) doPrivileged(
+                "getServletRegistrations", null);
+        } else {
+            return context.getServletRegistrations();
+        }
+    }
+
+
+    /**
+     * Adds the filter with the given name and class name to this servlet
+     * context.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public FilterRegistration.Dynamic addFilter(
+            String filterName, String className) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (FilterRegistration.Dynamic) doPrivileged(
+                "addFilter", new Object[] {filterName, className});
+        } else {
+            return context.addFilter(filterName, className);
+        }
+    }
+    
+
+    /*
+     * Registers the given filter instance with this ServletContext
+     * under the given <tt>filterName</tt>.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public FilterRegistration.Dynamic addFilter(
+            String filterName, Filter filter) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (FilterRegistration.Dynamic) doPrivileged(
+                "addFilter", new Class[]{String.class, Filter.class},
+                new Object[] {filterName, filter});
+        } else {
+            return context.addFilter(filterName, filter);
+        }
+    }
+
+
+    /**
+     * Adds the filter with the given name and class type to this servlet
+     * context.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public FilterRegistration.Dynamic addFilter(String filterName,
+            Class <? extends Filter> filterClass) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (FilterRegistration.Dynamic) doPrivileged(
+                "addFilter", new Class[]{String.class, Class.class},
+                new Object[] {filterName, filterClass});
+        } else {
+            return context.addFilter(filterName, filterClass);
+        }
+    }
+
+
+    /**
+     * Instantiates the given Filter class and performs any required
+     * resource injection into the new Filter instance before returning
+     * it.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public <T extends Filter> T createFilter(Class<T> clazz)
+            throws ServletException {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (T) doPrivileged(
+                "createFilter", new Object[] {clazz});
+        } else {
+            return context.createFilter(clazz);
+        }
+    }
+
+
+    /**
+     * Gets the FilterRegistration corresponding to the filter with the
+     * given <tt>filterName</tt>.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public FilterRegistration getFilterRegistration(String filterName) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (FilterRegistration) doPrivileged(
+                "getFilterRegistration", new Object[] {filterName});
+        } else {
+            return context.getFilterRegistration(filterName);
+        }
+    }
+
+
+    /**
+     * Gets a Map of the FilterRegistration objects corresponding to all
+     * currently registered filters.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Map<String, FilterRegistration>) doPrivileged(
+                "getFilterRegistrations", null);
+        } else {
+            return context.getFilterRegistrations();
+        }
+    }
+
+    
+    /**
+     * Gets the <tt>SessionCookieConfig</tt> object through which various
+     * properties of the session tracking cookies created on behalf of this
+     * <tt>ServletContext</tt> may be configured.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public SessionCookieConfig getSessionCookieConfig() {        
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (SessionCookieConfig) doPrivileged(
+                "getSessionCookieConfig", null);
+        } else {
+            return context.getSessionCookieConfig();
+        }
+    }
+    
+
+    /**
+     * Sets the session tracking modes that are to become effective for this
+     * <tt>ServletContext</tt>.
+     */
+    @Override
+    public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("setSessionTrackingModes",
+                    new Object[]{sessionTrackingModes});
+        } else {
+            context.setSessionTrackingModes(sessionTrackingModes);
+        }
+    }
+
+
+    /**
+     * Gets the session tracking modes that are supported by default for this
+     * <tt>ServletContext</tt>.
+     *
+     * @return set of the session tracking modes supported by default for
+     * this <tt>ServletContext</tt>
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Set<SessionTrackingMode>)
+                doPrivileged("getDefaultSessionTrackingModes", null);
+        } else {
+            return context.getDefaultSessionTrackingModes();
+        }
+    }
+
+
+    /**
+     * Gets the session tracking modes that are in effect for this
+     * <tt>ServletContext</tt>.
+     *
+     * @return set of the session tracking modes in effect for this
+     * <tt>ServletContext</tt>
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Set<SessionTrackingMode>)
+                doPrivileged("getEffectiveSessionTrackingModes", null);
+        } else {
+            return context.getEffectiveSessionTrackingModes();
+        }
+    }
+
+
+    /**
+     * Adds the listener with the given class name to this ServletContext.
+     */
+    @Override
+    public void addListener(String className) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("addListener",
+                    new Object[]{className});
+        } else {
+            context.addListener(className);
+        }
+    }
+
+
+    /**
+     * Adds the given listener to this ServletContext.
+     */
+    @Override
+    public <T extends EventListener> void addListener(T t) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("addListener",
+                    new Class[]{EventListener.class},
+                    new Object[]{t.getClass().getName()});
+        } else {
+            context.addListener(t);
+        }
+    }
+
+
+    /**
+     * Adds a listener of the given class type to this ServletContext.
+     */
+    @Override
+    public void addListener(Class <? extends EventListener> listenerClass) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("addListener",
+                    new Class[]{Class.class},
+                    new Object[]{listenerClass.getName()});
+        } else {
+            context.addListener(listenerClass);
+        }
+    }
+
+
+    /**
+     * Instantiates the given EventListener class and performs any
+     * required resource injection into the new EventListener instance
+     * before returning it.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public <T extends EventListener> T createListener(Class<T> clazz)
+            throws ServletException {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (T) doPrivileged(
+                "createListener", new Object[] {clazz});
+        } else {
+            return context.createListener(clazz);
+        }
+    }
+
+
+    /**
+     * Gets the <code>&lt;jsp-config&gt;</code> related configuration
+     * that was aggregated from the <code>web.xml</code> and
+     * <code>web-fragment.xml</code> descriptor files of the web application
+     * represented by this ServletContext.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public JspConfigDescriptor getJspConfigDescriptor() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (JspConfigDescriptor) doPrivileged("getJspConfigDescriptor",
+                    null);
+        } else {
+            return context.getJspConfigDescriptor();
+        }
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
+    public ClassLoader getClassLoader() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (ClassLoader) doPrivileged("getClassLoader", null);
+        } else {
+            return context.getClassLoader();
+        }
+    }
+
+
+    @Override
+    public void declareRoles(String... roleNames) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("declareRoles", roleNames);
+        } else {
+            context.declareRoles(roleNames);
+        }
+    }
+
+    @Override
+    public String getVirtualServerName() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String)doPrivileged("getVirtualServerName", null);
+        } else {
+            return context.getVirtualServerName();
+        }
+    }
+
+    @Override
+    public int getSessionTimeout() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (Integer)doPrivileged("getSessionTimeout", null);
+        } else {
+            return context.getSessionTimeout();
+        }
+    }
+
+    @Override
+    public void setSessionTimeout(int sessionTimeout) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("getSessionTimeout", null);
+        } else {
+            context.setSessionTimeout(sessionTimeout);
+        }
+    }
+
+    @Override
+    public String getRequestCharacterEncoding() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String)doPrivileged("getRequestCharacterEncoding", null);
+        } else {
+            return context.getRequestCharacterEncoding();
+        }
+    }
+
+    @Override
+    public void setRequestCharacterEncoding(String encoding) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("setRequestCharacterEncoding", null);
+        } else {
+            context.setRequestCharacterEncoding(encoding);
+        }
+    }
+
+    @Override
+    public String getResponseCharacterEncoding() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String)doPrivileged("getResponseCharacterEncoding", null);
+        } else {
+            return context.getResponseCharacterEncoding();
+        }
+    }
+
+    @Override
+    public void setResponseCharacterEncoding(String encoding) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("setResponseCharacterEncoding", null);
+        } else {
+            context.setResponseCharacterEncoding(encoding);
+        }
+    }
+
+    // START PWC 1.2
+    /**
+     * Gets the underlying StandardContext to which this
+     * ApplicationContextFacade is ultimately delegating.
+     *
+     * @return The underlying StandardContext
+     */
+    /*
+    public StandardContext getUnwrappedContext() {
+        return context.getUnwrappedContext();
+    }
+    */
+    // END PWC 1.2
+
+       
+    /**
+     * Use reflection to invoke the requested method. Cache the method object 
+     * to speed up the process
+     *                   will be invoked
+     * @param methodName The method to call.
+     * @param params The arguments passed to the called method.
+     */
+    private Object doPrivileged(final String methodName, Object[] params){
+        try{
+            return invokeMethod(context, methodName, params);
+        }catch(Throwable t){
+            throw new RuntimeException(t.getMessage());
+        }
+    }
+
+    
+    /**
+     * Use reflection to invoke the requested method. Cache the method object 
+     * to speed up the process
+     * @param appContext The AppliationContext object on which the method
+     *                   will be invoked
+     * @param methodName The method to call.
+     * @param params The arguments passed to the called method.
+     */
+    private Object invokeMethod(ApplicationContext appContext,
+                                final String methodName, 
+                                Object[] params) 
+        throws Throwable{
+
+        try{
+            Method method = objectCache.get(methodName);
+            if (method == null){
+                method = appContext.getClass()
+                    .getMethod(methodName, classCache.get(methodName));
+                objectCache.put(methodName, method);
+            }
+            
+            return executeMethod(method,appContext,params);
+        } catch (Exception ex){
+            handleException(ex, methodName);
+            return null;
+        }
+    }
+    
+    /**
+     * Use reflection to invoke the requested method. Cache the method object 
+     * to speed up the process
+     * @param methodName The method to call.
+     * @param clazz The list of argument classes for the given method
+     * @param params The arguments passed to the called method.
+     */    
+    private Object doPrivileged(final String methodName, 
+                                final Class<?>[] clazz,
+                                Object[] params){
+
+        try{
+            Method method = context.getClass().getMethod(methodName, clazz);
+            return executeMethod(method,context,params);
+        } catch (Exception ex){
+            try{
+                handleException(ex, methodName);
+            }catch (Throwable t){
+                throw new RuntimeException(t.getMessage());
+            }
+            return null;
+        }
+    }
+    
+    
+    /**
+     * Executes the method of the specified <code>ApplicationContext</code>
+     * @param method The method object to be invoked.
+     * @param context The AppliationContext object on which the method
+     *                   will be invoked
+     * @param params The arguments passed to the called method.
+     */
+    private Object executeMethod(final Method method, 
+                                 final ApplicationContext context,
+                                 final Object[] params) 
+            throws PrivilegedActionException, 
+                   IllegalAccessException,
+                   InvocationTargetException {
+                                     
+        if (Globals.IS_SECURITY_ENABLED){
+           return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>(){
+                public Object run() throws IllegalAccessException, InvocationTargetException{
+                    return method.invoke(context,  params);
+                }
+            });
+        } else {
+            return method.invoke(context, params);
+        }        
+    }
+
+    
+    /**
+     * Throw the real exception.
+     * @param ex The current exception
+     */
+    private void handleException(Exception ex, String methodName)
+	    throws Throwable {
+
+        Throwable realException;
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "ApplicationContextFacade." + methodName, ex);
+        }
+
+	if (ex instanceof PrivilegedActionException) {
+            ex = ((PrivilegedActionException) ex).getException();
+	}
+
+        if (ex instanceof InvocationTargetException) {
+            realException =
+		((InvocationTargetException) ex).getTargetException();
+        } else {
+            realException = ex;
+        }   
+
+        throw realException;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationDispatcher.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationDispatcher.java
new file mode 100644
index 0000000..87939db
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationDispatcher.java
@@ -0,0 +1,1139 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.*;
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.RequestFacade;
+import org.apache.catalina.connector.ResponseFacade;
+import org.apache.catalina.util.InstanceSupport;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.text.MessageFormat;
+import javax.servlet.http.HttpServletMapping;
+
+
+import static org.apache.catalina.InstanceEvent.EventType.AFTER_DISPATCH_EVENT;
+import org.apache.catalina.connector.MappingImpl;
+import org.glassfish.grizzly.http.server.util.Mapper;
+import org.glassfish.grizzly.http.server.util.MappingData;
+import org.glassfish.grizzly.http.util.CharChunk;
+import org.glassfish.grizzly.http.util.MessageBytes;
+
+/**
+ * Standard implementation of <code>RequestDispatcher</code> that allows a
+ * request to be forwarded to a different resource to create the ultimate
+ * response, or to include the output of another resource in the response
+ * from this resource.  This implementation allows application level servlets
+ * to wrap the request and/or response objects that are passed on to the
+ * called resource, as long as the wrapping classes extend
+ * <code>javax.servlet.ServletRequestWrapper</code> and
+ * <code>javax.servlet.ServletResponseWrapper</code>.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.16 $ $Date: 2007/02/26 22:57:08 $
+ */
+
+public final class ApplicationDispatcher
+    implements RequestDispatcher {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // This attribute corresponds to a String[] which acts like a stack
+    // containing the last two pushed elements
+    public static final String LAST_DISPATCH_REQUEST_PATH_ATTR =
+        "org.apache.catalina.core.ApplicationDispatcher.lastDispatchRequestPathAttr";
+
+    protected class PrivilegedDispatch implements PrivilegedExceptionAction<Void> {
+
+        private ServletRequest request;
+        private ServletResponse response;
+        private DispatcherType dispatcherType;
+
+        PrivilegedDispatch(ServletRequest request, ServletResponse response,
+                           DispatcherType dispatcherType) {
+            this.request = request;
+            this.response = response;
+            this.dispatcherType = dispatcherType;
+        }
+
+        public Void run() throws java.lang.Exception {
+            doDispatch(request, response, dispatcherType);
+            return null;
+        }
+    }
+
+    protected class PrivilegedInclude implements PrivilegedExceptionAction<Void> {
+
+        private ServletRequest request;
+        private ServletResponse response;
+
+        PrivilegedInclude(ServletRequest request, ServletResponse response) {
+            this.request = request;
+            this.response = response;
+        }
+
+        public Void run() throws ServletException, IOException {
+            doInclude(request,response);
+            return null;
+        }
+    }
+
+    /**
+     * Used to pass state when the request dispatcher is used. Using instance
+     * variables causes threading issues and state is too complex to pass and
+     * return single ServletRequest or ServletResponse objects.
+     */
+    private static class State {
+
+        /**
+         * Outermost request that will be passed on to the invoked servlet
+         */
+        ServletRequest outerRequest = null;
+
+        /**
+         * Outermost response that will be passed on to the invoked servlet.
+         */
+        ServletResponse outerResponse = null;
+        
+        /**
+         * Request wrapper we have created and installed (if any).
+         */
+        ServletRequest wrapRequest = null;
+
+        /**
+         * Response wrapper we have created and installed (if any).
+         */
+        ServletResponse wrapResponse = null;
+        
+        /**
+         * The type of dispatch we are performing
+         */
+        DispatcherType dispatcherType;
+
+        /**
+         * Outermost HttpServletRequest in the chain
+         */
+        HttpServletRequest hrequest = null;
+
+        /**
+         * Outermost HttpServletResponse in the chain
+         */
+        HttpServletResponse hresponse = null;
+
+        State(ServletRequest request, ServletResponse response,
+              DispatcherType dispatcherType) {
+            this.outerRequest = request;
+            this.outerResponse = response;
+            this.dispatcherType = dispatcherType;
+        }
+    }
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class, configured according to the
+     * specified parameters.  If both servletPath and pathInfo are
+     * <code>null</code>, it will be assumed that this RequestDispatcher
+     * was acquired by name, rather than by path.
+     *
+     * @param wrapper The Wrapper associated with the resource that will
+     *  be forwarded to or included (required)
+     * @param mappingForDispatch the mapping for this dispatch
+     * @param requestURI The request URI to this resource (if any)
+     * @param servletPath The revised servlet path to this resource (if any)
+     * @param pathInfo The revised extra path information to this resource
+     *  (if any)
+     * @param queryString Query string parameters included with this request
+     *  (if any)
+     * @param name Servlet name (if a named dispatcher was created)
+     *  else <code>null</code>
+     */
+    public ApplicationDispatcher
+        (Wrapper wrapper, HttpServletMapping mappingForDispatch, String requestURI, String servletPath,
+         String pathInfo, String queryString, String name) {
+        super();
+
+        // Save all of our configuration parameters
+        this.wrapper = wrapper;
+        this.mappingForDispatch = mappingForDispatch;
+        this.context = (Context) wrapper.getParent();
+        this.requestURI = requestURI;
+        this.servletPath = servletPath;
+        this.pathInfo = pathInfo;
+        this.queryString = queryString;
+        this.name = name;
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "servletPath= " + neutralizeForLog(this.servletPath) + ", pathInfo= "
+                    + neutralizeForLog(this.pathInfo) + ", queryString= " + neutralizeForLog(queryString) + ", name= "
+                    + neutralizeForLog(this.name));
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    //START OF 6364900
+    /**
+     * is this dispatch cross context
+     */
+    private Boolean crossContextFlag = null;
+    //END OF 6364900
+
+    /**
+     * The Context this RequestDispatcher is associated with.
+     */
+    private Context context = null;
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.ApplicationDispatcher/1.0";
+
+    /**
+     * The servlet name for a named dispatcher.
+     */
+    private String name = null;
+
+    /**
+     * The extra path information for this RequestDispatcher.
+     */
+    private String pathInfo = null;
+
+    /**
+     * The query string parameters for this RequestDispatcher.
+     */
+    private String queryString = null;
+
+    /**
+     * The request URI for this RequestDispatcher.
+     */
+    private String requestURI = null;
+
+    /**
+     * The servlet path for this RequestDispatcher.
+     */
+    private String servletPath = null;
+
+    /**
+     * The Wrapper associated with the resource that will be forwarded to
+     * or included.
+     */
+    private Wrapper wrapper = null;
+    
+    private HttpServletMapping mappingForDispatch;
+    
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the descriptive information about this implementation.
+     */
+    public String getInfo() {
+        return (this.info);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Forwards the given request and response to the resource
+     * for which this dispatcher was acquired.
+     *
+     * <p>Any runtime exceptions, IOException, or ServletException thrown
+     * by the target will be propagated to the caller.
+     *
+     * @param request The request to be forwarded
+     * @param response The response to be forwarded
+     *
+     * @throws IOException if an input/output error occurs
+     * @throws ServletException if a servlet exception occurs
+     */
+    public void forward(ServletRequest request, ServletResponse response)
+            throws ServletException, IOException {
+        dispatch(request, response, DispatcherType.FORWARD);
+    }
+
+    /**
+     * Dispatches the given request and response to the resource
+     * for which this dispatcher was acquired.
+     *
+     * <p>Any runtime exceptions, IOException, or ServletException thrown
+     * by the target will be propagated to the caller.
+     *
+     * @param request The request to be forwarded
+     * @param response The response to be forwarded
+     * @param dispatcherType The type of dispatch to be performed
+     *
+     * @throws IOException if an input/output error occurs
+     * @throws ServletException if a servlet exception occurs
+     * @throws IllegalArgumentException if the dispatcher type is different
+     * from FORWARD, ERROR, and ASYNC
+     */
+    public void dispatch(ServletRequest request, ServletResponse response,
+                  DispatcherType dispatcherType)
+            throws ServletException, IOException {
+
+        if (DispatcherType.FORWARD != dispatcherType &&
+                DispatcherType.ERROR != dispatcherType &&
+                DispatcherType.ASYNC != dispatcherType) {
+            throw new IllegalArgumentException("Illegal dispatcher type");
+        }
+
+        boolean isCommit = (DispatcherType.FORWARD == dispatcherType ||
+            DispatcherType.ERROR == dispatcherType);
+
+        if (Globals.IS_SECURITY_ENABLED) {
+            try {
+                PrivilegedDispatch dp = new PrivilegedDispatch(
+                    request, response, dispatcherType);
+                AccessController.doPrivileged(dp);
+                // START SJSAS 6374990
+                if (isCommit && !request.isAsyncStarted()) {
+                    ApplicationDispatcherForward.commit(request, response,
+                        context, wrapper);
+                }
+                // END SJSAS 6374990
+            } catch (PrivilegedActionException pe) {
+                Exception e = pe.getException();
+                if (e instanceof ServletException)
+                    throw (ServletException) e;
+                throw (IOException) e;
+            }
+        } else {
+            doDispatch(request, response, dispatcherType);
+            // START SJSAS 6374990
+            if (isCommit && !request.isAsyncStarted()) {
+                ApplicationDispatcherForward.commit(request, response,
+                    context, wrapper);
+            }
+            // END SJSAS 6374990
+        }
+    }
+
+    private void doDispatch(ServletRequest request, ServletResponse response,
+                            DispatcherType dispatcherType)
+        throws ServletException, IOException {
+
+        if (DispatcherType.ASYNC != dispatcherType) {
+            // Reset any output that has been buffered, but keep
+            // headers/cookies
+            if (response.isCommitted()) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Forward on committed response --> ISE");
+                throw new IllegalStateException
+                        (rb.getString(LogFacade.ILLEGAL_STATE_EXCEPTION));
+            }
+
+            try {
+                response.resetBuffer();
+            } catch (IllegalStateException e) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE,
+                           "Forward resetBuffer() returned ISE: " + e.toString(), e);
+                throw e;
+            }
+        }
+
+        if (DispatcherType.INCLUDE != dispatcherType) {
+            DispatchTargetsInfo dtInfo =
+                    (DispatchTargetsInfo)request.getAttribute(
+                    LAST_DISPATCH_REQUEST_PATH_ATTR);
+            if (dtInfo == null) {
+                dtInfo = new DispatchTargetsInfo();
+                request.setAttribute(LAST_DISPATCH_REQUEST_PATH_ATTR, dtInfo);
+            }
+
+            if (servletPath == null && pathInfo == null) {
+                // Handle an HTTP named dispatcher forward
+                dtInfo.addDispatchTarget(wrapper.getServletName(), true);
+            } else {
+                dtInfo.addDispatchTarget(getCombinedPath(), false);
+            }
+        }
+
+        // Set up to handle the specified request and response
+        State state = new State(request, response, dispatcherType);
+
+        ServletRequest sr = wrapRequest(state);
+        wrapResponse(state);
+
+        // Identify the HTTP-specific request and response objects (if any)
+        HttpServletRequest hrequest = state.hrequest;
+        HttpServletResponse hresponse = state.hresponse;
+
+        if ((hrequest == null) || (hresponse == null)) {
+            // Handle a non-HTTP forward
+            processRequest(request, response, state);
+        } else if ((servletPath == null) && (pathInfo == null)) {
+            // Handle an HTTP named dispatcher forward
+            ApplicationHttpRequest wrequest = (ApplicationHttpRequest)sr;
+            wrequest.setRequestURI(hrequest.getRequestURI());
+            wrequest.setContextPath(hrequest.getContextPath());
+            wrequest.setServletPath(hrequest.getServletPath());
+            wrequest.setPathInfo(hrequest.getPathInfo());
+            wrequest.setQueryString(hrequest.getQueryString());
+            
+            processRequest(request, response, state);
+
+        } else {
+            // Handle an HTTP path-based forward
+            ApplicationHttpRequest wrequest = (ApplicationHttpRequest)sr;
+
+            // If the request is being FORWARD- or ASYNC-dispatched for 
+            // the first time, initialize it with the required request
+            // attributes
+            if ((DispatcherType.FORWARD == dispatcherType &&
+                    hrequest.getAttribute(
+                        RequestDispatcher.FORWARD_REQUEST_URI) == null) ||
+                    (DispatcherType.ASYNC == dispatcherType &&
+                        hrequest.getAttribute(
+                            AsyncContext.ASYNC_REQUEST_URI) == null)) { 
+                wrequest.initSpecialAttributes(hrequest.getRequestURI(),
+                                               hrequest.getContextPath(),
+                                               hrequest.getServletPath(),
+                                               hrequest.getPathInfo(),
+                                               hrequest.getQueryString());
+            }
+
+            String targetContextPath = context.getPath();
+            // START IT 10395
+            RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(wrequest);
+            String originContextPath = null;
+            if (reqFacHelper != null) {
+                originContextPath = reqFacHelper.getContextPath(false);
+            } else {
+                originContextPath = wrequest.getContextPath();
+            }
+            if (originContextPath != null &&
+                    originContextPath.equals(targetContextPath)) {
+                targetContextPath = hrequest.getContextPath();
+            }
+            // END IT 10395
+            wrequest.setContextPath(targetContextPath);
+            wrequest.setRequestURI(requestURI);
+            wrequest.setServletPath(servletPath);
+            wrequest.setPathInfo(pathInfo);
+            if (queryString != null) {
+                wrequest.setQueryString(queryString);
+                wrequest.setQueryParams(queryString);
+            }
+
+            processRequest(request, response, state);
+
+        }
+
+        recycleRequestWrapper(state);
+        unwrapRequest(state);
+        unwrapResponse(state);
+    }
+
+
+    /**
+     * Prepare the request based on the filter configuration.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @throws IOException if an input/output error occurs
+     * @throws ServletException if a servlet error occurs
+     */
+    private void processRequest(ServletRequest request, 
+                                ServletResponse response,
+                                State state)
+        throws IOException, ServletException {
+                
+        if (request != null) {
+            if (state.dispatcherType != DispatcherType.ERROR) {
+                state.outerRequest.setAttribute(
+                    Globals.DISPATCHER_REQUEST_PATH_ATTR,
+                    getCombinedPath());
+                invoke(state.outerRequest, response, state);
+            } else {
+                invoke(state.outerRequest, response, state);
+            }
+        }
+    }
+
+    /**
+     * Combines the servletPath and the pathInfo.
+     *
+     * If pathInfo is <code>null</code>, it is ignored. If servletPath
+     * is <code>null</code>, then <code>null</code> is returned.
+     *
+     * @return The combined path with pathInfo appended to servletInfo
+     */
+    private String getCombinedPath() {
+        if (servletPath == null) {
+            return null;
+        }
+        if (pathInfo == null) {
+            return servletPath;
+        }
+        return servletPath + pathInfo;
+    }
+    
+
+    /**
+     * Include the response from another resource in the current response.
+     * Any runtime exception, IOException, or ServletException thrown by the
+     * called servlet will be propagated to the caller.
+     *
+     * @param request The servlet request that is including this one
+     * @param response The servlet response to be appended to
+     *
+     * @throws IOException if an input/output error occurs
+     * @throws ServletException if a servlet exception occurs
+     */
+    public void include(ServletRequest request, ServletResponse response)
+        throws ServletException, IOException
+    {
+        if (Globals.IS_SECURITY_ENABLED) {
+            try {
+                PrivilegedInclude dp = new PrivilegedInclude(request,response);
+                AccessController.doPrivileged(dp);
+            } catch (PrivilegedActionException pe) {
+                Exception e = pe.getException();
+                if (e instanceof ServletException)
+                    throw (ServletException) e;
+                throw (IOException) e;
+            }
+        } else {
+            doInclude(request,response);
+        }
+    }
+
+
+    private void doInclude(ServletRequest request, ServletResponse response)
+        throws ServletException, IOException
+    {
+
+        // Set up to handle the specified request and response
+        State state = new State(request, response, DispatcherType.INCLUDE);
+
+        // Create a wrapped response to use for this request
+        wrapResponse(state);
+
+        // Handle a non-HTTP include
+        /* GlassFish 6386229
+        if (!(request instanceof HttpServletRequest) ||
+            !(response instanceof HttpServletResponse)) {
+
+            if ( log.isDebugEnabled() )
+                log.debug(" Non-HTTP Include");
+            request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+                                             Integer.valueOf(ApplicationFilterFactory.INCLUDE));
+            request.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, 
+                                             //origServletPath);
+                                             servletPath);
+            try{
+                invoke(request, state.outerResponse, state);
+            } finally {
+                unwrapResponse(state);
+            }
+        }
+
+        // Handle an HTTP named dispatcher include
+        else if (name != null) {
+        */
+        // START GlassFish 6386229
+        // Handle an HTTP named dispatcher include
+        if (name != null) {
+        // END GlassFish 6386229
+            ApplicationHttpRequest wrequest = (ApplicationHttpRequest)wrapRequest(state);
+            wrequest.setAttribute(Globals.NAMED_DISPATCHER_ATTR, name);
+            if (servletPath != null)
+                wrequest.setServletPath(servletPath);
+            wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
+                                  getCombinedPath());
+            try{
+                invoke(state.outerRequest, state.outerResponse, state);
+            } finally {
+                recycleRequestWrapper(state);
+                unwrapRequest(state);
+                unwrapResponse(state);
+            }
+
+        }
+
+        // Handle an HTTP path based include
+        else {
+            ApplicationHttpRequest wrequest = (ApplicationHttpRequest)wrapRequest(state);
+            wrequest.initSpecialAttributes(requestURI,
+                                           context.getPath(),
+                                           servletPath,
+                                           pathInfo,
+                                           queryString);
+            wrequest.setQueryParams(queryString);
+            wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
+                                  getCombinedPath());
+            try{
+                invoke(state.outerRequest, state.outerResponse, state);
+            } finally {
+                recycleRequestWrapper(state);
+                unwrapRequest(state);
+                unwrapResponse(state);
+           }
+        }
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+    
+    
+    /**
+     * Ask the resource represented by this RequestDispatcher to process
+     * the associated request, and create (or append to) the associated
+     * response.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>: This implementation assumes
+     * that no filters are applied to a forwarded or included resource,
+     * because they were already done for the original request.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @throws IOException if an input/output error occurs
+     * @throws ServletException if a servlet error occurs
+     */
+    private void invoke(ServletRequest request, ServletResponse response,
+                State state)
+            throws IOException, ServletException {
+        //START OF 6364900 original invoke has been renamed to doInvoke
+        boolean crossContext = false;
+        if (crossContextFlag != null && crossContextFlag.booleanValue()) {
+            crossContext = true;
+        }
+        if (crossContext) {
+            context.getManager().lockSession(request); 
+        }       
+        try {
+            if (crossContext) {
+                context.getManager().preRequestDispatcherProcess(request,
+                                                                 response);
+            }            
+            doInvoke(request, response, crossContext, state);
+            if (crossContext) {
+                context.getManager().postRequestDispatcherProcess(request,
+                                                                  response);
+            }
+        } finally {
+            if (crossContext) {
+                context.getManager().unlockSession(request);
+            }
+            crossContextFlag = null;
+        }
+        //END OF 6364900
+    }
+    
+    
+    /**
+     * Ask the resource represented by this RequestDispatcher to process
+     * the associated request, and create (or append to) the associated
+     * response.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>: This implementation assumes
+     * that no filters are applied to a forwarded or included resource,
+     * because they were already done for the original request.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param crossContext true if the request dispatch is crossing context
+     * boundaries, false otherwise
+     * @param state the state of this ApplicationDispatcher
+     *
+     * @throws IOException if an input/output error occurs
+     * @throws ServletException if a servlet error occurs
+     */
+    private void doInvoke(ServletRequest request, ServletResponse response,
+                          boolean crossContext, State state)
+            throws IOException, ServletException {
+
+        // Checking to see if the context classloader is the current context
+        // classloader. If it's not, we're saving it, and setting the context
+        // classloader to the Context classloader
+        ClassLoader oldCCL = null;
+        if (crossContext) {
+            oldCCL = Thread.currentThread().getContextClassLoader();
+            ClassLoader contextClassLoader = context.getLoader().getClassLoader();
+            Thread.currentThread().setContextClassLoader(contextClassLoader);
+        }
+
+        HttpServletResponse hresponse = state.hresponse;
+        Servlet servlet = null;
+        IOException ioException = null;
+        ServletException servletException = null;
+        RuntimeException runtimeException = null;
+        boolean unavailable = false;
+              
+
+        // Check for the servlet being marked unavailable
+        if (wrapper.isUnavailable()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.UNAVAILABLE_SERVLET),
+                                              wrapper.getName());
+            log.log(Level.WARNING, msg);
+            if (hresponse == null) {
+                ;       // NOTE - Not much we can do generically
+            } else {
+                long available = wrapper.getAvailable();
+                if ((available > 0L) && (available < Long.MAX_VALUE))
+                    hresponse.setDateHeader("Retry-After", available);
+                hresponse.sendError
+                        (HttpServletResponse.SC_SERVICE_UNAVAILABLE, msg);
+            }
+            unavailable = true;
+        }
+
+        // Allocate a servlet instance to process this request
+        String allocateServletMsg =
+                MessageFormat.format(rb.getString(LogFacade.ALLOCATE_SERVLET_EXCEPTION),
+                                     wrapper.getName());
+        try {
+            if (!unavailable) {
+                servlet = wrapper.allocate();
+            }
+        } catch (ServletException e) {
+            log.log(Level.SEVERE, allocateServletMsg,
+                    StandardWrapper.getRootCause(e));
+            servletException = e;
+            servlet = null;
+        } catch (Throwable e) {
+            log.log(Level.SEVERE, allocateServletMsg, e);
+            servletException = new ServletException(allocateServletMsg, e);
+            servlet = null;
+        }
+                
+        // Get the FilterChain Here
+        ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
+        ApplicationFilterChain filterChain = factory.createFilterChain(
+            request, wrapper, servlet);
+
+        InstanceSupport support = ((StandardWrapper) wrapper).getInstanceSupport();
+
+        // Call the service() method for the allocated servlet instance
+        String servletServiceExceptionMsg =
+                MessageFormat.format(rb.getString(LogFacade.SERVLET_SERVICE_EXCEPTION),
+                                     wrapper.getName());
+        RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(request);
+        try {
+            String jspFile = wrapper.getJspFile();
+            if (jspFile != null) {
+                request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
+            } 
+            support.fireInstanceEvent(
+                InstanceEvent.EventType.BEFORE_DISPATCH_EVENT,
+                servlet, request, response);
+            // for includes/forwards
+            /* IASRI 4665318
+            if ((servlet != null) && (filterChain != null)) {
+            */
+            // START IASRI 4665318
+            if (servlet != null) {
+            // END IASRI 4665318
+                // START OF S1AS 4703023
+                if (reqFacHelper != null) {
+                    reqFacHelper.incrementDispatchDepth();
+                    if (reqFacHelper.isMaxDispatchDepthReached()) {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.MAX_DISPATCH_DEPTH_REACHED),
+                                                          new Object[]{Integer.valueOf(Request.getMaxDispatchDepth())});
+                        throw new ServletException(msg);
+                    }
+                }
+                // END OF S1AS 4703023 
+                /* IASRI 4665318
+                filterChain.doFilter(request, response);
+                */
+                // START IASRI 4665318
+                if (filterChain != null) {
+                    filterChain.setWrapper((StandardWrapper)wrapper);
+                    filterChain.doFilter(request, response);
+                } else {
+                    ((StandardWrapper)wrapper).service(
+                        request, response, servlet);
+                }
+                // END IASRI 4665318
+            }
+            // Servlet Service Method is called by the FilterChain
+            support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+        } catch (ClientAbortException e) {
+            support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            ioException = e;
+        } catch (IOException e) {
+            support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            log.log(Level.WARNING, servletServiceExceptionMsg, e);
+            ioException = e;
+        } catch (UnavailableException e) {
+            support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            log.log(Level.WARNING, servletServiceExceptionMsg, e);
+            servletException = e;
+            wrapper.unavailable(e);
+        } catch (ServletException e) {
+            support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            Throwable rootCause = StandardWrapper.getRootCause(e);
+            if (!(rootCause instanceof ClientAbortException)) {
+                log.log(Level.WARNING, servletServiceExceptionMsg, rootCause);
+            }
+            servletException = e;
+        } catch (RuntimeException e) {
+            support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
+                                      servlet, request, response);
+            log.log(Level.WARNING, servletServiceExceptionMsg, e);
+            runtimeException = e;
+        // START OF S1AS 4703023
+        } finally {
+            if (reqFacHelper != null) {
+                reqFacHelper.decrementDispatchDepth();
+            }
+        // END OF S1AS 4703023
+        }
+
+        // Release the filter chain (if any) for this request
+        try {
+            if (filterChain != null)
+                filterChain.release();
+        } catch (Throwable e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.RELEASE_FILTERS_EXCEPTION_SEVERE),
+                                                           wrapper.getName());
+            log.log(Level.SEVERE, msg, e);
+            // FIXME Exception handling needs to be simpiler to what is
+            // in the StandardWrapperValue
+        }
+
+        // Deallocate the allocated servlet instance
+        String deallocateServletExceptionMsg =
+                MessageFormat.format(rb.getString(LogFacade.ALLOCATE_SERVLET_EXCEPTION),
+                                                  wrapper.getName());
+        try {
+            if (servlet != null) {
+                wrapper.deallocate(servlet);
+            }
+        } catch (ServletException e) {
+            log.log(Level.SEVERE, deallocateServletExceptionMsg, e);
+            servletException = e;
+        } catch (Throwable e) {
+            log.log(Level.SEVERE, deallocateServletExceptionMsg, e);
+            servletException = new ServletException(deallocateServletExceptionMsg, e);
+        }
+
+        // Reset the old context class loader
+        if (oldCCL != null)
+            Thread.currentThread().setContextClassLoader(oldCCL);
+
+        // Rethrow an exception if one was thrown by the invoked servlet
+        if (ioException != null)
+            throw ioException;
+        if (servletException != null)
+            throw servletException;
+        if (runtimeException != null)
+            throw runtimeException;
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Context (if any)
+     *
+     * @param message Message to be logged
+     *
+    private void log(String message) {
+        org.apache.catalina.Logger logger = context.getLogger();
+        if (logger != null) {
+            logger.log("ApplicationDispatcher[" + context.getPath() +
+                       "]: " + message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.APPLICATION_DISPATCHER_INFO),
+                                                  context.getPath(), message);
+                log.log(Level.INFO, msg);
+            }
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     *
+    private void log(String message, Throwable t) {
+        org.apache.catalina.Logger logger = context.getLogger();
+        if (logger != null) {
+            logger.log("ApplicationDispatcher[" + context.getPath() +
+                "] " + message, t, org.apache.catalina.Logger.WARNING);
+        } else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.APPLICATION_DISPATCHER_WARNING),
+                                              context.getPath(), message);
+            log.log(Level.WARNING, msg, t);
+        }
+    }  */
+
+
+    /**
+     * Unwrap the request if we have wrapped it.
+     */
+    private void unwrapRequest(State state) {
+
+        if (state.wrapRequest == null)
+            return;
+
+        ServletRequest previous = null;
+        ServletRequest current = state.outerRequest;
+
+        while (current != null) {
+
+            // If we run into the container request we are done
+            if ((current instanceof org.apache.catalina.Request)
+                || (current instanceof RequestFacade))
+                break;
+
+            // Remove the current request if it is our wrapper
+            if (current == state.wrapRequest) {
+                ServletRequest next =
+                  ((ServletRequestWrapper) current).getRequest();
+                if (previous == null)
+                    state.outerRequest = next;
+                else
+                    ((ServletRequestWrapper) previous).setRequest(next);
+                break;
+            }
+
+            // Advance to the next request in the chain
+            previous = current;
+            current = ((ServletRequestWrapper) current).getRequest();
+        }
+    }
+
+
+    /**
+     * Unwrap the response if we have wrapped it.
+     */
+    private void unwrapResponse(State state) {
+
+        if (state.wrapResponse == null)
+            return;
+
+        ServletResponse previous = null;
+        ServletResponse current = state.outerResponse;
+
+        while (current != null) {
+
+            // If we run into the container response we are done
+            if ((current instanceof org.apache.catalina.Response) ||
+                    (current instanceof ResponseFacade))
+                break;
+
+            // Remove the current response if it is our wrapper
+            if (current == state.wrapResponse) {
+                ServletResponse next =
+                  ((ServletResponseWrapper) current).getResponse();
+                if (previous == null)
+                    state.outerResponse = next;
+                else
+                    ((ServletResponseWrapper) previous).setResponse(next);
+                break;
+            }
+
+            // Advance to the next response in the chain
+            previous = current;
+            current = ((ServletResponseWrapper) current).getResponse();
+        }
+    }
+
+
+    /**
+     * Create and return a request wrapper that has been inserted in the
+     * appropriate spot in the request chain.
+     */
+    private ServletRequest wrapRequest(State state) {
+
+        // Locate the request we should insert in front of
+        ServletRequest previous = null;
+        ServletRequest current = state.outerRequest;
+
+        while (current != null) {
+            if (state.hrequest == null && (current instanceof HttpServletRequest)) {
+                state.hrequest = (HttpServletRequest)current;
+            }
+            if ("org.apache.catalina.servlets.InvokerHttpRequest".
+                    equals(current.getClass().getName())) {
+                break; // KLUDGE - Make nested RD.forward() using invoker work
+            }
+            if (!(current instanceof ServletRequestWrapper)) {
+                break;
+            }
+            // If we find container-generated wrapper, break out
+            if (current instanceof ApplicationHttpRequest) {
+                break;
+            }
+            if (current instanceof ApplicationRequest) {
+                break;
+            }
+            previous = current;
+            current = ((ServletRequestWrapper) current).getRequest();
+        }
+
+        // Instantiate a new wrapper at this point and insert it in the chain
+        ServletRequest wrapper = null;
+        if ((current instanceof ApplicationHttpRequest) ||
+            (current instanceof Request) ||
+            (current instanceof HttpServletRequest)) {
+            // Compute a crossContext flag
+            HttpServletRequest hcurrent = (HttpServletRequest) current;
+            boolean crossContext = false;
+            if ((state.outerRequest instanceof ApplicationHttpRequest) ||
+                (state.outerRequest instanceof Request) ||
+                (state.outerRequest instanceof HttpServletRequest)) {
+                HttpServletRequest houterRequest =
+                    (HttpServletRequest) state.outerRequest;
+                Object contextPath = houterRequest.getAttribute(
+                        RequestDispatcher.INCLUDE_CONTEXT_PATH);
+                if (contextPath == null) {
+                    // Forward
+                    contextPath = houterRequest.getContextPath();
+                }
+                crossContext = !(context.getPath().equals(contextPath));
+            }
+            //START OF 6364900
+            crossContextFlag = Boolean.valueOf(crossContext);
+            //END OF 6364900
+            
+            //START OF github/javaee/glassfish/issues/21846
+            if (this.name != null) {
+                this.mappingForDispatch = computeNamedDispatchHttpServletMapping(context, hcurrent);
+            }
+            //END OF github/javaee/glassfish/issues/21846
+
+            //START OF github/javaee/glassfish/issues/22079
+            if (DispatcherType.ASYNC.equals(state.dispatcherType)) {
+                this.mappingForDispatch = hcurrent.getHttpServletMapping();
+            }
+            //END OF github/javaee/glassfish/issues/22079
+
+            wrapper = new ApplicationHttpRequest
+                (hcurrent, context, crossContext, mappingForDispatch, state.dispatcherType);
+        } else {
+            wrapper = new ApplicationRequest(current);
+        }
+
+        if (previous == null) {
+            state.outerRequest = wrapper;
+        } else {
+            ((ServletRequestWrapper) previous).setRequest(wrapper);
+        }
+
+        state.wrapRequest = wrapper;
+
+        return wrapper;
+    }
+    
+    private HttpServletMapping computeNamedDispatchHttpServletMapping(Context context, HttpServletRequest hcurrent) {
+        HttpServletMapping result = null;
+        Mapper mapper = context.getMapper();
+        if (null == mapper) {
+            return null;
+        }
+  
+        MessageBytes uriMB = MessageBytes.newInstance();
+        CharChunk cc = uriMB.getCharChunk();
+        MappingData mappingData = new MappingData();
+        String requestURI = hcurrent.getRequestURI();
+        if (null == requestURI) {
+            return null;
+        }
+        try {
+            cc.append(requestURI, 0, requestURI.length());
+            mapper.map(uriMB, mappingData);
+        } catch (Exception ex) {
+            return null;
+        }
+        result = new MappingImpl(mappingData);
+
+        return result;
+    }
+
+    /**
+     * Create and return a response wrapper that has been inserted in the
+     * appropriate spot in the response chain.
+     */
+    private ServletResponse wrapResponse(State state) {
+
+        // Locate the response we should insert in front of
+        ServletResponse previous = null;
+        ServletResponse current = state.outerResponse;
+
+        while (current != null) {
+            if(state.hresponse == null && (current instanceof HttpServletResponse)) {
+                state.hresponse = (HttpServletResponse)current;
+                if (DispatcherType.INCLUDE != state.dispatcherType) // Forward only needs hresponse
+                    return null;
+            }
+
+            if (!(current instanceof ServletResponseWrapper))
+                break;
+            if (current instanceof ApplicationHttpResponse)
+                break;
+            if (current instanceof ApplicationResponse)
+                break;
+            previous = current;
+            current = ((ServletResponseWrapper) current).getResponse();
+        }
+
+        // Instantiate a new wrapper at this point and insert it in the chain
+        ServletResponse wrapper = null;
+        if ((current instanceof ApplicationHttpResponse) ||
+            (current instanceof HttpResponse) ||
+            (current instanceof HttpServletResponse))
+            wrapper =
+                new ApplicationHttpResponse((HttpServletResponse) current,
+                    DispatcherType.INCLUDE == state.dispatcherType);
+        else
+            wrapper = new ApplicationResponse(current,
+                DispatcherType.INCLUDE == state.dispatcherType);
+        if (previous == null)
+            state.outerResponse = wrapper;
+        else
+            ((ServletResponseWrapper) previous).setResponse(wrapper);
+        state.wrapResponse = wrapper;
+
+        return (wrapper);
+    }
+
+    private void recycleRequestWrapper(State state) {
+        if (state.wrapRequest instanceof ApplicationHttpRequest) {
+            ((ApplicationHttpRequest) state.wrapRequest).recycle();
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationDispatcherForward.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationDispatcherForward.java
new file mode 100644
index 0000000..a812f37
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationDispatcherForward.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.deploy.ErrorPage;
+import org.apache.catalina.util.ResponseUtil;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ErrorReportValve;
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Class responsible for processing the result of a RD.forward() invocation
+ * before committing the response.
+ *
+ * If sendError() was called during RD.forward(), an attempt is made to match
+ * the status code against the error pages of the RD's associated context, or
+ * those of the host on which the context has been deployed.
+ *
+ * The response contents are then committed, to comply with SRV.8.4
+ * ("The Forward Method"):
+ * 
+ *   Before the forward method of the <code>RequestDispatcher</code>
+ *   interface returns without exception, the response content must be
+ *   sent and committed, and closed by the servlet container.
+ *
+ *   If an error occurs in the target of the
+ *   <code>RequestDispatcher.forward()</code> the exception may be
+ *   propogated back through all the calling filters and servlets and
+ *   eventually back to the container.
+ */
+class ApplicationDispatcherForward {
+
+    private static final Logger log = LogFacade.getLogger();
+
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+    private static final StringManager sm =
+        StringManager.getManager(org.apache.catalina.valves.Constants.Package);
+
+
+    static void commit(ServletRequest request,
+                       ServletResponse response,
+                       Context context,
+                       Wrapper wrapper)
+            throws IOException, ServletException {
+
+        if (!(request instanceof HttpServletRequest) ||
+                !(response instanceof HttpServletResponse)) {
+            closeResponse(response);
+            return;
+        }
+
+        HttpServletRequest hreq = (HttpServletRequest) request;
+        HttpServletResponse hres = (HttpServletResponse) response;
+        int statusCode = hres.getStatus();
+        Object exception = hreq.getAttribute(
+            RequestDispatcher.ERROR_EXCEPTION);
+        String errorReportValveClass = 
+            ((StandardHost)(context.getParent())).getErrorReportValveClass();
+        boolean hasErrorReportValve = (errorReportValveClass != null &&
+                errorReportValveClass.length() > 0);
+        RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(request);
+        if (hasErrorReportValve && reqFacHelper != null && reqFacHelper.isResponseError() &&
+                statusCode >= 400 && exception == null) {            
+            boolean matchFound = status(hreq, hres, context, wrapper, statusCode);
+            if (!matchFound) {
+                boolean isDefaultErrorPageEnabled = true;
+                if (wrapper != null) {
+                    String initParam =
+                            wrapper.findInitParameter(Constants.IS_DEFAULT_ERROR_PAGE_ENABLED_INIT_PARAM);
+                    if (initParam != null) {
+                        isDefaultErrorPageEnabled = Boolean.parseBoolean(initParam);
+                    }
+                }
+                if (isDefaultErrorPageEnabled) {
+                    serveDefaultErrorPage(hreq, hres, statusCode);
+                }
+            }
+        }
+
+        /*
+         * Commit the response only if no exception
+         */
+        if (statusCode < 400
+                || (exception == null && hasErrorReportValve)) {
+            closeResponse(response);
+        }
+    }
+
+
+    /*
+     * Searches and processes a custom error page for the given status code.
+     *
+     * This method attempts to map the given status code to an error page,
+     * using the mappings of the given context or those of the host on which
+     * the given context has been deployed.
+     *
+     * If a match is found using the context mappings, the request is forwarded
+     * to the error page. Otherwise, if a match is found using the host 
+     * mappings, the contents of the error page are returned. If no match is
+     * found, no action is taken.
+     *
+     * @return true if a matching error page was found, false otherwise
+     */
+    private static boolean status(HttpServletRequest request,
+                                  HttpServletResponse response,
+                                  Context context,
+                                  Wrapper wrapper,
+                                  int statusCode) {
+
+        RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(request);
+        /*
+         * Attempt error-page mapping only if response.sendError(), as
+         * opposed to response.setStatus(), was called.
+         */
+        if (reqFacHelper == null || !reqFacHelper.isResponseError()) {
+            return false;
+        }
+
+        boolean matchFound = false;
+
+        ErrorPage errorPage = context.findErrorPage(statusCode);
+        if (errorPage != null) {
+
+            matchFound = true;
+
+            // Prevent infinite loop
+            String requestPath = (String) request.getAttribute(
+                Globals.DISPATCHER_REQUEST_PATH_ATTR);
+            if (requestPath == null
+                    || !requestPath.equals(errorPage.getLocation())) {
+                String message = reqFacHelper.getResponseMessage();
+                if (message == null) {
+                    message = "";
+                } else {
+                    message = HtmlEntityEncoder.encodeXSS(message);
+                }
+                prepareRequestForDispatch(request,
+                                          wrapper,
+                                          errorPage.getLocation(),
+                                          statusCode,
+                                          message);
+                custom(request, response, errorPage, context);
+            }
+        } else {
+            errorPage = ((StandardHost) context.getParent()).findErrorPage(
+                                            statusCode);
+            if (errorPage != null) {
+                matchFound = true;
+                try {
+                    serveErrorPage(response, errorPage, statusCode);
+                } catch (Exception e) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.EXCEPTION_PROCESSING), errorPage);
+                    log.log(Level.WARNING, msg, e);
+                }
+            }
+        }
+
+        return matchFound;
+    }
+
+
+    /**
+     * Handles an HTTP status code or exception by forwarding control
+     * to the location included in the specified errorPage object. 
+     */
+    private static void custom(HttpServletRequest request,
+                               HttpServletResponse response,
+                               ErrorPage errorPage,
+                               Context context) {
+        try {
+            // Forward control to the specified error page
+            if (response.isCommitted()) {
+                /*
+                 * If the target of the RD.forward() has called
+                 * response.sendError(), the response will have been committed,
+                 * and any attempt to RD.forward() the response to the error
+                 * page will cause an IllegalStateException.
+                 * Uncommit the response.
+                 */
+                RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(request);
+                if (reqFacHelper != null) {
+                    reqFacHelper.resetResponse();
+                }
+            }
+            ServletContext servletContext = context.getServletContext();
+            ApplicationDispatcher dispatcher = (ApplicationDispatcher)
+                servletContext.getRequestDispatcher(errorPage.getLocation());
+            dispatcher.dispatch(request, response, DispatcherType.ERROR);
+        } catch (IllegalStateException ise) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.EXCEPTION_PROCESSING), errorPage);
+            log.log(Level.WARNING, msg, ise);
+        } catch (Throwable t) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.EXCEPTION_PROCESSING), errorPage);
+            log.log(Level.WARNING, msg, t);
+        }
+    }
+
+
+    /**
+     * Adds request attributes in preparation for RD.forward().
+     */
+    private static void prepareRequestForDispatch(HttpServletRequest request,
+                                                  Wrapper errorServlet,
+                                                  String errorPageLocation,
+                                                  int errorCode,
+                                                  String errorMessage) {
+        request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
+                             request.getRequestURI());
+
+        if (errorServlet != null) {
+            // Save the logical name of the servlet in which the error occurred
+            request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
+                                 errorServlet.getName());
+        }
+
+        request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
+                             errorPageLocation);
+
+        request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
+                             Integer.valueOf(errorCode));
+
+        request.setAttribute(RequestDispatcher.ERROR_MESSAGE, errorMessage);
+    }
+
+    /**
+     * Copies the contents of the given error page to the response.
+     */
+    private static void serveErrorPage(HttpServletResponse response,
+                                       ErrorPage errorPage,
+                                       int statusCode)
+            throws Exception {
+
+        ServletOutputStream ostream = null;
+        PrintWriter writer = null;
+        FileReader reader = null;
+        BufferedInputStream istream = null;
+        IOException ioe = null;
+
+        String message = errorPage.getReason();
+        if (message != null && !response.isCommitted()) {
+            response.reset();
+            response.setStatus(statusCode, message);
+        }
+         
+        try {
+            ostream = response.getOutputStream();
+        } catch (IllegalStateException e) {
+            // If it fails, we try to get a Writer instead if we're
+            // trying to serve a text file
+            writer = response.getWriter();
+        }
+
+        if (writer != null) {
+            reader = new FileReader(errorPage.getLocation());
+            ioe = ResponseUtil.copy(reader, writer);
+            try {
+                reader.close();
+            } catch (Throwable t) {
+                ;
+            }
+        } else {
+            istream = new BufferedInputStream(
+                new FileInputStream(errorPage.getLocation()));
+            ioe = ResponseUtil.copy(istream, ostream);
+            try {
+                istream.close();
+            } catch (Throwable t) {
+                ;
+            }
+        }
+
+        // Rethrow any exception that may have occurred
+        if (ioe != null) {
+            throw ioe;
+        }
+    }
+
+
+    /**
+     * Renders the default error page.
+     */
+    private static void serveDefaultErrorPage(HttpServletRequest request,
+                                              HttpServletResponse response,
+                                              int statusCode)
+            throws IOException, ServletException {
+
+        RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(request);
+        // Do nothing on a 1xx, 2xx and 3xx status
+        if (response.isCommitted() || statusCode < 400
+                || (reqFacHelper != null && reqFacHelper.getResponseContentCount() > 0)
+                || Boolean.TRUE.equals(request.getAttribute("org.glassfish.jsp.error_handled"))) {
+            return;
+        }
+
+        String message = null;
+        if (reqFacHelper != null) {
+            message = reqFacHelper.getResponseMessage();
+        }
+        if (message == null) {
+            message = "";
+        } else {
+            message = HtmlEntityEncoder.encodeXSS(message);
+        }
+
+        // Do nothing if there is no report for the specified status code
+        String report = null;
+        try {
+            report = sm.getString("http." + statusCode, message);
+        } catch (Throwable t) {
+            ;
+        }
+        if (report == null) {
+            return;
+        }
+
+        String responseContents =
+            ErrorReportValve.makeErrorPage(statusCode, message, null, null,
+                                           report, response);
+        // START SJSAS 6412710
+        response.setLocale(sm.getResourceBundleLocale(response.getLocale()));
+        // END SJSAS 6412710
+
+        try {
+            response.setContentType("text/html");
+            response.getWriter().write(responseContents);
+        } catch (Throwable t) {
+            log.log(Level.WARNING, LogFacade.EXCEPTION_SENDING_DEFAULT_ERROR_PAGE, t);
+        }
+    }
+
+    
+/*
+    private static ResponseFacade getResponseFacade(ServletResponse response) {
+   
+        while (response instanceof ServletResponseWrapper) {
+            response = ((ServletResponseWrapper) response).getResponse();
+        }
+
+        return ((ResponseFacade) response);
+    }
+*/
+
+
+    private static void closeResponse(ServletResponse response) {
+        try {
+            PrintWriter writer = response.getWriter();
+            writer.flush();
+            writer.close();
+        } catch (IllegalStateException e) {
+            try {
+                ServletOutputStream stream = response.getOutputStream();
+                stream.flush();
+                stream.close();
+            } catch (IllegalStateException f) {
+                ;
+            } catch (IOException f) {
+                ;
+            }
+        } catch (IOException e) {
+            ;
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterChain.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterChain.java
new file mode 100644
index 0000000..984cd82
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterChain.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.InstanceSupport;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+
+
+import static org.apache.catalina.InstanceEvent.EventType.AFTER_FILTER_EVENT;
+import static org.apache.catalina.InstanceEvent.EventType.BEFORE_FILTER_EVENT;
+
+/**
+ * Implementation of <code>javax.servlet.FilterChain</code> used to manage
+ * the execution of a set of filters for a particular request.  When the
+ * set of defined filters has all been executed, the next call to
+ * <code>doFilter()</code> will execute the servlet's <code>service()</code>
+ * method itself.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.6 $ $Date: 2006/11/21 17:39:39 $
+ */
+
+final class ApplicationFilterChain implements FilterChain {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final int INCREMENT = 10;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new chain instance with no defined filters.
+     */
+    public ApplicationFilterChain() {
+        super();
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Filters.
+     */
+    private ApplicationFilterConfig[] filters = 
+        new ApplicationFilterConfig[0];
+
+
+    /**
+     * The int which is used to maintain the current position 
+     * in the filter chain.
+     */
+    private int pos = 0;
+
+
+    /**
+     * The int which gives the current number of filters in the chain.
+     */
+    private int n = 0;
+
+
+    /**
+     * The servlet instance to be executed by this chain.
+     */
+    private Servlet servlet = null;
+
+
+    /**
+     * The wrapper around the servlet instance to be executed by this chain.
+     */    
+    private StandardWrapper wrapper = null;
+
+
+    /**
+     * Static class array used when the SecurityManager is turned on and 
+     * <code>doFilter</code is invoked.
+     */
+    private static Class<?>[] classType = new Class[]{ServletRequest.class, 
+                                                   ServletResponse.class,
+                                                   FilterChain.class};
+                                                   
+    /**
+     * Static class array used when the SecurityManager is turned on and 
+     * <code>service</code is invoked.
+     */                                                 
+    /* IASRI 4665318
+    private static Class<?>[] classTypeUsedInService = new Class[]{
+                                                         ServletRequest.class,
+                                                         ServletResponse.class};
+    */
+
+    // ---------------------------------------------------- FilterChain Methods
+
+
+    /**
+     * Invoke the next filter in this chain, passing the specified request
+     * and response.  If there are no more filters in this chain, invoke
+     * the <code>service()</code> method of the servlet itself.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response)
+        throws IOException, ServletException {
+
+        if (Globals.IS_SECURITY_ENABLED) {
+            final ServletRequest req = request;
+            final ServletResponse res = response;
+            try {
+                java.security.AccessController.doPrivileged(
+                    new java.security.PrivilegedExceptionAction<Void>() {
+                        public Void run() 
+                            throws ServletException, IOException {
+                            internalDoFilter(req,res);
+                            return null;
+                        }
+                    }
+                );
+            } catch( PrivilegedActionException pe) {
+                Exception e = pe.getException();
+                if (e instanceof ServletException)
+                    throw (ServletException) e;
+                else if (e instanceof IOException)
+                    throw (IOException) e;
+                else if (e instanceof RuntimeException)
+                    throw (RuntimeException) e;
+                else
+                    throw new ServletException(e.getMessage(), e);
+            }
+        } else {
+            internalDoFilter(request,response);
+        }
+    }
+
+    private void internalDoFilter(ServletRequest request, 
+                                  ServletResponse response)
+            throws IOException, ServletException {
+
+        if (wrapper == null) {
+            throw new IllegalStateException("Missing wrapper");
+        }
+
+        InstanceSupport support = wrapper.getInstanceSupport();
+
+        // Call the next filter if there is one
+        if (pos < n) {
+            ApplicationFilterConfig filterConfig = filters[pos++];
+            if (!filterConfig.isAsyncSupported()) {
+                RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(request);
+                if (reqFacHelper != null) {
+                    reqFacHelper.disableAsyncSupport();
+                }
+            }
+            Filter filter = null;
+            try {
+                filter = filterConfig.getFilter();
+                support.fireInstanceEvent(BEFORE_FILTER_EVENT,
+                                          filter, request, response);
+                
+                if( SecurityUtil.isPackageProtectionEnabled() ) {
+                    final ServletRequest req = request;
+                    final ServletResponse res = response;
+                    Principal principal = 
+                        ((HttpServletRequest) req).getUserPrincipal();
+                    Object[] filterType = new Object[3];
+
+                    filterType[0] = req;
+                    filterType[1] = res;
+                    filterType[2] = this;
+                    SecurityUtil.doAsPrivilege
+                        ("doFilter", filter, classType, filterType, principal);
+                } else {  
+                    filter.doFilter(request, response, this);
+                }
+
+                support.fireInstanceEvent(AFTER_FILTER_EVENT,
+                                          filter, request, response);
+            } catch (IOException e) {
+                if (filter != null)
+                    support.fireInstanceEvent(AFTER_FILTER_EVENT,
+                                              filter, request, response, e);
+                throw e;
+            } catch (ServletException e) {
+                if (filter != null)
+                    support.fireInstanceEvent(AFTER_FILTER_EVENT,
+                                              filter, request, response, e);
+                throw e;
+            } catch (RuntimeException e) {
+                if (filter != null)
+                    support.fireInstanceEvent(AFTER_FILTER_EVENT,
+                                              filter, request, response, e);
+                throw e;
+            } catch (Throwable e) {
+                if (filter != null)
+                    support.fireInstanceEvent(AFTER_FILTER_EVENT,
+                                              filter, request, response, e);
+                throw new ServletException
+                        (rb.getString(LogFacade.FILTER_EXECUTION_EXCEPTION), e);
+            }
+            return;
+        }
+
+        // We fell off the end of the chain -- call the servlet instance
+        /* IASRI 4665318
+        try {
+            support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
+                                      servlet, request, response);
+            if ((request instanceof HttpServletRequest) &&
+                (response instanceof HttpServletResponse)) {
+                    
+                // START SJS WS 7.0 6236329
+                //if( System.getSecurityManager() != null) {
+                if ( SecurityUtil.executeUnderSubjectDoAs() ){
+                // END OF SJS WS 7.0 6236329
+                    final ServletRequest req = request;
+                    final ServletResponse res = response;
+                    Principal principal = 
+                        ((HttpServletRequest) req).getUserPrincipal();
+
+                    Object[] serviceType = new Object[2];
+                    serviceType[0] = req;
+                    serviceType[1] = res;
+                    
+                    SecurityUtil.doAsPrivilege("service",
+                                               servlet,
+                                               classTypeUsedInService, 
+                                               serviceType,
+                                               principal);                                                   
+                    serviceType = null;
+                } else {  
+                    servlet.service((HttpServletRequest) request,
+                                    (HttpServletResponse) response);
+                }
+            } else {
+                servlet.service(request, response);
+            }
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response);
+        } catch (IOException e) {
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response, e);
+            throw e;
+        } catch (ServletException e) {
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response, e);
+            throw e;
+        } catch (RuntimeException e) {
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response, e);
+            throw e;
+        } catch (Throwable e) {
+            support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
+                                      servlet, request, response, e);
+            throw new ServletException
+              (sm.getString("filterChain.servlet"), e);
+              //filterChain.servlet=PWC1244: Servlet execution threw an exception
+        }
+
+        */
+        // START IASRI 4665318
+        wrapper.service(request, response, servlet);
+        // END IASRI 4665318
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+
+    /**
+     * Add a filter to the set of filters that will be executed in this chain.
+     *
+     * @param filterConfig The FilterConfig for the servlet to be executed
+     */
+    void addFilter(ApplicationFilterConfig filterConfig) {
+        if (n == filters.length) {
+            ApplicationFilterConfig[] newFilters =
+                new ApplicationFilterConfig[n + INCREMENT];
+            System.arraycopy(filters, 0, newFilters, 0, n);
+            filters = newFilters;
+        }
+        filters[n++] = filterConfig;
+    }
+
+
+    /**
+     * Release references to the filters and wrapper executed by this chain.
+     */
+    void release() {
+        n = 0;
+        pos = 0;
+        servlet = null;
+        wrapper = null;
+    }
+
+
+    /**
+     * Sets the Servlet instance that will be executed at the end of this
+     * Filter chain.
+     *
+     * @param servlet the Servlet instance that will be executed at the end
+     * of this Filter chain.
+     */
+    void setServlet(Servlet servlet) {
+        this.servlet = servlet;
+    }
+
+
+    /**
+     * Sets the wrapper of the Servlet that will be executed at the end of
+     * this Filter chain.
+     *
+     * @param wrapper the wrapper of the Servlet that will be executed at
+     * the end of this Filter chain.
+     */
+    void setWrapper(StandardWrapper wrapper) {
+        this.wrapper = wrapper;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterConfig.java
new file mode 100644
index 0000000..b630956
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterConfig.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.deploy.FilterDef;
+import org.apache.catalina.security.SecurityUtil;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.ResourceBundle;
+
+
+
+/**
+ * Implementation of a <code>javax.servlet.FilterConfig</code> useful in
+ * managing the filter instances instantiated when a web application
+ * is first started.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.6 $ $Date: 2007/03/22 18:04:04 $
+ */
+
+final class ApplicationFilterConfig implements FilterConfig, Serializable {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new ApplicationFilterConfig for the specified filter
+     * definition.
+     *
+     * @param context The context with which we are associated
+     * @param filterDef Filter definition for which a FilterConfig is to be
+     *  constructed
+     *
+     * @exception ClassCastException if the specified class does not implement
+     *  the <code>javax.servlet.Filter</code> interface
+     * @exception ClassNotFoundException if the filter class cannot be found
+     * @exception IllegalAccessException if the filter class cannot be
+     *  publicly instantiated
+     * @exception InstantiationException if an exception occurs while
+     *  instantiating the filter object
+     * @exception ServletException if thrown by the filter's init() method
+     */
+    public ApplicationFilterConfig(StandardContext context,
+                                   FilterDef filterDef)
+        throws ClassCastException, ClassNotFoundException,
+               IllegalAccessException, InstantiationException,
+               ServletException {
+        super();
+        this.context = context;
+        setFilterDef(filterDef);
+        // init the filter
+        try {
+            getFilter();
+        } catch(InstantiationException iex) {
+            throw iex;
+        } catch(Exception ex) {
+            InstantiationException iex = new InstantiationException();
+            iex.initCause(ex);
+            throw iex;
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Context with which we are associated.
+     */
+    private transient StandardContext context = null;
+
+
+    /**
+     * The application Filter we are configured for.
+     */
+    private transient Filter filter = null;
+
+
+    /**
+     * The <code>FilterDef</code> that defines our associated Filter.
+     */
+    private FilterDef filterDef = null;
+
+
+    /**
+     * Does the filter instance need to be initialized?
+     */
+    private boolean needInitialize = true;
+
+
+    // --------------------------------------------------- FilterConfig Methods
+
+
+    /**
+     * Return the name of the filter we are configuring.
+     */
+    public String getFilterName() {
+        return (filterDef.getFilterName());
+    }
+
+
+    /**
+     * Checks if this filter has been annotated or flagged in the deployment
+     * descriptor as being able to support asynchronous operations.
+     *
+     * @return true if this filter supports async operations, and false
+     * otherwise
+     */
+    public boolean isAsyncSupported() {
+        return filterDef.isAsyncSupported();
+    }
+
+
+    /**
+     * Return a <code>String</code> containing the value of the named
+     * initialization parameter, or <code>null</code> if the parameter
+     * does not exist.
+     *
+     * @param name Name of the requested initialization parameter
+     */
+    public String getInitParameter(String name) {
+        return filterDef.getInitParameter(name);
+    }
+
+
+    /**
+     * Return an <code>Enumeration</code> of the names of the initialization
+     * parameters for this Filter.
+     */
+    public Enumeration<String> getInitParameterNames() {
+        return filterDef.getInitParameterNames();
+    }
+
+
+    /**
+     * Return the ServletContext of our associated web application.
+     */
+    public ServletContext getServletContext() {
+        return (this.context.getServletContext());
+    }
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+        StringBuilder sb = new StringBuilder("ApplicationFilterConfig[");
+        sb.append("name=");
+        sb.append(filterDef.getFilterName());
+        sb.append(", filterClass=");
+        sb.append(filterDef.getFilterClassName());
+        sb.append("]");
+        return (sb.toString());
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Return the application Filter we are configured for.
+     */
+    synchronized Filter getFilter() throws Exception {
+
+        // Return the existing filter instance, if any
+        if (filter != null && !needInitialize) {
+            return filter;
+        }
+
+        if (filter == null) {
+            Class<? extends Filter> clazz = filterDef.getFilterClass();
+            if (clazz == null) {
+                // Identify the class loader we will be using
+                ClassLoader classLoader = null;
+                String filterClassName = filterDef.getFilterClassName();
+                if (filterClassName.startsWith("org.apache.catalina.")) {
+                    classLoader = this.getClass().getClassLoader();
+                } else {
+                    classLoader = context.getLoader().getClassLoader();
+                }
+
+                // Instantiate a new instance of this filter and return it
+                clazz = loadFilterClass(classLoader, filterClassName);
+            }
+
+            this.filter = context.createFilterInstance(clazz);
+        }
+
+        // START PWC 1.2
+        if (context != null) {
+            context.fireContainerEvent(
+                ContainerEvent.BEFORE_FILTER_INITIALIZED,
+                filter);
+        }
+        // END PWC 1.2
+
+        filter.init(this);
+        needInitialize = false;
+
+        // START PWC 1.2
+        if (context != null) {
+            context.fireContainerEvent(ContainerEvent.AFTER_FILTER_INITIALIZED,
+                                       filter);
+        }
+        // END PWC 1.2
+
+        return (this.filter);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Class<? extends Filter> loadFilterClass(ClassLoader classLoader,
+            String filterClassName) throws ClassNotFoundException {
+        return (Class<? extends Filter>)classLoader.loadClass(filterClassName);
+    }
+
+
+    /**
+     * Return the filter definition we are configured for.
+     */
+    FilterDef getFilterDef() {
+        return (this.filterDef);
+    }
+
+
+    /**
+     * Release the Filter instance associated with this FilterConfig,
+     * if there is one.
+     */
+    void release() {
+
+        if (this.filter != null){
+
+            if (context != null) {
+                context.fireContainerEvent(
+                    ContainerEvent.BEFORE_FILTER_DESTROYED,
+                    filter);
+            }
+
+            // START SJS WS 7.0 6236329
+            //if( System.getSecurityManager() != null) {
+            if ( SecurityUtil.executeUnderSubjectDoAs() ){
+            // END OF SJS WS 7.0 6236329
+                try{
+                    SecurityUtil.doAsPrivilege("destroy",
+                                               filter); 
+                    SecurityUtil.remove(filter);
+                } catch(java.lang.Exception ex){
+                    String msg = rb.getString(LogFacade.DO_AS_PRIVILEGE);
+                    log.log(Level.SEVERE, msg, ex);
+                }
+            } else { 
+                filter.destroy();
+            }
+
+            if (context != null) {
+                context.fireContainerEvent(
+                    ContainerEvent.AFTER_FILTER_DESTROYED,
+                    filter);
+                // See GlassFish IT 7071
+                context = null;
+            }
+
+        }
+
+        this.filter = null;
+        needInitialize = true;
+     }
+
+
+    /**
+     * Set the filter definition we are configured for.  This has the side
+     * effect of instantiating an instance of the corresponding filter class.
+     *
+     * @param filterDef The new filter definition
+     *
+     * @exception ClassCastException if the specified class does not implement
+     *  the <code>javax.servlet.Filter</code> interface
+     * @exception ClassNotFoundException if the filter class cannot be found
+     * @exception IllegalAccessException if the filter class cannot be
+     *  publicly instantiated
+     * @exception InstantiationException if an exception occurs while
+     *  instantiating the filter object
+     * @exception ServletException if thrown by the filter's init() method
+     */
+    void setFilterDef(FilterDef filterDef)
+        throws ClassCastException, ClassNotFoundException,
+               IllegalAccessException, InstantiationException,
+               ServletException {
+
+        this.filterDef = filterDef;
+        if (filterDef == null) {
+
+            // Release any previously allocated filter instance
+            if (this.filter != null){
+                // START SJS WS 7.0 6236329
+                //if( System.getSecurityManager() != null) {
+                if ( SecurityUtil.executeUnderSubjectDoAs() ){
+                // END OF SJS WS 7.0 6236329
+                    try{
+                        SecurityUtil.doAsPrivilege("destroy",
+                                                   filter);  
+                        SecurityUtil.remove(filter);
+                    } catch(java.lang.Exception ex){
+                        String msg = rb.getString(LogFacade.DO_AS_PRIVILEGE);
+                        log.log(Level.SEVERE, msg, ex);
+                    }
+                } else { 
+                    filter.destroy();
+                }
+            }
+            this.filter = null;
+
+        } else {
+            filter = filterDef.getFilter();
+        }
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterFactory.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterFactory.java
new file mode 100644
index 0000000..51d5761
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterFactory.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.Request;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.deploy.FilterMap;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Factory for the creation and caching of Filters and creation
+ * of Filter Chains.
+ *
+ * @author Greg Murray
+ * @author Remy Maucherat
+ * @version $Revision: 1.0
+ */
+
+public final class ApplicationFilterFactory {
+
+
+    // -------------------------------------------------------------- Constants
+
+    private static ApplicationFilterFactory factory = new ApplicationFilterFactory();
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /*
+     * Prevent instantiation outside of the getInstanceMethod().
+     */
+    private ApplicationFilterFactory() {
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the factory instance.
+     */
+    public static ApplicationFilterFactory getInstance() {
+        return factory;
+    }
+
+
+    /**
+     * Construct and return a FilterChain implementation that will wrap the
+     * execution of the specified servlet instance.  If we should not execute
+     * a filter chain at all, return <code>null</code>.
+     *
+     * @param request The servlet request we are processing
+     * @param servlet The servlet instance to be wrapped
+     */
+    public ApplicationFilterChain createFilterChain
+        (ServletRequest request, Wrapper wrapper, Servlet servlet) {
+        
+        // If there is no servlet to execute, return null
+        if (servlet == null)
+            return (null);
+
+        // Create and initialize a filter chain object
+        ApplicationFilterChain filterChain = null;
+        /** IASRI 4665318
+        if ((securityManager == null) && (request instanceof Request)) {
+            Request req = (Request) request;
+            filterChain = (ApplicationFilterChain) req.getFilterChain();
+            if (filterChain == null) {
+                filterChain = new ApplicationFilterChain();
+                req.setFilterChain(filterChain);
+            }
+        } else {
+            // Security: Do not recycle
+            filterChain = new ApplicationFilterChain();
+        }
+
+        filterChain.setServlet(servlet);
+
+        filterChain.setSupport
+            (((StandardWrapper)wrapper).getInstanceSupport());
+        */
+
+        // Acquire the filter mappings for this Context
+        StandardContext context = (StandardContext) wrapper.getParent();
+        List<FilterMap> filterMaps = context.findFilterMaps();
+
+        // If there are no filter mappings, we are done
+        if (filterMaps.isEmpty()) {
+            return (filterChain);
+        }
+
+        // get the dispatcher type
+        DispatcherType dispatcher = request.getDispatcherType();
+        String requestPath = null;
+        Object attribute = request.getAttribute(
+            Globals.DISPATCHER_REQUEST_PATH_ATTR);
+        if (attribute != null){
+            requestPath = attribute.toString();
+        }
+
+        // Acquire the information we will need to match filter mappings
+        String servletName = wrapper.getName();
+
+        int n = 0;
+
+        // Add the relevant path-mapped filters to this filter chain
+        Iterator<FilterMap> i = filterMaps.iterator(); 
+        while (i.hasNext()) {
+            FilterMap filterMap = i.next();
+            if (!filterMap.getDispatcherTypes().contains(dispatcher)) {
+                continue;
+            }
+            /* SJSWS 6324431
+            if (!matchFiltersURL(filterMaps[i], requestPath))
+                continue;
+            */
+            // START SJSWS 6324431
+            if (!matchFiltersURL(filterMap, requestPath, 
+                                 context.isCaseSensitiveMapping()))
+                continue;
+            // END SJSWS 6324431
+            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
+                context.findFilterConfig(filterMap.getFilterName());
+            if (filterConfig == null) {
+                // FIXME - log configuration problem
+                continue;
+            }
+            // START IASRI 4665318
+            // Create a filter chain only when there are filters to add
+            if (filterChain == null)
+                filterChain = internalCreateFilterChain(request, wrapper,
+                                                        servlet);
+            // END IASRI 4665318
+            filterChain.addFilter(filterConfig);
+            n++;
+        }
+
+        // Add filters that match on servlet name second
+        i = filterMaps.iterator(); 
+        while (i.hasNext()) {
+            FilterMap filterMap = i.next();
+            if (!filterMap.getDispatcherTypes().contains(dispatcher)) {
+                continue;
+            }
+            if (!matchFiltersServlet(filterMap, servletName))
+                continue;
+            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
+                context.findFilterConfig(filterMap.getFilterName());
+            if (filterConfig == null) {
+                // FIXME - log configuration problem
+                continue;
+            }
+            // START IASRI 4665318
+            // Create a filter chain only when there are filters to add
+            if (filterChain == null)
+                filterChain = internalCreateFilterChain(request, wrapper,
+                                                        servlet);
+            // END IASRI 4665318
+            filterChain.addFilter(filterConfig);
+            n++;
+        }
+
+        // Return the completed filter chain
+        return (filterChain);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Return <code>true</code> if the context-relative request path
+     * matches the requirements of the specified filter mapping;
+     * otherwise, return <code>null</code>.
+     *
+     * @param filterMap Filter mapping being checked
+     * @param requestPath Context-relative request path of this request
+     */
+    /* SJSWS 6324431
+    private boolean matchFiltersURL(FilterMap filterMap, String requestPath) {
+    */
+    // START SJSWS 6324431
+    private boolean matchFiltersURL(FilterMap filterMap, String requestPath,
+                                    boolean caseSensitiveMapping) {
+    // END SJSWS 6324431
+
+        if (requestPath == null)
+            return (false);
+
+        // Match on context relative request path
+        String testPath = filterMap.getURLPattern();
+        if (testPath == null)
+            return (false);
+
+        // START SJSWS 6324431
+        if (!caseSensitiveMapping) {
+            requestPath = requestPath.toLowerCase(Locale.ENGLISH);
+            testPath = testPath.toLowerCase(Locale.ENGLISH);
+        }
+        // END SJSWS 6324431
+
+        // Case 1 - Exact Match
+        if (testPath.equals(requestPath))
+            return (true);
+
+        // Case 2 - Path Match ("/.../*")
+        if (testPath.equals("/*"))
+            return (true);
+        if (testPath.endsWith("/*")) {
+            if (testPath.regionMatches(0, requestPath, 0, 
+                                       testPath.length() - 2)) {
+                if (requestPath.length() == (testPath.length() - 2)) {
+                    return (true);
+                } else if ('/' == requestPath.charAt(testPath.length() - 2)) {
+                    return (true);
+                }
+            }
+            return (false);
+        }
+
+        // Case 3 - Extension Match
+        if (testPath.startsWith("*.")) {
+            int slash = requestPath.lastIndexOf('/');
+            int period = requestPath.lastIndexOf('.');
+            if ((slash >= 0) && (period > slash) 
+                && (period != requestPath.length() - 1)
+                && ((requestPath.length() - period) 
+                    == (testPath.length() - 1))) {
+                return (testPath.regionMatches(2, requestPath, period + 1,
+                                               testPath.length() - 2));
+            }
+        }
+
+        // Case 4 - "Default" Match
+        return (false); // NOTE - Not relevant for selecting filters
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified servlet name matches
+     * the requirements of the specified filter mapping; otherwise
+     * return <code>false</code>.
+     *
+     * @param filterMap Filter mapping being checked
+     * @param servletName Servlet name being checked
+     */
+    private boolean matchFiltersServlet(FilterMap filterMap, 
+                                        String servletName) {
+
+        if (servletName == null) {
+            return (false);
+        } else {
+            if (servletName.equals(filterMap.getServletName())
+                    || "*".equals(filterMap.getServletName())) {
+                return (true);
+            } else {
+                return false;
+            }
+        }
+
+    }
+
+
+    // START IASRI 4665318
+    private ApplicationFilterChain internalCreateFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
+        ApplicationFilterChain filterChain = null;
+        if (!Globals.IS_SECURITY_ENABLED && (request instanceof Request)) {
+            Request req = (Request) request;
+            filterChain = (ApplicationFilterChain) req.getFilterChain();
+            if (filterChain == null) {
+                filterChain = new ApplicationFilterChain();
+                req.setFilterChain(filterChain);
+            }
+        } else {
+            // Security: Do not recycle
+            filterChain = new ApplicationFilterChain();
+        }
+
+        filterChain.setServlet(servlet);
+        filterChain.setWrapper((StandardWrapper)wrapper);
+
+        return filterChain;
+    }
+    // END IASRI 4665318
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationHttpRequest.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationHttpRequest.java
new file mode 100644
index 0000000..d97f6b5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationHttpRequest.java
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.connector.RequestFacade;
+import org.apache.catalina.session.StandardSession;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.RequestUtil;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.PushBuilder;
+import java.io.IOException;
+import java.util.*;
+import javax.servlet.http.HttpServletMapping;
+import org.apache.catalina.connector.MappingImpl;
+// END GlassFish 896
+
+/**
+ * Wrapper around a <code>javax.servlet.http.HttpServletRequest</code>
+ * that transforms an application request object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.http.HttpServletRequestWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.HttpRequest</code>.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.15 $ $Date: 2007/05/03 21:58:54 $
+ */
+public class ApplicationHttpRequest extends HttpServletRequestWrapper {
+
+    // ------------------------------------------------------- Static Variables
+
+    /**
+     * The set of attribute names that are special for request dispatchers
+     */
+    private static final HashSet<String> specials = new HashSet<String>(15);
+
+    static {
+        specials.add(RequestDispatcher.INCLUDE_REQUEST_URI);
+        specials.add(RequestDispatcher.INCLUDE_CONTEXT_PATH);
+        specials.add(RequestDispatcher.INCLUDE_SERVLET_PATH);
+        specials.add(RequestDispatcher.INCLUDE_MAPPING);
+        specials.add(RequestDispatcher.INCLUDE_PATH_INFO);
+        specials.add(RequestDispatcher.INCLUDE_QUERY_STRING);
+        specials.add(RequestDispatcher.FORWARD_REQUEST_URI);
+        specials.add(RequestDispatcher.FORWARD_CONTEXT_PATH);
+        specials.add(RequestDispatcher.FORWARD_SERVLET_PATH);
+        specials.add(RequestDispatcher.FORWARD_MAPPING);
+        specials.add(RequestDispatcher.FORWARD_PATH_INFO);
+        specials.add(RequestDispatcher.FORWARD_QUERY_STRING);
+        specials.add(AsyncContext.ASYNC_REQUEST_URI);
+        specials.add(AsyncContext.ASYNC_CONTEXT_PATH);
+        specials.add(AsyncContext.ASYNC_SERVLET_PATH);
+        specials.add(AsyncContext.ASYNC_MAPPING);
+        specials.add(AsyncContext.ASYNC_PATH_INFO);
+        specials.add(AsyncContext.ASYNC_QUERY_STRING);
+    }
+
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Construct a new wrapped request around the specified servlet request.
+     *
+     * @param request the servlet request being wrapped
+     * @param context the target context of the request dispatch
+     * @param crossContext true if this is a cross-context dispatch, false
+     * otherwise
+     * @param dispatcherType the dispatcher type
+     */
+    public ApplicationHttpRequest(HttpServletRequest request,
+                                  Context context,
+                                  boolean crossContext,
+                                  HttpServletMapping mappingForDispatch,
+                                  DispatcherType dispatcherType) {
+        super(request);
+        this.context = context;
+        this.crossContext = crossContext;
+        this.mappingForDispatch = mappingForDispatch;
+        this.dispatcherType = dispatcherType;
+
+        setRequest(request);
+
+        if (context.getManager() != null) {
+            isSessionVersioningSupported =
+                context.getManager().isSessionVersioningSupported();
+            if (isSessionVersioningSupported) {
+                Map<String, String> sessionVersions =
+                    getSessionVersions();
+                if (sessionVersions != null) {
+                    requestedSessionVersion = sessionVersions.get(
+                        context.getPath());
+                }
+            }
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The context for this request.
+     */
+    protected Context context = null;
+
+    /**
+     * The context path for this request.
+     */
+    protected String contextPath = null;
+
+    /**
+     * If this request is cross context, since this changes session access
+     * behavior.
+     */
+    protected boolean crossContext = false;
+    
+    private HttpServletMapping mappingForDispatch;
+
+    /**
+     * The dispatcher type.
+     */
+    protected DispatcherType dispatcherType;
+
+    /**
+     * The request parameters for this request.  This is initialized from the
+     * wrapped request, but updates are allowed.
+     */
+    protected Map<String, String[]> parameters = null;
+
+    /**
+     * Have the parameters for this request already been parsed?
+     */
+    private boolean parsedParams = false;
+
+    /**
+     * The path information for this request.
+     */
+    protected String pathInfo = null;
+
+    /**
+     * The query parameters for the current request.
+     */
+    private String queryParamString = null;
+
+    /**
+     * The query string for this request.
+     */
+    protected String queryString = null;
+
+    /**
+     * The current request dispatcher path.
+     */
+    protected Object requestDispatcherPath = null;
+
+    /**
+     * The request URI for this request.
+     */
+    protected String requestURI = null;
+
+    /**
+     * The servlet path for this request.
+     */
+    protected String servletPath = null;
+
+    /**
+     * The currently active session for this request.
+     */
+    protected Session session = null;
+
+    /**
+     * Special attributes.
+     */
+    private HashMap<String, Object> specialAttributes = null;
+
+    private String requestedSessionVersion = null;
+
+    private boolean isSessionVersioningSupported = false;
+
+
+    // ------------------------------------------------- ServletRequest Methods
+
+    /**
+     * Override the <code>getAttribute()</code> method of the wrapped request.
+     *
+     * @param name Name of the attribute to retrieve
+     */
+    @Override
+    public Object getAttribute(String name) {
+
+        if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
+            if ( requestDispatcherPath != null ){
+                return requestDispatcherPath.toString();
+            } else {
+                return null;   
+            }
+        }
+
+        if (!isSpecial(name)) {
+            return getRequest().getAttribute(name);
+        } else {
+            Object value = null;
+            if (specialAttributes != null) {
+                value = specialAttributes.get(name);
+            }
+            if (value == null && name.startsWith("javax.servlet.forward")) {
+                /*
+                 * If it's a forward special attribute, and null, delegate
+                 * to the wrapped request. This will allow access to the
+                 * forward special attributes from a request that was first
+                 * forwarded and then included, or forwarded multiple times
+                 * in a row.
+                 * Notice that forward special attributes are set only on
+                 * the wrapper that was created for the initial forward
+                 * (i.e., the top-most wrapper for a request that was
+                 * forwarded multiple times in a row, and never included,
+                 * will not contain any specialAttributes!).
+                 * This is different from an include, where the special
+                 * include attributes are set on every include wrapper.
+                 */
+                value = getRequest().getAttribute(name);
+            }
+            return value;
+        }
+
+    }
+
+
+    /**
+     * Override the <code>getAttributeNames()</code> method of the wrapped
+     * request.
+     */
+    @Override
+    public Enumeration<String> getAttributeNames() {
+        return (new AttributeNamesEnumerator());
+    }
+
+    /**
+     * Override the <code>removeAttribute()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the attribute to remove
+     */
+    @Override
+    public void removeAttribute(String name) {
+
+        if (isSpecial(name)) {
+            if (specialAttributes != null) {
+                specialAttributes.remove(name);
+            }
+        } else {
+            getRequest().removeAttribute(name);
+        }
+    }
+
+
+    /**
+     * Override the <code>setAttribute()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the attribute to set
+     * @param value Value of the attribute to set
+     */
+    @Override
+    public void setAttribute(String name, Object value) {
+
+        if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
+            requestDispatcherPath = value;
+            return;
+        }
+
+        if (isSpecial(name)) {
+            if (specialAttributes != null) {
+                specialAttributes.put(name, value);
+            }
+        } else {
+            getRequest().setAttribute(name, value);
+        }
+    }
+
+
+    /**
+     * 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
+     */
+    @Override
+    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(RequestDispatcher.INCLUDE_SERVLET_PATH);
+        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 = requestPath.substring(0, pos + 1) + path;
+        } else {
+            relative = requestPath + path;
+        }
+
+        return (context.getServletContext().getRequestDispatcher(relative));
+
+    }
+
+
+    @Override
+    public DispatcherType getDispatcherType() {
+        return dispatcherType;
+    }
+
+
+    // --------------------------------------------- HttpServletRequest Methods
+
+
+    /**
+     * Override the <code>getContextPath()</code> method of the wrapped
+     * request.
+     */
+    @Override
+    public String getContextPath() {
+
+        return (this.contextPath);
+
+    }
+
+
+    /**
+     * Override the <code>getParameter()</code> method of the wrapped request.
+     *
+     * @param name Name of the requested parameter
+     */
+    @Override
+    public String getParameter(String name) {
+
+        parseParameters();
+        synchronized (parameters) {
+            String[] value = parameters.get(name);
+            return ((value != null) ? value[0] : null);
+        }
+
+    }
+
+
+    /**
+     * Override the <code>getParameterMap()</code> method of the
+     * wrapped request.
+     */
+    @Override
+    public Map<String, String[]> getParameterMap() {
+
+        parseParameters();
+        return (parameters);
+
+    }
+
+
+    /**
+     * Override the <code>getParameterNames()</code> method of the
+     * wrapped request.
+     */
+    @Override
+    public Enumeration<String> getParameterNames() {
+
+        parseParameters();
+        synchronized (parameters) {
+            return (new Enumerator<String>(parameters.keySet()));
+        }
+
+    }
+
+
+    /**
+     * Override the <code>getParameterValues()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the requested parameter
+     */
+    @Override
+    public String[] getParameterValues(String name) {
+
+        parseParameters();
+        synchronized (parameters) {
+            String[] value = parameters.get(name);
+            return value;
+        }
+
+    }
+
+
+    /**
+     * Override the <code>getPathInfo()</code> method of the wrapped request.
+     */
+    @Override
+    public String getPathInfo() {
+
+        return (this.pathInfo);
+
+    }
+
+
+    /**
+     * Override the <code>getQueryString()</code> method of the wrapped
+     * request.
+     */
+    @Override
+    public String getQueryString() {
+
+        return (this.queryString);
+
+    }
+
+
+    /**
+     * Override the <code>getRequestURI()</code> method of the wrapped
+     * request.
+     */
+    @Override
+    public String getRequestURI() {
+
+        return (this.requestURI);
+
+    }
+
+
+    /**
+     * Override the <code>getRequestURL()</code> method of the wrapped
+     * request.
+     */
+    @Override
+    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);
+    }
+
+    @Override
+    public HttpServletMapping getHttpServletMapping() {
+        HttpServletMapping result = null;
+        switch (dispatcherType) {
+        case INCLUDE:
+            // Safe to cast because we received this in the ctor 
+            // as an HttpServletRequest.
+            result = ((HttpServletRequest)getRequest()).getHttpServletMapping();
+            break;
+        case ASYNC:
+        case FORWARD:
+        case ERROR:
+            result = mappingForDispatch;
+            break;
+        default: // REQUEST
+            break;
+        }
+        
+        return result;
+    }
+
+    /**
+     * Override the <code>getServletPath()</code> method of the wrapped
+     * request.
+     */
+    @Override
+    public String getServletPath() {
+
+        return (this.servletPath);
+
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary.
+     */
+    @Override
+    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
+     */
+    @Override
+    public HttpSession getSession(boolean create) {
+
+        if (crossContext) {
+            
+            // 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()) {
+                return (session.getSession());
+            }
+
+            HttpSession other = super.getSession(false);
+            if (create && (other == null)) {
+                // First create a session in the first context: the problem is
+                // that the top level request is the only one which can 
+                // create the cookie safely
+                other = super.getSession(true);
+            }
+            if (other != null) {
+                Session localSession = null;
+                try {
+                    if (isSessionVersioningSupported) {
+                        localSession =
+                            context.getManager().findSession(
+                                other.getId(),
+                                requestedSessionVersion);
+                        //XXX need to revisit
+                        if (localSession instanceof StandardSession) {
+                            incrementSessionVersion((StandardSession) localSession,
+                                                    context);
+                        }
+                    } else {
+                        localSession =
+                            context.getManager().findSession(other.getId());
+                    }
+                } catch (IOException e) {
+                    // Ignore
+                }
+
+                if ((localSession != null) && !localSession.isValid()) {
+                    localSession = null;
+                } else if (localSession == null && create) {
+                    //START OF 6364900
+                    localSession = 
+                        context.getManager().createSession(other.getId());
+                    //XXX need to revisit
+                    if (isSessionVersioningSupported &&
+                            localSession instanceof StandardSession) {
+                        incrementSessionVersion((StandardSession) localSession,
+                                                context);
+                    }
+                    //END OF 6364900
+                    /* CR 6364900
+                    localSession = context.getManager().createEmptySession();
+                    localSession.setNew(true);
+                    localSession.setValid(true);
+                    localSession.setCreationTime(System.currentTimeMillis());
+                    localSession.setMaxInactiveInterval
+                        (context.getManager().getMaxInactiveIntervalSeconds());
+                    localSession.setId(other.getId());
+                    */
+                    // START GlassFish 896
+                    RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(getRequest());
+                    if (reqFacHelper != null) {
+                        reqFacHelper.track(localSession);
+                    }
+                    // END GlassFish 896
+                }
+                if (localSession != null) {
+                    localSession.access();
+                    session = localSession;
+                    return session.getSession();
+                }
+            }
+            return null;
+
+        } else {
+            return super.getSession(create);
+        }
+
+    }
+
+
+    /**
+     * Returns true if the request specifies a JSESSIONID that is valid within
+     * the context of this ApplicationHttpRequest, false otherwise.
+     *
+     * @return true if the request specifies a JSESSIONID that is valid within
+     * the context of this ApplicationHttpRequest, false otherwise.
+     */
+    @Override
+    public boolean isRequestedSessionIdValid() {
+
+        if (crossContext) {
+
+            String requestedSessionId = getRequestedSessionId();
+            if (requestedSessionId == null)
+                return (false);
+            if (context == null)
+                return (false);
+
+            if (session != null
+                    && requestedSessionId.equals(session.getIdInternal())) {
+                return session.isValid();
+            }
+
+            Manager manager = context.getManager();
+            if (manager == null)
+                return (false);
+            Session localSession = null;
+            try {
+                if (isSessionVersioningSupported) {
+                    localSession = manager.findSession(requestedSessionId,
+                                                       requestedSessionVersion);
+                } else {
+                    localSession = manager.findSession(requestedSessionId);
+                }
+            } catch (IOException e) {
+                localSession = null;
+            }
+            if ((localSession != null) && localSession.isValid()) {
+                return (true);
+            } else {
+                return (false);
+            }
+
+        } else {
+            return super.isRequestedSessionIdValid();
+        }
+    }
+
+    @Override
+    public PushBuilder newPushBuilder() {
+        return ((HttpServletRequest)getRequest()).newPushBuilder();
+    }
+
+    // -------------------------------------------------------- Package Methods
+
+    /**
+     * Recycle this request
+     */
+    public void recycle() {
+        if (session != null) {
+            session.endAccess();
+        }
+    }
+
+    /**
+     * Perform a shallow copy of the specified Map, and return the result.
+     *
+     * @param orig Origin Map to be copied
+     */
+    void copyMap(Map<String, String[]> orig, Map<String, String[]> dest) {
+
+        if (orig == null)
+            return;
+        synchronized (orig) {
+            for (Map.Entry<String, String[]> entry : orig.entrySet()) {
+                dest.put(entry.getKey(), entry.getValue());
+            }
+        }
+    }
+
+    /**
+     * Set the context path for this request.
+     *
+     * @param contextPath The new context path
+     */
+    void setContextPath(String contextPath) {
+        this.contextPath = contextPath;
+    }
+
+    /**
+     * Set the path information for this request.
+     *
+     * @param pathInfo The new path info
+     */
+    void setPathInfo(String pathInfo) {
+        this.pathInfo = pathInfo;
+    }
+
+    /**
+     * Set the query string for this request.
+     *
+     * @param queryString The new query string
+     */
+    void setQueryString(String queryString) {
+        this.queryString = queryString;
+    }
+
+    /**
+     * Set the request that we are wrapping.
+     *
+     * @param request The new wrapped request
+     */
+    void setRequest(HttpServletRequest request) {
+
+        super.setRequest(request);
+
+        // Initialize the attributes for this request
+        requestDispatcherPath = 
+            request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
+
+        // Initialize the path elements for this request
+        contextPath = request.getContextPath();
+        pathInfo = request.getPathInfo();
+        queryString = request.getQueryString();
+        requestURI = request.getRequestURI();
+        servletPath = request.getServletPath();
+
+    }
+
+    /**
+     * Set the request URI for this request.
+     *
+     * @param requestURI The new request URI
+     */
+    void setRequestURI(String requestURI) {
+        this.requestURI = requestURI;
+    }
+
+    /**
+     * Set the servlet path for this request.
+     *
+     * @param servletPath The new servlet path
+     */
+    void setServletPath(String servletPath) {
+        this.servletPath = servletPath;
+    }
+
+    /**
+     * Parses the parameters of this request.
+     *
+     * If parameters are present in both the query string and the request
+     * content, they are merged.
+     */
+    void parseParameters() {
+        if (parsedParams) {
+            return;
+        }
+
+        parameters = new HashMap<String, String[]>();
+        synchronized (parameters) {
+            copyMap(getRequest().getParameterMap(), parameters);
+            mergeParameters();
+            parsedParams = true;
+        }
+    }
+
+    /**
+     * Save query parameters for this request.
+     *
+     * @param queryString The query string containing parameters for this
+     *                    request
+     */
+    void setQueryParams(String queryString) {
+        this.queryParamString = queryString;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * Is this attribute name one of the special ones that is added only for
+     * included servlets?
+     *
+     * @param name Attribute name to be tested
+     */
+    protected boolean isSpecial(String name) {
+        return specials.contains(name);
+    }
+
+    /**
+     * Initializes the special attributes of this request wrapper.
+     *
+     * @param requestUri The request URI
+     * @param contextPath The context path
+     * @param servletPath The servlet path
+     * @param pathInfo The path info
+     * @param queryString The query string
+     */
+    void initSpecialAttributes(String requestUri,
+                               String contextPath,
+                               String servletPath,
+                               String pathInfo,
+                               String queryString) {
+        specialAttributes = new HashMap<String, Object>(5);
+        HttpServletMapping originalMapping;        
+
+        switch (dispatcherType) {
+        case INCLUDE:
+            specialAttributes.put(RequestDispatcher.INCLUDE_REQUEST_URI,
+                                  requestUri);
+            specialAttributes.put(RequestDispatcher.INCLUDE_CONTEXT_PATH,
+                                  contextPath);
+            specialAttributes.put(RequestDispatcher.INCLUDE_SERVLET_PATH,
+                                  servletPath);
+            specialAttributes.put(RequestDispatcher.INCLUDE_MAPPING,
+                                  mappingForDispatch);
+            specialAttributes.put(RequestDispatcher.INCLUDE_PATH_INFO,
+                                  pathInfo);
+            specialAttributes.put(RequestDispatcher.INCLUDE_QUERY_STRING,
+                                  queryString);
+            break;
+        case FORWARD:
+        case ERROR:
+            specialAttributes.put(RequestDispatcher.FORWARD_REQUEST_URI,
+                                  requestUri);
+            specialAttributes.put(RequestDispatcher.FORWARD_CONTEXT_PATH,
+                                  contextPath);
+            specialAttributes.put(RequestDispatcher.FORWARD_SERVLET_PATH,
+                                  servletPath);
+            specialAttributes.put(RequestDispatcher.FORWARD_PATH_INFO,
+                                  pathInfo);
+            specialAttributes.put(RequestDispatcher.FORWARD_QUERY_STRING,
+                                  queryString);
+            // Safe to cast because we received it in the ctor as HttpServletRequest
+            originalMapping = ((HttpServletRequest)getRequest()).getHttpServletMapping();
+            specialAttributes.put(RequestDispatcher.FORWARD_MAPPING, originalMapping);
+            break;
+        case ASYNC:
+            specialAttributes.put(AsyncContext.ASYNC_REQUEST_URI,
+                                  requestUri);
+            specialAttributes.put(AsyncContext.ASYNC_CONTEXT_PATH,
+                                  contextPath);
+            specialAttributes.put(AsyncContext.ASYNC_SERVLET_PATH,
+                                  servletPath);
+            // Safe to cast because we received it in the ctor as HttpServletRequest
+            originalMapping = ((HttpServletRequest)getRequest()).getHttpServletMapping();
+            specialAttributes.put(AsyncContext.ASYNC_MAPPING,
+                                  originalMapping);
+            specialAttributes.put(AsyncContext.ASYNC_PATH_INFO,
+                                  pathInfo);
+            specialAttributes.put(AsyncContext.ASYNC_QUERY_STRING,
+                                  queryString);
+            break;
+        default: // REQUEST
+            break;
+        }
+    }
+
+    /**
+     * Merge the two sets of parameter values into a single String array.
+     *
+     * @param values1 First set of values
+     * @param values2 Second set of values
+     */
+    protected String[] mergeValues(Object values1, Object values2) {
+
+        ArrayList<String> results = new ArrayList<String>();
+
+        if (values1 == null)
+            ;
+        else if (values1 instanceof String)
+            results.add((String)values1);
+        else if (values1 instanceof String[]) {
+            String values[] = (String[]) values1;
+            for (int i = 0; i < values.length; i++)
+                results.add(values[i]);
+        } else
+            results.add(values1.toString());
+
+        if (values2 == null)
+            ;
+        else if (values2 instanceof String)
+            results.add((String)values2);
+        else if (values2 instanceof String[]) {
+            String values[] = (String[]) values2;
+            for (int i = 0; i < values.length; i++)
+                results.add(values[i]);
+        } else
+            results.add(values2.toString());
+
+        String values[] = new String[results.size()];
+        return results.toArray(values);
+
+    }
+
+
+    // ------------------------------------------------------ Private Methods
+
+    /**
+     * Merge the parameters from the saved query parameter string (if any), and
+     * the parameters already present on this request (if any), such that the
+     * parameter values from the query string show up first if there are
+     * duplicate parameter names.
+     */
+    private void mergeParameters() {
+
+        if ((queryParamString == null) || (queryParamString.length() < 1)) {
+            return;
+        }
+
+        HashMap<String, String[]> queryParameters = new HashMap<String, String[]>();
+        String encoding = getCharacterEncoding();
+        if (encoding == null)
+            encoding = Globals.ISO_8859_1_ENCODING;
+        try {
+            RequestUtil.parseParameters
+                (queryParameters, queryParamString, encoding);
+        } catch (Exception e) {
+            // Ignore
+        }
+        synchronized (parameters) {
+            // Merge any query parameters whose names are present in the
+            // original parameter map
+            Iterator<String> keys = parameters.keySet().iterator();
+            while (keys.hasNext()) {
+                String key = keys.next();
+                Object queryValue = queryParameters.get(key);
+                if (queryValue != null) {
+                    parameters.put
+                        (key, mergeValues(queryValue, parameters.get(key)));
+                }
+            }
+            // Add any query parameters whose names are not present in the
+            // original parameter map
+            for (Map.Entry<String, String[]> e : queryParameters.entrySet()) {
+                String key = e.getKey();
+                if (parameters.get(key) == null) {
+                    parameters.put(key, e.getValue());
+                }
+            }
+        }
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private Map<String, String> getSessionVersions() {
+        return (Map<String, String>) getAttribute(
+                Globals.SESSION_VERSIONS_REQUEST_ATTRIBUTE);
+    }
+
+
+    // ----------------------------------- AttributeNamesEnumerator Inner Class
+
+    /**
+     * Utility class used to expose the special attributes as being available
+     * as request attributes.
+     */
+    protected class AttributeNamesEnumerator implements Enumeration<String> {
+
+        protected Enumeration<String> parentEnumeration = null;
+        protected String next = null;
+        private Iterator<String> specialNames = null;
+
+        public AttributeNamesEnumerator() {
+            parentEnumeration = getRequest().getAttributeNames();
+            if (specialAttributes != null) {
+                specialNames = specialAttributes.keySet().iterator();
+            }
+        }
+
+        public boolean hasMoreElements() {
+            return (specialNames != null && specialNames.hasNext())
+                    || (next != null) 
+                    || ((next = findNext()) != null);
+        }
+
+        public String nextElement() {
+
+            if (specialNames != null && specialNames.hasNext()) {
+                return specialNames.next();
+            }
+
+            String result = next;
+            if (next != null) {
+                next = findNext();
+            } else {
+                throw new NoSuchElementException();
+            }
+            return result;
+        }
+
+        protected String findNext() {
+            String result = null;
+            while ((result == null) && (parentEnumeration.hasMoreElements())) {
+                String current = parentEnumeration.nextElement();
+                if (!isSpecial(current) ||
+                        (!dispatcherType.equals(DispatcherType.FORWARD) &&
+                        current.startsWith("javax.servlet.forward") &&
+                        getAttribute(current) != null)) {
+                    result = current;
+                }
+            }
+            return result;
+        }
+    }
+
+    /**
+     * Increments the version of the given session, and stores it as a
+     * request attribute, so it can later be included in a response cookie.
+     */
+    private void incrementSessionVersion(StandardSession ss,
+                                         Context context) {
+        if (ss == null || context == null) {
+            return;
+        }
+
+        String versionString = Long.toString(ss.incrementVersion());
+        Map<String, String> sessionVersions = getSessionVersions();
+        if (sessionVersions == null) {
+            sessionVersions = new HashMap<String, String>();
+            setAttribute(Globals.SESSION_VERSIONS_REQUEST_ATTRIBUTE,
+                         sessionVersions);
+        }
+        String path = context.getPath();
+        if ("".equals(path)) {
+            path = "/";
+        }
+        sessionVersions.put(path, versionString);
+    }
+
+    /**
+     * Gets the facade for the request implementation object.
+     */
+    public RequestFacade getRequestFacade() {
+        if (getRequest() instanceof RequestFacade) {
+            return ((RequestFacade) (getRequest()));
+        } else {
+            return ((ApplicationHttpRequest) (getRequest())).getRequestFacade();
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationHttpResponse.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationHttpResponse.java
new file mode 100644
index 0000000..8caf991
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationHttpResponse.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.io.IOException;
+import java.util.Locale;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.http.HttpServletResponse</code>
+ * that transforms an application response object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.http.HttpServletResponseWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.HttpResponse</code>.
+ * <p>
+ * <strong>WARNING</strong>:  Due to Java's lack of support for multiple
+ * inheritance, all of the logic in <code>ApplicationResponse</code> is
+ * duplicated in <code>ApplicationHttpResponse</code>.  Make sure that you
+ * keep these two classes in synchronization when making changes!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:27:32 $
+ */
+
+/** PWC 4858179
+class ApplicationHttpResponse extends HttpServletResponseWrapper {
+**/
+// START OF PWC 4858179
+public class ApplicationHttpResponse extends HttpServletResponseWrapper {
+// END OF PWC 4858179
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped response around the specified servlet response.
+     *
+     * @param response The servlet response being wrapped
+     */
+    public ApplicationHttpResponse(HttpServletResponse response) {
+
+        this(response, false);
+
+    }
+
+
+    /**
+     * Construct a new wrapped response around the specified servlet response.
+     *
+     * @param response The servlet response being wrapped
+     * @param included <code>true</code> if this response is being processed
+     *  by a <code>RequestDispatcher.include()</code> call
+     */
+    public ApplicationHttpResponse(HttpServletResponse response,
+                                   boolean included) {
+
+        super(response);
+        setIncluded(included);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Is this wrapped response the subject of an <code>include()</code>
+     * call?
+     */
+    protected boolean included = false;
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.core.ApplicationHttpResponse/1.0";
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    /**
+     * Disallow <code>reset()</code> calls on a included response.
+     *
+     * @exception IllegalStateException if the response has already
+     *  been committed
+     */
+    public void reset() {
+
+        // If already committed, the wrapped response will throw ISE
+        if (!included || getResponse().isCommitted())
+            getResponse().reset();
+
+    }
+
+
+    /**
+     * Disallow <code>setContentLength()</code> calls on an included response.
+     *
+     * @param len The new content length
+     */
+    public void setContentLength(int len) {
+
+        if (!included)
+            getResponse().setContentLength(len);
+
+    }
+
+
+    /**
+     * Disallow <code>setContentType()</code> calls on an included response.
+     *
+     * @param type The new content type
+     */
+    public void setContentType(String type) {
+
+        if (!included)
+            getResponse().setContentType(type);
+
+    }
+
+
+    /**
+     * Disallow <code>setLocale()</code> calls on an included response.
+     *
+     * @param loc The new locale
+     */
+    public void setLocale(Locale loc) {
+
+        if (!included)
+            getResponse().setLocale(loc);
+
+    }
+
+
+    /**
+     * Ignore <code>setBufferSize()</code> calls on an included response.
+     *
+     * @param size The buffer size
+     */
+    public void setBufferSize(int size) {
+        if (!included)
+            getResponse().setBufferSize(size);
+    }
+
+
+    // -------------------------------------------- HttpServletResponse Methods
+
+
+    /**
+     * Disallow <code>addCookie()</code> calls on an included response.
+     *
+     * @param cookie The new cookie
+     */
+    public void addCookie(Cookie cookie) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).addCookie(cookie);
+
+    }
+
+
+    /**
+     * Disallow <code>addDateHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void addDateHeader(String name, long value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).addDateHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>addHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void addHeader(String name, String value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).addHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>addIntHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void addIntHeader(String name, int value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).addIntHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>sendError()</code> calls on an included response.
+     *
+     * @param sc The new status code
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendError(int sc) throws IOException {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).sendError(sc);
+
+    }
+
+
+    /**
+     * Disallow <code>sendError()</code> calls on an included response.
+     *
+     * @param sc The new status code
+     * @param msg The new message
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendError(int sc, String msg) throws IOException {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).sendError(sc, msg);
+
+    }
+
+
+    /**
+     * Disallow <code>sendRedirect()</code> calls on an included response.
+     *
+     * @param location The new location
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendRedirect(String location) throws IOException {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).sendRedirect(location);
+
+    }
+
+
+    /**
+     * Disallow <code>setDateHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void setDateHeader(String name, long value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setDateHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>setHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void setHeader(String name, String value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>setIntHeader()</code> calls on an included response.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void setIntHeader(String name, int value) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setIntHeader(name, value);
+
+    }
+
+
+    /**
+     * Disallow <code>setStatus()</code> calls on an included response.
+     *
+     * @param sc The new status code
+     */
+    public void setStatus(int sc) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setStatus(sc);
+
+    }
+
+
+    /**
+     * Disallow <code>setStatus()</code> calls on an included response.
+     *
+     * @param sc The new status code
+     * @param msg The new message
+     */
+    public void setStatus(int sc, String msg) {
+
+        if (!included)
+            ((HttpServletResponse) getResponse()).setStatus(sc, msg);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+    // START OF RIMOD 4858179
+    /**
+     * Return the included flag for this response.
+     */
+    public boolean isIncluded() {
+
+        return (this.included);
+
+    }
+
+    /*
+    boolean isIncluded() {
+
+        return (this.included);
+
+    }*/
+    // END OF RIMOD 4858179
+
+
+    /**
+     * Set the included flag for this response.
+     *
+     * @param included The new included flag
+     */
+    void setIncluded(boolean included) {
+
+        this.included = included;
+
+    }
+
+
+    /**
+     * Set the response that we are wrapping.
+     *
+     * @param response The new wrapped response
+     */
+    void setResponse(HttpServletResponse response) {
+
+        super.setResponse(response);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationPushBuilder.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationPushBuilder.java
new file mode 100644
index 0000000..b2841f7
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationPushBuilder.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2017-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.PushBuilder;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.connector.Request;
+
+import org.glassfish.grizzly.http.Cookie;
+import org.glassfish.grizzly.http.Method;
+import org.glassfish.grizzly.http.server.http2.PushEvent;
+import org.glassfish.grizzly.http2.Http2Stream;
+import org.glassfish.grizzly.http.util.Header;
+import org.glassfish.grizzly.http.util.MimeHeaders;
+
+/**
+ * Implementation of javax.servlet.http.PushBuilder.
+ *
+ * @author Shing Wai Chan
+ */
+public class ApplicationPushBuilder implements PushBuilder {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    private static final Header[] REMOVE_HEADERS = {
+            Header.ETag,
+            Header.IfModifiedSince,
+            Header.IfNoneMatch,
+            Header.IfRange,
+            Header.IfUnmodifiedSince,
+            Header.IfMatch,
+            Header.LastModified,
+            Header.Referer,
+            Header.AcceptRanges,
+            Header.Range,
+            Header.AcceptRanges,
+            Header.ContentRange,
+            Header.Authorization,
+            Header.ProxyAuthenticate,
+            Header.ProxyAuthorization,
+    };
+
+    // RFC 7232
+    private static final Header[] CONDITIONAL_HEADERS = {
+            Header.IfModifiedSince,
+            Header.IfNoneMatch,
+            Header.IfRange,
+            Header.IfUnmodifiedSince,
+            Header.IfMatch,
+    };
+
+    private final HttpServletRequest baseRequest;
+    private final Request catalinaRequest;
+    private final org.glassfish.grizzly.http.server.Request coyoteRequest;
+    private final String sessionCookieName;
+    private final boolean addSessionCookie;
+    private final boolean addSessionPathParameter;
+
+    private final MimeHeaders headers = new MimeHeaders();
+    private final List<Cookie> cookies = new ArrayList<>();
+    private String method = "GET";
+    private String path;
+    private String queryString;
+    private String sessionId;
+
+    public ApplicationPushBuilder(HttpServletRequest request) {
+        baseRequest = request;
+
+        // Need a reference to the CoyoteRequest in order to process the push
+        ServletRequest current = request;
+        while (current instanceof ServletRequestWrapper) {
+            current = ((ServletRequestWrapper) current).getRequest();
+        }
+        if (current instanceof Request) {
+            catalinaRequest = ((Request) current);
+            coyoteRequest = catalinaRequest.getCoyoteRequest();
+        } else {
+            String msg = MessageFormat.format(
+                    rb.getString(LogFacade.NO_PUSH_COYOTE_REQUEST_EXCEPTION), current.getClass().getName());
+            throw new UnsupportedOperationException(msg);
+        }
+
+        headers.copyFrom(coyoteRequest.getRequest().getHeaders());
+        String authorization = headers.getHeader(Header.Authorization);
+
+        for (Header removeHeader : REMOVE_HEADERS) {
+            headers.removeHeader(removeHeader);
+        }
+
+        if (request.getRemoteUser() != null) {
+            headers.addValue(Header.Authorization).setString(authorization);
+        }
+
+        // set the referer header
+        StringBuffer referer = request.getRequestURL();
+        if (request.getQueryString() != null) {
+            referer.append('?');
+            referer.append(request.getQueryString());
+
+        }
+        headers.addValue(Header.Referer).setString(referer.toString());
+
+        // Session
+        Context context = catalinaRequest.getContext();
+        sessionCookieName = context.getSessionCookieName();
+
+        HttpSession session = request.getSession(false);
+        if (session != null) {
+            sessionId = session.getId();
+        }
+        if (sessionId == null) {
+            sessionId = request.getRequestedSessionId();
+        }
+        if (!request.isRequestedSessionIdFromCookie() && !request.isRequestedSessionIdFromURL() &&
+                sessionId != null) {
+            Set<SessionTrackingMode> sessionTrackingModes =
+                    request.getServletContext().getEffectiveSessionTrackingModes();
+            addSessionCookie = sessionTrackingModes.contains(SessionTrackingMode.COOKIE);
+            addSessionPathParameter = sessionTrackingModes.contains(SessionTrackingMode.URL);
+        } else {
+            addSessionCookie = request.isRequestedSessionIdFromCookie();
+            addSessionPathParameter = request.isRequestedSessionIdFromURL();
+        }
+
+        // Cookies
+        if (request.getCookies() != null) {
+            for (javax.servlet.http.Cookie c : request.getCookies()) {
+                cookies.add(new Cookie(c.getName(), c.getValue()));
+            }
+        }
+
+        org.glassfish.grizzly.http.server.Response coyoteResponse = coyoteRequest.getResponse();
+        for (Cookie responseCookie : coyoteResponse.getCookies()) {
+            if (responseCookie.getMaxAge() > 0) {
+                cookies.add(new Cookie(responseCookie.getName(), responseCookie.getValue()));
+            } else {
+                // Path information not available so can only remove based on name.
+                Iterator<Cookie> cookieIterator = cookies.iterator();
+                while (cookieIterator.hasNext()) {
+                    Cookie cookie = cookieIterator.next();
+                    if (cookie.getName().equals(responseCookie.getName())) {
+                        cookieIterator.remove();
+                    }
+                }
+            }
+        }
+
+        if (cookies != null && !cookies.isEmpty()) {
+            for (Cookie c : cookies) {
+                headers.addValue(Header.Cookie).setString(c.asClientCookieString());
+            }
+        }
+    }
+
+    @Override
+    public PushBuilder method(String method) {
+        if (method == null) {
+            throw new NullPointerException(rb.getString(LogFacade.NULL_PUSH_METHOD_EXCEPTION));
+        }
+
+        if (method.length() == 0) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.EMPTY_PUSH_METHOD_EXCEPTION));
+        }
+
+        String upperMethod = method.toUpperCase(Locale.ENGLISH);
+        if (upperMethod.equals("POST") || upperMethod.equals("PUT") ||
+                upperMethod.equals("DELETE") || upperMethod.equals("CONNECT") ||
+                upperMethod.equals("OPTIONS") || upperMethod.equals("TRACE")) {
+
+            String msg = MessageFormat.format(
+                    rb.getString(LogFacade.NONCACHEABLE_UNSAFE_PUSH_METHOD_EXCEPTION), method);
+            throw new IllegalArgumentException(msg);
+        }
+
+        this.method = method;
+        return this;
+    }
+
+    @Override
+    public String getMethod() {
+        return method;
+    }
+
+    @Override
+    public PushBuilder queryString(String queryString) {
+        this.queryString = queryString;
+        return this;
+    }
+
+    @Override
+    public String getQueryString() {
+        return queryString;
+    }
+
+    @Override
+    public PushBuilder sessionId(String sessionId) {
+        this.sessionId = sessionId;
+        return this;
+    }
+
+    @Override
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    @Override
+    public PushBuilder addHeader(String name, String value) {
+        if (isValidNameValue(name, value)) {
+            headers.addValue(name).setString(value);
+        }
+        return this;
+    }
+
+    @Override
+    public PushBuilder setHeader(String name, String value) {
+        if (isValidNameValue(name, value)) {
+            headers.setValue(name).setString(value);
+        }
+        return this;
+    }
+
+    @Override
+    public PushBuilder removeHeader(String name) {
+        if (isValidName(name)) {
+            headers.removeHeader(name);
+        }
+        return this;
+    }
+
+    @Override
+    public Set<String> getHeaderNames() {
+        HashSet<String> names = new HashSet<>();
+        Iterator<String> nameIter = headers.names().iterator();
+        while (nameIter.hasNext()) {
+            names.add(nameIter.next());
+        }
+        return names;
+    }
+
+    @Override
+    public String getHeader(String name) {
+        return headers.getHeader(name);
+    }
+
+    @Override
+    public PushBuilder path(String path) {
+        if (path != null && !path.startsWith("/")) {
+            this.path = baseRequest.getContextPath() + "/" + path;
+        } else {
+            this.path = path;
+        }
+        return this;
+    }
+
+    @Override
+    public String getPath() {
+        return path;
+    }
+
+    @Override
+    public void push() {
+        if (path == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.NO_PUSH_PATH_EXCEPTION));
+        }
+
+        Http2Stream http2Stream = (Http2Stream) coyoteRequest.getAttribute(Http2Stream.HTTP2_STREAM_ATTRIBUTE);
+        if (http2Stream == null || !http2Stream.isPushEnabled()) {
+            return;
+        }
+
+        // modify pathLocal rather than path
+        String pathLocal = ((path.charAt(0) == '/') ? path : coyoteRequest.getContextPath() + '/' + path);
+        if (queryString != null) {
+            pathLocal += ((pathLocal.indexOf('?') != -1)
+                    ? '&' + queryString
+                    : '?' + queryString);
+        }
+
+        // Session ID (do this before setting the path since it may change it)
+        if (sessionId != null) {
+            if (addSessionPathParameter) {
+                pathLocal = pathLocal + ";" + sessionCookieName + "=" + sessionId;
+            }
+            if (addSessionCookie) {
+                cookies.add(new Cookie(sessionCookieName, sessionId));
+            }
+        }
+
+        PushEvent.PushEventBuilder pushEventBuilder = PushEvent.builder();
+        pushEventBuilder.method(method);
+        pushEventBuilder.headers(headers);
+        pushEventBuilder.path(pathLocal);
+        pushEventBuilder.httpRequest(coyoteRequest.getRequest());
+
+        coyoteRequest.getContext().notifyDownstream(pushEventBuilder.build());
+
+        // Reset for next call
+        path = null;
+        for (Header conditionalHeader : CONDITIONAL_HEADERS) {
+            headers.removeHeader(conditionalHeader);
+        }
+    }
+
+
+    private static boolean isValidNameValue(final String name, final String value) {
+        return isValidName(name) && value != null && !value.isEmpty();
+    }
+
+    private static boolean isValidName(final String name) {
+        return (name != null && !name.isEmpty());
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationRequest.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationRequest.java
new file mode 100644
index 0000000..981c426
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationRequest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.ServletRequest</code>
+ * that transforms an application request object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.ServletRequestWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.Request</code>.
+ * <p>
+ * <strong>WARNING</strong>:  Due to Java's lack of support for multiple
+ * inheritance, all of the logic in <code>ApplicationRequest</code> is
+ * duplicated in <code>ApplicationHttpRequest</code>.  Make sure that you
+ * keep these two classes in synchronization when making changes!
+ *
+ * @author Craig R. McClanahan
+ * @version $Id: ApplicationRequest.java 1360933 2012-07-12 20:51:27Z markt $
+ */
+
+class ApplicationRequest extends ServletRequestWrapper {
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The set of attribute names that are special for request dispatchers.
+     */
+    private static final HashSet<String> specials = new HashSet<String>(15);
+
+    static {
+        specials.add(RequestDispatcher.INCLUDE_REQUEST_URI);
+        specials.add(RequestDispatcher.INCLUDE_CONTEXT_PATH);
+        specials.add(RequestDispatcher.INCLUDE_SERVLET_PATH);
+        specials.add(RequestDispatcher.INCLUDE_PATH_INFO);
+        specials.add(RequestDispatcher.INCLUDE_QUERY_STRING);
+        specials.add(RequestDispatcher.FORWARD_REQUEST_URI);
+        specials.add(RequestDispatcher.FORWARD_CONTEXT_PATH);
+        specials.add(RequestDispatcher.FORWARD_SERVLET_PATH);
+        specials.add(RequestDispatcher.FORWARD_PATH_INFO);
+        specials.add(RequestDispatcher.FORWARD_QUERY_STRING);
+        specials.add(AsyncContext.ASYNC_REQUEST_URI);
+        specials.add(AsyncContext.ASYNC_CONTEXT_PATH);
+        specials.add(AsyncContext.ASYNC_SERVLET_PATH);
+        specials.add(AsyncContext.ASYNC_PATH_INFO);
+        specials.add(AsyncContext.ASYNC_QUERY_STRING);
+    }
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped request around the specified servlet request.
+     *
+     * @param request The servlet request being wrapped
+     */
+    public ApplicationRequest(ServletRequest request) {
+
+        super(request);
+        setRequest(request);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The request attributes for this request.  This is initialized from the
+     * wrapped request, but updates are allowed.
+     */
+    protected final HashMap<String, Object> attributes = new HashMap<String, Object>();
+
+
+    // ------------------------------------------------- ServletRequest Methods
+
+
+    /**
+     * Override the <code>getAttribute()</code> method of the wrapped request.
+     *
+     * @param name Name of the attribute to retrieve
+     */
+    @Override
+    public Object getAttribute(String name) {
+
+        synchronized (attributes) {
+            return (attributes.get(name));
+        }
+
+    }
+
+
+    /**
+     * Override the <code>getAttributeNames()</code> method of the wrapped
+     * request.
+     */
+    @Override
+    public Enumeration<String> getAttributeNames() {
+
+        synchronized (attributes) {
+            return Collections.enumeration(attributes.keySet());
+        }
+
+    }
+
+
+    /**
+     * Override the <code>removeAttribute()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the attribute to remove
+     */
+    @Override
+    public void removeAttribute(String name) {
+
+        synchronized (attributes) {
+            attributes.remove(name);
+            if (!isSpecial(name))
+                getRequest().removeAttribute(name);
+        }
+
+    }
+
+
+    /**
+     * Override the <code>setAttribute()</code> method of the
+     * wrapped request.
+     *
+     * @param name Name of the attribute to set
+     * @param value Value of the attribute to set
+     */
+    @Override
+    public void setAttribute(String name, Object value) {
+
+        synchronized (attributes) {
+            attributes.put(name, value);
+            if (!isSpecial(name))
+                getRequest().setAttribute(name, value);
+        }
+
+    }
+
+
+    // ------------------------------------------ ServletRequestWrapper Methods
+
+
+    /**
+     * Set the request that we are wrapping.
+     *
+     * @param request The new wrapped request
+     */
+    @Override
+    public void setRequest(ServletRequest request) {
+
+        super.setRequest(request);
+
+        // Initialize the attributes for this request
+        synchronized (attributes) {
+            attributes.clear();
+            Enumeration<String> names = request.getAttributeNames();
+            while (names.hasMoreElements()) {
+                String name = names.nextElement();
+                Object value = request.getAttribute(name);
+                attributes.put(name, value);
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Is this attribute name one of the special ones that is added only for
+     * included servlets?
+     *
+     * @param name Attribute name to be tested
+     */
+    protected boolean isSpecial(String name) {
+        return specials.contains(name);
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationResponse.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationResponse.java
new file mode 100644
index 0000000..07cf299
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationResponse.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+import java.util.Locale;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.ServletResponse</code>
+ * that transforms an application response object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.ServletResponseWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.Response</code>.
+ * <p>
+ * <strong>WARNING</strong>:  Due to Java's lack of support for multiple
+ * inheritance, all of the logic in <code>ApplicationResponse</code> is
+ * duplicated in <code>ApplicationHttpResponse</code>.  Make sure that you
+ * keep these two classes in synchronization when making changes!
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:27:33 $
+ */
+
+/** START OF PWC 4858179
+class ApplicationResponse extends ServletResponseWrapper {
+**/
+// START OF PWC 4858179
+public class ApplicationResponse extends ServletResponseWrapper {
+// END OF PWC 4858179
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped response around the specified servlet response.
+     *
+     * @param response The servlet response being wrapped
+     */
+    public ApplicationResponse(ServletResponse response) {
+
+        this(response, false);
+
+    }
+
+
+    /**
+     * Construct a new wrapped response around the specified servlet response.
+     *
+     * @param response The servlet response being wrapped
+     * @param included <code>true</code> if this response is being processed
+     *  by a <code>RequestDispatcher.include()</code> call
+     */
+    public ApplicationResponse(ServletResponse response, boolean included) {
+
+        super(response);
+        setIncluded(included);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Is this wrapped response the subject of an <code>include()</code>
+     * call?
+     */
+    protected boolean included = false;
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    /**
+     * Disallow <code>reset()</code> calls on a included response.
+     *
+     * @exception IllegalStateException if the response has already
+     *  been committed
+     */
+    public void reset() {
+
+        // If already committed, the wrapped response will throw ISE
+        if (!included || getResponse().isCommitted())
+            getResponse().reset();
+
+    }
+
+
+    /**
+     * Disallow <code>setContentLength()</code> calls on an included response.
+     *
+     * @param len The new content length
+     */
+    public void setContentLength(int len) {
+
+        if (!included)
+            getResponse().setContentLength(len);
+
+    }
+
+
+    /**
+     * Disallow <code>setContentType()</code> calls on an included response.
+     *
+     * @param type The new content type
+     */
+    public void setContentType(String type) {
+
+        if (!included)
+            getResponse().setContentType(type);
+
+    }
+
+
+    /**
+     * Ignore <code>setLocale()</code> calls on an included response.
+     *
+     * @param loc The new locale
+     */
+    public void setLocale(Locale loc) {
+        if (!included)
+            getResponse().setLocale(loc);
+    }
+
+
+    /**
+     * Ignore <code>setBufferSize()</code> calls on an included response.
+     *
+     * @param size The buffer size
+     */
+    public void setBufferSize(int size) {
+        if (!included)
+            getResponse().setBufferSize(size);
+    }
+
+
+    // ----------------------------------------- ServletResponseWrapper Methods
+
+
+    /**
+     * Set the response that we are wrapping.
+     *
+     * @param response The new wrapped response
+     */
+    public void setResponse(ServletResponse response) {
+
+        super.setResponse(response);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+    // START OF PWC 4858179
+    /**
+     * Return the included flag for this response.
+     */
+    public boolean isIncluded() {
+
+        return (this.included);
+
+    }
+    
+    /**
+    public boolean isIncluded() {
+
+        return (this.included);
+
+    }
+     */
+    // END OF PWC 4858179
+
+
+    /**
+     * Set the included flag for this response.
+     *
+     * @param included The new included flag
+     */
+    void setIncluded(boolean included) {
+
+        this.included = included;
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/Constants.java
new file mode 100644
index 0000000..751e7ef
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/Constants.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.core";
+    public static final int MAJOR_VERSION = 4;
+    public static final int MINOR_VERSION = 0;
+
+    public static final String JSP_SERVLET_CLASS =
+        "org.apache.jasper.servlet.JspServlet";
+
+    public static final String JSP_SERVLET_NAME = "jsp";
+
+    public static final String DEFAULT_SERVLET_NAME = "default";
+
+    public static final String IS_DEFAULT_ERROR_PAGE_ENABLED_INIT_PARAM = "org.glassfish.web.isDefaultErrorPageEnabled";
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContainerBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContainerBase.java
new file mode 100644
index 0000000..bb80a0f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContainerBase.java
@@ -0,0 +1,1675 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.text.MessageFormat;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletException;
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.ContainerListener;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Pipeline;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.Valve;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.naming.resources.ProxyDirContext;
+import org.glassfish.web.valve.GlassFishValve;
+
+
+/**
+ * Abstract implementation of the <b>Container</b> interface, providing common
+ * functionality required by nearly every implementation.  Classes extending
+ * this base class must implement <code>getInfo()</code>, and may implement
+ * a replacement for <code>invoke()</code>.
+ * <p>
+ * All subclasses of this abstract base class will include support for a
+ * Pipeline object that defines the processing to be performed for each request
+ * received by the <code>invoke()</code> method of this class, utilizing the
+ * "Chain of Responsibility" design pattern.  A subclass should encapsulate its
+ * own processing functionality as a <code>Valve</code>, and configure this
+ * Valve into the pipeline by calling <code>setBasic()</code>.
+ * <p>
+ * This implementation fires property change events, per the JavaBeans design
+ * pattern, for changes in singleton properties.  In addition, it fires the
+ * following <code>ContainerEvent</code> events to listeners who register
+ * themselves with <code>addContainerListener()</code>:
+ * <table border=1>
+ *   <tr>
+ *     <th>Type</th>
+ *     <th>Data</th>
+ *     <th>Description</th>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>addChild</code></td>
+ *     <td align=center><code>Container</code></td>
+ *     <td>Child container added to this Container.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>addValve</code></td>
+ *     <td align=center><code>Valve</code></td>
+ *     <td>Valve added to this Container.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>removeChild</code></td>
+ *     <td align=center><code>Container</code></td>
+ *     <td>Child container removed from this Container.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>removeValve</code></td>
+ *     <td align=center><code>Valve</code></td>
+ *     <td>Valve removed from this Container.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>start</code></td>
+ *     <td align=center><code>null</code></td>
+ *     <td>Container was started.</td>
+ *   </tr>
+ *   <tr>
+ *     <td align=center><code>stop</code></td>
+ *     <td align=center><code>null</code></td>
+ *     <td>Container was stopped.</td>
+ *   </tr>
+ * </table>
+ * Subclasses that fire additional events should document them in the
+ * class comments of the implementation class.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public abstract class ContainerBase
+    implements Container, Lifecycle, Pipeline {
+
+    protected static final Logger log = LogFacade.getLogger();
+    protected static final ResourceBundle rb = log.getResourceBundle();
+
+
+
+    /**
+     * Perform addChild with the permissions of this class.
+     * addChild can be called with the XML parser on the stack,
+     * this allows the XML parser to have fewer privileges than
+     * Tomcat.
+     */
+    protected class PrivilegedAddChild
+        implements PrivilegedAction<Void> {
+
+        private Container child;
+
+        PrivilegedAddChild(Container child) {
+            this.child = child;
+        }
+
+        public Void run() {
+            addChildInternal(child);
+            return null;
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The child Containers belonging to this Container, keyed by name.
+     */
+    protected Map<String, Container> children = new LinkedHashMap<String, Container>();
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    protected int debug = 0;
+
+
+    /**
+     * The processor delay for this component.
+     */
+    protected int backgroundProcessorDelay = -1;
+
+
+    /**
+     * Flag indicating whether a check to see if the request is secure is
+     * required before adding Pragma and Cache-Control headers when proxy 
+     * caching has been disabled
+     */
+    protected boolean checkIfRequestIsSecure = false;
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The container event listeners for this Container.
+     */
+    protected ArrayList<ContainerListener> listeners =
+        new ArrayList<ContainerListener>();
+    private ContainerListener[] listenersArray = new ContainerListener[0];
+
+
+    /**
+     * The Loader implementation with which this Container is associated.
+     */
+    protected Loader loader = null;
+
+    private ReadWriteLock lock = new ReentrantReadWriteLock();
+    protected Lock readLock = lock.readLock();
+    protected Lock writeLock = lock.writeLock();
+
+    /**
+     * The Logger implementation with which this Container is associated.
+     */
+    protected org.apache.catalina.Logger logger = null;
+
+    /**
+     * The Manager implementation with which this Container is associated.
+     */
+    protected Manager manager = null;
+
+    /**
+     * The human-readable name of this Container.
+     */
+    protected String name = null;
+
+
+    /**
+     * The parent Container to which this Container is a child.
+     */
+    protected Container parent = null;
+
+
+    /**
+     * The parent class loader to be configured when we install a Loader.
+     */
+    protected ClassLoader parentClassLoader = null;
+
+
+    /**
+     * The Pipeline object with which this Container is associated.
+     */
+    protected Pipeline pipeline = new StandardPipeline(this);
+
+
+    protected boolean hasCustomPipeline = false;
+
+
+    /**
+     * The Realm with which this Container is associated.
+     */
+    protected Realm realm = null;
+
+    /**
+     * The resources DirContext object with which this Container is associated.
+     */
+    protected DirContext resources = null;
+
+    /**
+     * Has this component been started?
+     */
+    protected volatile boolean started = false;
+
+    protected boolean initialized=false;
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * The background thread.
+     */
+    private Thread thread = null;
+
+
+    /**
+     * The background thread completion semaphore.
+     */
+    private volatile boolean threadDone = false;
+
+
+    /**
+     * Indicates whether ContainerListener instances need to be notified
+     * of a particular configuration event.
+     */
+    protected boolean notifyContainerListeners = true;
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * @return true if ContainerListener instances need to be notified
+     * of a particular configuration event, and false otherwise
+     */
+    boolean isNotifyContainerListeners() {
+        return notifyContainerListeners;
+    }    
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (this.debug);
+
+    }
+
+
+    /**
+     * Set the debugging detail level for this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+
+        int oldDebug = this.debug;
+        this.debug = debug;
+        support.firePropertyChange("debug", Integer.valueOf(oldDebug),
+                                   Integer.valueOf(this.debug));
+
+    }
+
+
+    /**
+     * Get the delay between the invocation of the backgroundProcess method on
+     * this container and its children. Child containers will not be invoked
+     * if their delay value is not negative (which would mean they are using 
+     * their own thread). Setting this to a positive value will cause 
+     * a thread to be spawn. After waiting the specified amount of time, 
+     * the thread will invoke the executePeriodic method on this container 
+     * and all its children.
+     */
+    public int getBackgroundProcessorDelay() {
+        return backgroundProcessorDelay;
+    }
+
+
+    /**
+     * Set the delay between the invocation of the execute method on this
+     * container and its children.
+     * 
+     * @param delay The delay in seconds between the invocation of 
+     *              backgroundProcess methods
+     */
+    public void setBackgroundProcessorDelay(int delay) {
+        backgroundProcessorDelay = delay;
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return this.getClass().getName();
+    }
+
+
+    /**
+     * Return the Loader with which this Container is associated.  If there is
+     * no associated Loader, return the Loader associated with our parent
+     * Container (if any); otherwise, return <code>null</code>.
+     */
+    public Loader getLoader() {
+
+        try {
+            readLock.lock();
+            if (loader != null)
+                return (loader);
+        } finally {
+            readLock.unlock();
+        }
+
+        if (parent != null)
+            return (parent.getLoader());
+
+        return (null);
+    }
+
+
+    /**
+     * Set the Loader with which this Container is associated.
+     *
+     * @param loader The newly associated loader
+     */
+    public void setLoader(Loader loader) {
+
+        Loader oldLoader;
+
+        try {
+	    writeLock.lock();
+
+            // Change components if necessary
+            oldLoader = this.loader;
+            if (oldLoader == loader)
+                return;
+            this.loader = loader;
+
+            // Stop the old component if necessary
+            if (started && (oldLoader != null) &&
+                    (oldLoader instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) oldLoader).stop();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_SET_LOADER_STOP, e);
+                }
+            }
+
+            // Start the new component if necessary
+            if (loader != null)
+                loader.setContainer(this);
+            if (started && (loader != null) &&
+                (loader instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) loader).start();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_SET_LOADER_START, e);
+                }
+            }
+        } finally {
+	    writeLock.unlock();
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("loader", oldLoader, this.loader);
+
+    }
+
+
+    /**
+     * Return the Logger with which this Container is associated.  If there is
+     * no associated Logger, return the Logger associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public org.apache.catalina.Logger getLogger() {
+
+        try {
+            readLock.lock();
+            if (logger != null)
+                return (logger);
+        } finally {
+            readLock.unlock();
+        }
+
+        if (parent != null)
+            return (parent.getLogger());
+
+        return (null);
+    }
+
+
+    /**
+     * Set the Logger with which this Container is associated.
+     *
+     * @param logger The newly associated Logger
+     */
+    public void setLogger(org.apache.catalina.Logger logger) {
+
+        org.apache.catalina.Logger oldLogger;
+
+        try {
+            writeLock.lock();
+            // Change components if necessary
+            oldLogger = this.logger;
+            if (oldLogger == logger)
+                return;
+            this.logger = logger;
+
+            // Stop the old component if necessary
+            if (started && (oldLogger != null) &&
+                    (oldLogger instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) oldLogger).stop();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_SET_LOGGER_STOP, e);
+                }
+            }
+
+        
+            // Start the new component if necessary
+            if (logger != null)
+                logger.setContainer(this);
+            if (started && (logger != null) &&
+                (logger instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) logger).start();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_SET_LOGGER_START, e);
+                }
+            }
+        } finally {
+            writeLock.unlock();
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("logger", oldLogger, this.logger);
+
+    }
+
+
+    /**
+     * Return the Manager with which this Container is associated.  If there is
+     * no associated Manager, return the Manager associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Manager getManager() {
+
+        try {
+            readLock.lock();
+            if (manager != null)
+                return (manager);
+        } finally {
+            readLock.unlock();
+        }
+
+        if (parent != null)
+            return (parent.getManager());
+
+        return (null);
+    }
+
+
+    /**
+     * Set the Manager with which this Container is associated.
+     *
+     * @param manager The newly associated Manager
+     */
+    public void setManager(Manager manager) {
+
+        Manager oldManager;
+
+        try {
+            writeLock.lock();
+            // Change components if necessary
+            oldManager = this.manager;
+            if (oldManager == manager)
+                return;
+            this.manager = manager;
+
+            // Stop the old component if necessary
+            if (started && (oldManager != null) &&
+                    (oldManager instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) oldManager).stop();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_SET_MANAGER_STOP, e);
+                }
+            }
+
+            // Start the new component if necessary
+            if (manager != null)
+                manager.setContainer(this);
+            if (started && (manager != null) &&
+                    (manager instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) manager).start();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_SET_MANAGER_START, e);
+                }
+            }
+        } finally {
+            writeLock.unlock();
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("manager", oldManager, this.manager);
+    }
+
+
+    /**
+     * Return an object which may be utilized for mapping to this component.
+     */
+    public Object getMappingObject() {
+        return this;
+    }
+
+
+    /**
+     * Return a name string (suitable for use by humans) that describes this
+     * Container.  Within the set of child containers belonging to a particular
+     * parent, Container names must be unique.
+     */
+    public String getName() {
+
+        return (name);
+    }
+
+
+    /**
+     * Set a name string (suitable for use by humans) that describes this
+     * Container.  Within the set of child containers belonging to a particular
+     * parent, Container names must be unique.
+     *
+     * @param name New name of this container
+     *
+     * @exception IllegalStateException if this Container has already been
+     *  added to the children of a parent Container (after which the name
+     *  may not be changed)
+     */
+    public void setName(String name) {
+
+        String oldName = this.name;
+        this.name = name;
+        support.firePropertyChange("name", oldName, this.name);
+    }
+
+
+    /**
+     * Return the Container for which this Container is a child, if there is
+     * one.  If there is no defined parent, return <code>null</code>.
+     */
+    public Container getParent() {
+
+        return (parent);
+    }
+
+
+    /**
+     * Set the parent Container to which this Container is being added as a
+     * child.  This Container may refuse to become attached to the specified
+     * Container by throwing an exception.
+     *
+     * @param container Container to which this Container is being added
+     *  as a child
+     *
+     * @exception IllegalArgumentException if this Container refuses to become
+     *  attached to the specified Container
+     */
+    public void setParent(Container container) {
+
+        Container oldParent = this.parent;
+        this.parent = container;
+        support.firePropertyChange("parent", oldParent, this.parent);
+    }
+
+
+    /**
+     * Return the parent class loader (if any) for this web application.
+     * This call is meaningful only <strong>after</strong> a Loader has
+     * been configured.
+     */
+    public ClassLoader getParentClassLoader() {
+        if (parentClassLoader != null)
+            return (parentClassLoader);
+        if (parent != null) {
+            return (parent.getParentClassLoader());
+        }
+        return (ClassLoader.getSystemClassLoader());
+    }
+
+
+    /**
+     * Set the parent class loader (if any) for this web application.
+     * This call is meaningful only <strong>before</strong> a Loader has
+     * been configured, and the specified value (if non-null) should be
+     * passed as an argument to the class loader constructor.
+     *
+     *
+     * @param parent The new parent class loader
+     */
+    public void setParentClassLoader(ClassLoader parent) {
+        ClassLoader oldParentClassLoader = this.parentClassLoader;
+        this.parentClassLoader = parent;
+        support.firePropertyChange("parentClassLoader", oldParentClassLoader,
+                                   this.parentClassLoader);
+    }
+
+
+    /**
+     * Return the Pipeline object that manages the Valves associated with
+     * this Container.
+     */
+    public Pipeline getPipeline() {
+        return (this.pipeline);
+    }
+
+
+    /**
+     * @return true if this container was configured with a custom pipeline,
+     * false otherwise
+     */
+    public boolean hasCustomPipeline() {
+        return hasCustomPipeline;
+    }
+
+
+    /**
+     * Indicates whether the request will be checked to see if it is secure
+     * before adding Pragma and Cache-control headers when proxy caching has
+     * been disabled.
+     *
+     * @return true if the check is required; false otherwise.
+     */
+    public boolean isCheckIfRequestIsSecure() {
+        return checkIfRequestIsSecure;
+    }
+
+
+    /**
+     * Sets the checkIfRequestIsSecure property of this Container.
+     *
+     * Setting this property to true will check if the request is secure
+     * before adding Pragma and Cache-Control headers when proxy caching has
+     * been disabled.
+     *
+     * @param checkIfRequestIsSecure true if check is required, false
+     * otherwise
+     */
+    public void setCheckIfRequestIsSecure(boolean checkIfRequestIsSecure) {
+        this.checkIfRequestIsSecure = checkIfRequestIsSecure;
+    }
+
+
+    /**
+     * Return the Realm with which this Container is associated.  If there is
+     * no associated Realm, return the Realm associated with our parent
+     * Container (if any); otherwise return <code>null</code>.
+     */
+    public Realm getRealm() {
+        try {
+            readLock.lock();
+            if (realm != null)
+                return (realm);
+        } finally {
+            readLock.unlock();
+        }
+
+        if (parent != null)
+            return (parent.getRealm());
+
+        return (null);
+    }
+
+
+    /**
+     * Set the Realm with which this Container is associated.
+     *
+     * @param realm The newly associated Realm
+     */
+    public void setRealm(Realm realm) {
+
+        Realm oldRealm;
+
+        try {
+            writeLock.lock();
+            // Change components if necessary
+            oldRealm = this.realm;
+            if (oldRealm == realm)
+                return;
+            this.realm = realm;
+
+            // Stop the old component if necessary
+            if (started && (oldRealm != null) &&
+                    (oldRealm instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) oldRealm).stop();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_SET_REALM_STOP, e);
+                }
+            }
+
+            // Start the new component if necessary
+            if (realm != null)
+                realm.setContainer(this);
+            if (started && (realm != null) &&
+                    (realm instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) realm).start();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_SET_REALM_START, e);
+                }
+            }
+        } finally {
+            writeLock.unlock();
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("realm", oldRealm, this.realm);
+    }
+
+
+    /**
+      * Return the resources DirContext object with which this Container is
+      * associated.  If there is no associated resources object, return the
+      * resources associated with our parent Container (if any); otherwise
+      * return <code>null</code>.
+     */
+    public DirContext getResources() {
+
+        try {
+            readLock.lock();
+            if (resources != null)
+                return (resources);
+        } finally {
+            readLock.unlock();
+        }
+
+        if (parent != null)
+            return (parent.getResources());
+
+        return (null);
+    }
+
+
+    /**
+     * Set the resources DirContext object with which this Container is
+     * associated.
+     *
+     * @param resources The newly associated DirContext
+     */
+    public void setResources(DirContext resources) throws Exception {
+        // Called from StandardContext.setResources()
+        //              <- StandardContext.start() 
+        //              <- ContainerBase.addChildInternal() 
+
+        // Change components if necessary
+        DirContext oldResources;
+
+        try {
+            writeLock.lock();
+            oldResources = this.resources;
+            if (oldResources == resources)
+                return;
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            if (getParent() != null)
+                env.put(ProxyDirContext.HOST, getParent().getName());
+            env.put(ProxyDirContext.CONTEXT, getName());
+            this.resources = new ProxyDirContext(env, resources);
+            // Report this property change to interested listeners
+        } finally {
+            writeLock.unlock();
+        }
+
+        support.firePropertyChange("resources", oldResources,
+                                   this.resources);
+    }
+
+
+    // ------------------------------------------------------ Container Methods
+
+
+    /**
+     * Add a new child Container to those associated with this Container,
+     * if supported.  Prior to adding this Container to the set of children,
+     * the child's <code>setParent()</code> method must be called, with this
+     * Container as an argument.  This method may thrown an
+     * <code>IllegalArgumentException</code> if this Container chooses not
+     * to be attached to the specified Container, in which case it is not added
+     *
+     * @param child New child Container to be added
+     *
+     * @exception IllegalArgumentException if this exception is thrown by
+     *  the <code>setParent()</code> method of the child Container
+     * @exception IllegalArgumentException if the new child does not have
+     *  a name unique from that of existing children of this Container
+     * @exception IllegalStateException if this Container does not support
+     *  child Containers
+     */
+    public void addChild(Container child) {
+        if (Globals.IS_SECURITY_ENABLED) {
+            PrivilegedAction<Void> dp =
+                new PrivilegedAddChild(child);
+            AccessController.doPrivileged(dp);
+        } else {
+            addChildInternal(child);
+        }
+    }
+
+    private void addChildInternal(Container child) {
+        
+        if(log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Add child " + child + " " + this);
+        synchronized(children) {
+            if (children.get(child.getName()) != null) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.DUPLICATE_CHILD_NAME_EXCEPTION),
+                                                               child.getName());
+            throw new IllegalArgumentException(msg);
+            }
+            child.setParent(this);  // May throw IAE
+            if (started && (child instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) child).start();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_ADD_CHILD_START, e);
+                    throw new IllegalStateException
+                            (rb.getString(LogFacade.CONTAINER_BASE_ADD_CHILD_START) + e);
+                }
+            }
+            children.put(child.getName(), child);
+
+            if (notifyContainerListeners) {
+                fireContainerEvent(ADD_CHILD_EVENT, child);
+            }
+        }
+
+    }
+
+
+    /**
+     * Add a container event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addContainerListener(ContainerListener listener) {
+
+        synchronized (listeners) {
+            listeners.add(listener);
+            listenersArray = listeners.toArray(
+                new ContainerListener[listeners.size()]);
+        }
+
+    }
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return the child Container, associated with this Container, with
+     * the specified name (if any); otherwise, return <code>null</code>
+     *
+     * @param name Name of the child Container to be retrieved
+     */
+    public Container findChild(String name) {
+
+        if (name == null)
+            return (null);
+        synchronized (children) {       // Required by post-start changes
+            return children.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the set of children Containers associated with this Container.
+     * If this Container has no children, a zero-length array is returned.
+     */
+    public Container[] findChildren() {
+
+        synchronized (children) {
+            return children.values().toArray(new Container[children.size()]);
+        }
+
+    }
+
+
+    /**
+     * Return the set of container listeners associated with this Container.
+     * If this Container has no registered container listeners, a zero-length
+     * array is returned.
+     */
+    public ContainerListener[] findContainerListeners() {
+
+        synchronized (listeners) {
+            return listenersArray;
+        }
+    }
+
+
+    /**
+     * Process the specified Request, to produce the corresponding Response,
+     * by invoking the first Valve in our pipeline (if any), or the basic
+     * Valve otherwise.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     *
+     * @exception IllegalStateException if neither a pipeline or a basic
+     *  Valve have been configured for this Container
+     * @exception IOException if an input/output error occurred while
+     *  processing
+     * @exception ServletException if a ServletException was thrown
+     *  while processing this request
+     */
+    public void invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        pipeline.invoke(request, response);
+    }
+
+
+    /**
+     * Remove an existing child Container from association with this parent
+     * Container.
+     *
+     * @param child Existing child Container to be removed
+     */
+    public void removeChild(Container child) {
+        if (child == null) {
+            return;
+        }
+
+        synchronized(children) {
+            if (children.get(child.getName()) == null)
+                return;
+            children.remove(child.getName());
+        }
+
+        if (started && (child instanceof Lifecycle)) {
+            try {
+                if( child instanceof ContainerBase ) {
+                    if( ((ContainerBase)child).started ) {
+                        ((Lifecycle) child).stop();
+                    }
+                } else {
+                    ((Lifecycle) child).stop();
+                }
+            } catch (LifecycleException e) {
+                log.log(Level.SEVERE, LogFacade.CONTAINER_BASE_REMOVE_CHILD_STOP, e);
+            }
+        }
+        
+        if (notifyContainerListeners) {
+            fireContainerEvent(REMOVE_CHILD_EVENT, child);
+        }
+    
+        // child.setParent(null);
+    }
+
+
+    /**
+     * Remove a container event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeContainerListener(ContainerListener listener) {
+
+        synchronized (listeners) {
+            listeners.remove(listener);
+            listenersArray = listeners.toArray(
+                new ContainerListener[listeners.size()]);
+        }
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this Container.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Removes the given lifecycle event listener from this Container.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+
+    /**
+     * Removes any lifecycle event listeners from this Container.
+     */
+    public void removeLifecycleListeners() {
+        lifecycle.removeLifecycleListeners();
+    }
+
+
+    /**
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public synchronized void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.CONTAINER_STARTED, logName());
+            }
+            return;
+        }
+        
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        started = true;
+
+        // Start our subordinate components, if any
+        if ((loader != null) && (loader instanceof Lifecycle))
+            ((Lifecycle) loader).start();
+        if ((logger != null) && (logger instanceof Lifecycle))
+            ((Lifecycle) logger).start();
+        if ((manager != null) && (manager instanceof Lifecycle))
+            ((Lifecycle) manager).start();
+        if ((realm != null) && (realm instanceof Lifecycle))
+            ((Lifecycle) realm).start();
+        if ((resources != null) && (resources instanceof Lifecycle))
+            ((Lifecycle) resources).start();
+
+        // Start our child containers, if any
+        startChildren();
+
+        // Start the Valves in our pipeline (including the basic), if any
+        if (pipeline instanceof Lifecycle) {
+            ((Lifecycle) pipeline).start();
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+
+        // Start our thread
+        threadStart();
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public synchronized void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.CONTAINER_NOT_STARTED_EXCEPTION, logName());
+            }
+            return;
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        // Stop our thread
+        threadStop();
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Stop the Valves in our pipeline (including the basic), if any
+        if (pipeline instanceof Lifecycle) {
+            ((Lifecycle) pipeline).stop();
+        }
+
+        // Stop our child containers, if any
+        Container children[] = findChildren();
+        for (int i = 0; i < children.length; i++) {
+            if (children[i] instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) children[i]).stop();
+                } catch (Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_STOPPING_CONTAINER), children[i]);
+                    log.log(Level.SEVERE, msg, t);
+                }
+            }
+        }
+
+        // Remove children - so next start can work
+        children = findChildren();
+        for (int i = 0; i < children.length; i++) {
+            removeChild(children[i]);
+        }
+
+        // Stop our subordinate components, if any
+        if ((resources != null) && (resources instanceof Lifecycle)) {
+            ((Lifecycle) resources).stop();
+        }
+        if ((realm != null) && (realm instanceof Lifecycle)) {
+            ((Lifecycle) realm).stop();
+        }
+        if ((manager != null) && (manager instanceof Lifecycle)) {
+            ((Lifecycle) manager).stop();
+        }
+        if ((logger != null) && (logger instanceof Lifecycle)) {
+            ((Lifecycle) logger).stop();
+        }
+        if ((loader != null) && (loader instanceof Lifecycle)) {
+            ((Lifecycle) loader).stop();
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+    }
+
+
+    /** Init method, part of the MBean lifecycle.
+     *  If the container was added via JMX, it'll register itself with the 
+     * parent, using the ObjectName conventions to locate the parent.
+     * 
+     *  If the container was added directly and it doesn't have an ObjectName,
+     * it'll create a name and register itself with the JMX console. On destroy(), 
+     * the object will unregister.
+     * 
+     * @throws Exception
+     */
+    public void init() throws Exception {
+        initialized=true;
+    }
+    
+    public ObjectName getParentName() throws MalformedObjectNameException {
+        return null;
+    }
+    
+    public void destroy() throws Exception {
+        if( started ) {
+            stop();
+        }
+        initialized=false;
+
+        // unregister this component
+        if( oname != null ) {
+            try {
+                if( controller == oname ) {
+                    if (log.isLoggable(Level.FINE)) {
+                        log.log(Level.FINE, "unregistering " + oname);
+                    }
+                }
+            } catch( Throwable t ) {
+                log.log(Level.SEVERE, LogFacade.ERROR_UNREGISTERING, t);
+            }
+        }
+
+        if (parent != null) {
+            parent.removeChild(this);
+        }
+
+        // Stop our child containers, if any
+        Container children[] = findChildren();
+        for(Container aChildren : children) {
+            removeChild(aChildren);
+        }
+
+        // START SJSAS 6330332
+        // Remove LifecycleListeners
+        removeLifecycleListeners();
+        // Release realm
+        setRealm(null);
+        // END SJSAS 6330332                
+    }
+
+    // ------------------------------------------------------- Pipeline Methods
+
+
+    /**
+     * Add a new Valve to the end of the pipeline associated with this
+     * Container.  Prior to adding the Valve, the Valve's
+     * <code>setContainer</code> method must be called, with this Container
+     * as an argument.  The method may throw an
+     * <code>IllegalArgumentException</code> if this Valve chooses not to
+     * be associated with this Container, or <code>IllegalStateException</code>
+     * if it is already associated with a different Container.
+     *
+     * @param valve Valve to be added
+     *
+     * @exception IllegalArgumentException if this Container refused to
+     *  accept the specified Valve
+     * @exception IllegalArgumentException if the specified Valve refuses to be
+     *  associated with this Container
+     * @exception IllegalStateException if the specified Valve is already
+     *  associated with a different Container
+     */
+    public synchronized void addValve(GlassFishValve valve) {
+
+        pipeline.addValve(valve);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent(ADD_VALVE_EVENT, valve);
+        }
+    }
+
+
+    /**
+     * Add Tomcat-style valve.
+     */
+    public synchronized void addValve(Valve valve) {
+
+        pipeline.addValve(valve);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent(ADD_VALVE_EVENT, valve);
+        }
+    }
+
+
+    public ObjectName[] getValveObjectNames() {
+        return ((StandardPipeline)pipeline).getValveObjectNames();
+    }
+    
+    /**
+     * <p>Return the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).
+     */
+    public GlassFishValve getBasic() {
+        return (pipeline.getBasic());
+    }
+
+
+    /**
+     * Return the set of Valves in the pipeline associated with this
+     * Container, including the basic Valve (if any).  If there are no
+     * such Valves, a zero-length array is returned.
+     */
+    public GlassFishValve[] getValves() {
+        return (pipeline.getValves());
+    }
+
+
+    /**
+     * @return true if this pipeline has any non basic valves, false
+     * otherwise
+     */
+    public boolean hasNonBasicValves() {
+        return pipeline.hasNonBasicValves();
+    }
+
+
+    /**
+     * Remove the specified Valve from the pipeline associated with this
+     * Container, if it is found; otherwise, do nothing.
+     *
+     * @param valve Valve to be removed
+     */
+    public synchronized void removeValve(GlassFishValve valve) {
+
+        pipeline.removeValve(valve);
+ 
+        if (notifyContainerListeners) {
+            fireContainerEvent(REMOVE_VALVE_EVENT, valve);
+        }
+    }
+
+
+    /**
+     * <p>Set the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).  Prior to setting the basic Valve,
+     * the Valve's <code>setContainer()</code> will be called, if it
+     * implements <code>Contained</code>, with the owning Container as an
+     * argument.  The method may throw an <code>IllegalArgumentException</code>
+     * if this Valve chooses not to be associated with this Container, or
+     * <code>IllegalStateException</code> if it is already associated with
+     * a different Container.</p>
+     *
+     * @param valve Valve to be distinguished as the basic Valve
+     */
+    public void setBasic(GlassFishValve valve) {
+
+        pipeline.setBasic(valve);
+
+    }
+
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess() {
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Notify all container event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param data Event data
+     */
+    public void fireContainerEvent(String type, Object data) {
+
+        ContainerListener[] list = null;
+
+        synchronized (listeners) {
+            if (listeners.isEmpty()) {
+                return;
+            }
+            list = listenersArray;
+        }
+
+        ContainerEvent event = new ContainerEvent(this, type, data);
+        for (int i = 0; i < list.length; i++) {
+            list[i].containerEvent(event);
+        }
+    }
+
+
+    /**   
+     * Starts the children of this container.
+     */
+    protected void startChildren() {
+
+        Container children[] = findChildren();
+        for (int i = 0; i < children.length; i++) {
+            if (children[i] instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) children[i]).start();
+                } catch (Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.CONTAINER_NOT_STARTED_EXCEPTION), children[i]);
+                    log.log(Level.SEVERE, msg, t);
+                    if (children[i] instanceof Context) {
+                        ((Context) children[i]).setAvailable(false);
+                    } else if (children[i] instanceof Wrapper) {
+                        ((Wrapper) children[i]).setAvailable(Long.MAX_VALUE);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Log the specified message to our current Logger (if any).
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+            message = neutralizeForLog(message);
+//         Logger logger = getLogger();
+//         if (logger != null)
+//             logger.log(logName() + ": " + message);
+//         else
+            log.log(Level.INFO, message);
+    }
+
+
+    /**
+     * Log the specified message and exception to our current Logger
+     * (if any).
+     *
+     * @param message Message to be logged
+     * @param throwable Related exception
+     */
+    protected void log(String message, Throwable throwable) {
+        message = neutralizeForLog(message);
+        org.apache.catalina.Logger logger = getLogger();
+        if (logger != null)
+            logger.log(logName() + ": " + message, throwable);
+        else {
+            log.log(Level.SEVERE, message, throwable);
+        }
+
+    }
+
+
+    /**
+     * Return the abbreviated name of this container for logging messages
+     */
+    protected String logName() {
+
+        String className = this.getClass().getName();
+        int period = className.lastIndexOf(".");
+        if (period >= 0)
+            className = className.substring(period + 1);
+        return (className + "[" + getName() + "]");
+
+    }
+
+    // -------------------- JMX and Registration  --------------------
+    protected String domain;
+    protected ObjectName oname;
+    protected ObjectName controller;
+
+    public ObjectName getJmxName() {
+        synchronized (this) {
+            return oname;
+        }
+    }
+    
+    public String getObjectName() {
+        if (oname != null) {
+            return oname.toString();
+        } else return null;
+    }
+
+    public String getDomain() {
+        if( domain==null ) {
+            Container parent=this;
+            while( parent != null &&
+                    !( parent instanceof StandardEngine) ) {
+                parent=parent.getParent();
+            }
+            if( parent != null ) {
+                // parent will always be an instanceof StandardEngine unless it is null
+                domain=((StandardEngine)parent).getDomain();
+            } 
+        }
+        return domain;
+    }
+
+    public void setDomain(String domain) {
+        this.domain=domain;
+    }
+
+
+    public ObjectName[] getChildren() {
+        synchronized(children) {
+            ObjectName result[]=new ObjectName[children.size()];
+            Iterator<Container> it=children.values().iterator();
+            int i=0;
+            while( it.hasNext() ) {
+                Object next=it.next();
+                if( next instanceof ContainerBase ) {
+                    result[i++]=((ContainerBase)next).getJmxName();
+                }
+            }
+            return result;
+        }
+    }
+
+    public ObjectName createObjectName(String domain, ObjectName parent)
+        throws Exception
+    {
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, neutralizeForLog("Create ObjectName " + domain + " " + parent));
+        return null;
+    }
+
+    public String getContainerSuffix() {
+        Container container=this;
+        Container context=null;
+        Container host=null;
+        Container servlet=null;
+        
+        StringBuilder suffix=new StringBuilder();
+        
+        if( container instanceof StandardHost ) {
+            host=container;
+        } else if( container instanceof StandardContext ) {
+            host=container.getParent();
+            context=container;
+        } else if( container instanceof StandardWrapper ) {
+            context=container.getParent();
+            host=context.getParent();
+            servlet=container;
+        }
+        if( context!=null ) {
+            String path=((StandardContext)context).getEncodedPath();
+            suffix.append(",path=").append((path.equals("")) ? "/" : path);
+        } 
+        if( host!=null ) suffix.append(",host=").append( host.getName() );
+        if (servlet != null) {
+            String containerName = container.getName();
+            suffix.append(",servlet=");
+            suffix.append("".equals(containerName) ? "/" : containerName);
+        }
+        return suffix.toString();
+    }
+
+
+    /**
+     * Start the background thread that will periodically check for
+     * session timeouts.
+     */
+    protected void threadStart() {
+
+        if (thread != null)
+            return;
+        if (backgroundProcessorDelay <= 0)
+            return;
+
+        threadDone = false;
+        String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
+        thread = new Thread(new ContainerBackgroundProcessor(), threadName);
+        thread.setDaemon(true);
+        thread.start();
+
+    }
+
+
+    /**
+     * Stop the background thread that is periodically checking for
+     * session timeouts.
+     */
+    protected void threadStop() {
+
+        if (thread == null)
+            return;
+
+        threadDone = true;
+        thread.interrupt();
+        try {
+            thread.join();
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+
+        thread = null;
+
+    }
+
+
+    // -------------------------------------- ContainerExecuteDelay Inner Class
+
+
+    /**
+     * Private thread class to invoke the backgroundProcess method 
+     * of this container and its children after a fixed delay.
+     */
+    protected class ContainerBackgroundProcessor implements Runnable {
+
+        public void run() {
+            while (!threadDone) {
+                try {
+                    Thread.sleep(backgroundProcessorDelay * 1000L);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+                if (!threadDone) {
+                    Container parent = (Container) getMappingObject();
+                    ClassLoader cl = 
+                        Thread.currentThread().getContextClassLoader();
+                    if (parent.getLoader() != null) {
+                        cl = parent.getLoader().getClassLoader();
+                    }
+                    processChildren(parent, cl);
+                }
+            }
+        }
+
+        protected void processChildren(Container container, ClassLoader cl) {
+            try {
+                if (container.getLoader() != null) {
+                    Thread.currentThread().setContextClassLoader
+                        (container.getLoader().getClassLoader());
+                }
+                container.backgroundProcess();
+            } catch (Throwable t) {
+                log.log(Level.SEVERE, LogFacade.EXCEPTION_INVOKES_PERIODIC_OP, t);
+            } finally {
+                Thread.currentThread().setContextClassLoader(cl);
+            }
+            Container[] children = container.findChildren();
+            for (int i = 0; i < children.length; i++) {
+                if (children[i].getBackgroundProcessorDelay() <= 0) {
+                    processChildren(children[i], cl);
+                }
+            }
+        }
+
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContextsAdapterUtility.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContextsAdapterUtility.java
new file mode 100644
index 0000000..68c87e6
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContextsAdapterUtility.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.core;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+/**
+ * Utility class to adapt:
+ * {@link Context} to {@link org.glassfish.grizzly.http.server.naming.NamingContext} and
+ * {@link DirContext} to {@link org.glassfish.grizzly.http.server.naming.DirContext}.
+ */
+public class ContextsAdapterUtility {
+
+    /**
+     * Wraps {@link Context} and returns corresponding Grizzly
+     * {@link org.glassfish.grizzly.http.server.naming.NamingContext}.
+     *
+     * @param namingContext {@link Context} to wrap.
+     * @return {@link org.glassfish.grizzly.http.server.naming.NamingContext}
+     */
+    public static org.glassfish.grizzly.http.server.naming.NamingContext wrap(
+            final Context namingContext) {
+        if (namingContext == null) {
+            return null;
+        }
+        
+        return new NamingContextAdapter(namingContext);
+    }
+
+    /**
+     * Unwraps Grizzly
+     * {@link org.glassfish.grizzly.http.server.naming.NamingContext} and returns
+     * internal {@link Context}.
+     *
+     * @param grizzlyNamingContext {@link org.glassfish.grizzly.http.server.naming.NamingContext}
+     * @return {@link Context}
+     * @throws IllegalArgumentException if passed Grizzly
+     * {@link final org.glassfish.grizzly.http.server.naming.NamingContext} is
+     * of unknown type (wasn't wrapped by this utility class).
+     */
+    public static Context unwrap(
+            final org.glassfish.grizzly.http.server.naming.NamingContext grizzlyNamingContext) {
+        if (grizzlyNamingContext == null) {
+            return null;
+        }
+        
+        if (!(grizzlyNamingContext instanceof NamingContextAdapter)) {
+            throw new IllegalArgumentException("Unknown NamingContext type: " +
+                    grizzlyNamingContext.getClass().getName());
+        }
+        return ((NamingContextAdapter) grizzlyNamingContext).getJmxNamingContext();
+    }
+    
+    /**
+     * Wraps {@link DirContext} and returns corresponding Grizzly
+     * {@link org.glassfish.grizzly.http.server.naming.DirContext}.
+     *
+     * @param dirContext {@link DirContext} to wrap.
+     * @return {@link org.glassfish.grizzly.http.server.naming.DirContext}
+     */
+    public static org.glassfish.grizzly.http.server.naming.DirContext wrap(
+            final DirContext dirContext) {
+        if (dirContext == null) {
+            return null;
+        }
+        
+        return new DirContextAdapter(dirContext);
+    }
+
+    /**
+     * Unwraps Grizzly
+     * {@link org.glassfish.grizzly.http.server.naming.DirContext} and returns
+     * internal {@link DirContext}.
+     *
+     * @param grizzlyDirContext {@link org.glassfish.grizzly.http.server.naming.DirContext}
+     * @return {@link DirContext}
+     * @throws IllegalArgumentException if passed Grizzly
+     * {@link final org.glassfish.grizzly.http.server.naming.DirContext} is not
+     * of unknown type (wasn't wrapped by this utility class).
+     */
+    public static DirContext unwrap(
+            final org.glassfish.grizzly.http.server.naming.DirContext grizzlyDirContext) {
+        
+        if (grizzlyDirContext == null) {
+            return null;
+        }
+        
+        if (!(grizzlyDirContext instanceof DirContextAdapter)) {
+            throw new IllegalArgumentException("Unknown DirContext type: " +
+                    grizzlyDirContext.getClass().getName());
+        }
+        return ((DirContextAdapter) grizzlyDirContext).getJmxDirContext();
+    }
+
+    private static Object wrapIfNeeded(final Object resource) {
+        if (resource == null) {
+            return null;
+        } else if (resource instanceof DirContext) {
+            return wrap((DirContext) resource);
+        } else if (resource instanceof Context) {
+            return wrap((Context) resource);
+        }
+
+        return resource;
+    }
+    
+    private static class NamingContextAdapter
+            implements org.glassfish.grizzly.http.server.naming.DirContext {
+        private final Context jmxNamingContext;
+
+        private NamingContextAdapter(final Context jmxNamingContext) {
+            this.jmxNamingContext = jmxNamingContext;
+        }
+
+        public Context getJmxNamingContext() {
+            return jmxNamingContext;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Object lookup(final String pathStr)
+                throws org.glassfish.grizzly.http.server.naming.NamingException {
+            try {
+                return wrapIfNeeded(jmxNamingContext.lookup(pathStr));
+            } catch (NamingException e) {
+                throw new org.glassfish.grizzly.http.server.naming.NamingException(e);
+            }
+        }
+    }
+    
+    private static class DirContextAdapter
+            implements org.glassfish.grizzly.http.server.naming.DirContext {
+        private final DirContext jmxDirContext;
+
+        private DirContextAdapter(final DirContext jmxDirContext) {
+            this.jmxDirContext = jmxDirContext;
+        }
+
+        public DirContext getJmxDirContext() {
+            return jmxDirContext;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Object lookup(final String pathStr)
+                throws org.glassfish.grizzly.http.server.naming.NamingException {
+            try {
+                return wrapIfNeeded(jmxDirContext.lookup(pathStr));
+            } catch (NamingException e) {
+                throw new org.glassfish.grizzly.http.server.naming.NamingException(e);
+            }
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/DispatchTargetsInfo.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DispatchTargetsInfo.java
new file mode 100644
index 0000000..bedc597
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DispatchTargetsInfo.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import java.io.Serializable;
+
+/**
+ * This class stores information about the last dispatch target
+ * which is used for AsyncContext#dispatch().
+ *
+ * @author Shing Wai Chan
+ */
+public class DispatchTargetsInfo implements Serializable {
+    private String[] targets = new String[2];
+    private boolean[] named = new boolean[2];
+
+    DispatchTargetsInfo() {
+    }
+
+    void addDispatchTarget(String target, boolean isNamed) {
+        targets[0] = targets[1];
+        targets[1] = target;
+
+        named[0] = named[1];
+        named[1] = isNamed;
+    }
+
+    public String getLastDispatchTarget() {
+        return targets[0];
+    }
+
+    public boolean isLastNamedDispatchTarget() {
+        return named[0];
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/DummyRequest.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DummyRequest.java
new file mode 100644
index 0000000..adc62fa
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DummyRequest.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.Socket;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+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 javax.servlet.http.Part;
+import javax.servlet.http.HttpUpgradeHandler;
+
+import org.apache.catalina.Connector;
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.Response;
+import org.apache.catalina.Session;
+import org.apache.catalina.Wrapper;
+import org.glassfish.grizzly.http.util.DataChunk;
+
+/**
+ * Dummy request object, used for request dispatcher mapping, as well as
+ * JSP precompilation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.5.6.2 $ $Date: 2008/04/17 18:37:07 $
+ */
+
+public class DummyRequest implements HttpRequest, HttpServletRequest {
+
+    protected String queryString;
+    protected String pathInfo;
+    protected String servletPath;
+    protected Wrapper wrapper;
+
+    protected FilterChain filterChain;
+
+    // START CR 6415120
+    /**
+     * Whether or not access to resources in WEB-INF or META-INF needs to be
+     * checked.
+     */
+    protected boolean checkRestrictedResources = true;
+    // END CR 6415120
+
+    // START PWC 4707989
+    private String method;
+    // END PWC 4707989
+
+    private static Enumeration<String> dummyEnum = new Enumeration<String>(){
+        public boolean hasMoreElements(){
+            return false;
+        }
+        public String nextElement(){
+            return null;
+        }
+    };
+
+    public String getContextPath() {
+        return null;
+    }
+
+    public ServletRequest getRequest() {
+        return this;
+    }
+
+    public ServletRequest getRequest(boolean maskDefaultContextMapping) {
+        return getRequest();
+    }
+
+    public String getDecodedRequestURI() {
+        return null;
+    }
+
+    public FilterChain getFilterChain() {
+        return filterChain;
+    }
+
+    public void setFilterChain(FilterChain filterChain) {
+        this.filterChain = filterChain;
+    }
+
+    public String getQueryString() {
+        return queryString;
+    }
+
+    public void setQueryString(String query) {
+        queryString = query;
+    }
+
+    public String getPathInfo() {
+        return pathInfo;
+    }
+
+    public void setPathInfo(String path) {
+        pathInfo = path;
+    }
+
+    public DataChunk getRequestPathMB() {
+        return null;
+    }
+
+    public String getServletPath() {
+        return servletPath;
+    }
+
+    public void setServletPath(String path) {
+        servletPath = path;
+    }
+
+    public Wrapper getWrapper() {
+        return wrapper;
+    }
+
+    public void setWrapper(Wrapper wrapper) {
+        this.wrapper = wrapper;
+    }
+
+    // START PWC 4707989
+    public void setMethod(String method) {
+        this.method = method;
+    }
+
+    public String getMethod() {
+        return method;
+    }
+    // END PWC 4707989
+
+    public String getAuthorization() { return null; }
+    public Connector getConnector() { return null; }
+    public void setConnector(Connector connector) {}
+    public Context getContext() { return null; }
+    public void setContext(Context context) {}
+    public Host getHost() { return null; }
+    public void setHost(Host host) {}
+    public String getInfo() { return null; }
+    public Response getResponse() { return null; }
+    public void setResponse(Response response) {}
+    public Socket getSocket() { return null; }
+    public void setSocket(Socket socket) {}
+    public InputStream getStream() { return null; }
+    public void setStream(InputStream input) {}
+    public void addLocale(Locale locale) {}
+    public ServletInputStream createInputStream() throws IOException {
+        return null;
+    }
+    public void finishRequest() throws IOException {}
+    public Object getNote(String name) { return null; }
+    public Iterator<String> getNoteNames() { return null; }
+    public void removeNote(String name) {}
+    public void setContentType(String type) {}
+    public void setNote(String name, Object value) {}
+    public void setProtocol(String protocol) {}
+    public void setRemoteAddr(String remoteAddr) {}
+    public void setRemoteHost(String remoteHost) {}
+    public void setServerName(String name) {}
+    public void setServerPort(int port) {}
+    public Object getAttribute(String name) { return null; }
+    public Enumeration<String> getAttributeNames() { return null; }
+    public String getCharacterEncoding() { return null; }
+    public int getContentLength() { return -1; }
+    public long getContentLengthLong() { return -1L; }
+    public void setContentLength(int length) {}
+    public String getContentType() { return null; }
+    public ServletInputStream getInputStream() throws IOException {
+        return null;
+    }
+    public Locale getLocale() { return null; }
+    public Enumeration<Locale> getLocales() { return null; }
+    public String getProtocol() { return null; }
+    public BufferedReader getReader() throws IOException { return null; }
+    public String getRealPath(String path) { return null; }
+    public String getRemoteAddr() { return null; }
+    public String getRemoteHost() { return null; }
+    public String getScheme() { return null; }
+    public String getServerName() { return null; }
+    public int getServerPort() { return -1; }
+    public boolean isSecure() { return false; }
+    public void removeAttribute(String name) {}
+    public void setAttribute(String name, Object value) {}
+    public void setCharacterEncoding(String enc)
+        throws UnsupportedEncodingException {}
+    public void addCookie(Cookie cookie) {}
+    public void addHeader(String name, String value) {}
+    public void addParameter(String name, String values[]) {}
+    public void clearCookies() {}
+    public void clearHeaders() {}
+    public void clearLocales() {}
+    public void clearParameters() {}
+    public void replayPayload(byte[] payloadByteArray) {}
+    public void recycle() {}
+    public void setAuthType(String authType) {}
+    /* START PWC 4707989
+    public void setMethod(String method) {}
+    */
+    public void setRequestedSessionCookie(boolean flag) {}
+    public void setRequestedSessionId(String id) {}
+    public void setRequestedSessionURL(boolean flag) {}
+    public void setRequestURI(String uri) {}
+    public void setSecure(boolean secure) {}
+    public void setUserPrincipal(Principal principal) {}
+    public String getParameter(String name) { return null; }
+    public Map<String, String[]> getParameterMap() { return null; }
+    public Enumeration<String> getParameterNames() { return dummyEnum; }
+    public String[] getParameterValues(String name) { return null; }
+    public RequestDispatcher getRequestDispatcher(String path) {
+        return null;
+    }
+    public String getAuthType() { return null; }
+    public Cookie[] getCookies() { return null; }
+    public long getDateHeader(String name) { return -1; }
+    public String getHeader(String name) { return null; }
+    public Enumeration<String> getHeaders(String name) { return null; }
+    public Enumeration<String> getHeaderNames() { return null; }
+    public int getIntHeader(String name) { return -1; }
+    /* START PWC 4707989
+    public String getMethod() { return null; }
+    */
+    public String getPathTranslated() { return null; }
+    public String getRemoteUser() { return null; }
+    public String getRequestedSessionId() { return null; }
+    public String getRequestURI() { return null; }
+    public StringBuffer getRequestURL() { return null; }
+    public HttpSession getSession() { return null; }
+    public HttpSession getSession(boolean create) { return null; }
+    public Session getSessionInternal(boolean create) { return null; }
+    public String changeSessionId() { return null; }
+    public boolean isRequestedSessionIdFromCookie() { return false; }
+    public boolean isRequestedSessionIdFromURL() { return false; }
+    public boolean isRequestedSessionIdFromUrl() { return false; }
+    public boolean isRequestedSessionIdValid() { return false; }
+    public void setRequestedSessionCookiePath(String cookiePath) {}
+    public boolean isUserInRole(String role) { return false; }
+    public Principal getUserPrincipal() { return null; }
+    public String getLocalAddr() { return null; }    
+    public String getLocalName() { return null; }
+    public int getLocalPort() { return -1; }
+    public int getRemotePort() { return -1; }
+    public DispatcherType getDispatcherType() { return null; }
+    public AsyncContext startAsync() throws IllegalStateException { return null; }
+    public AsyncContext startAsync(ServletRequest servletRequest,
+                                   ServletResponse servletResponse)
+            throws IllegalStateException { return null; }
+    public boolean isAsyncStarted() { return false; }
+    public boolean isAsyncSupported() { return false; }
+    public void setAsyncTimeout(long timeout) {}
+    public long getAsyncTimeout() { return -1; }
+    public AsyncContext getAsyncContext() { return null; }
+    public void addAsyncListener(AsyncListener listener) {};
+    public void addAsyncListener(AsyncListener listener,
+                                 ServletRequest servletRequest,
+                                 ServletResponse servletResponse) {}
+    public boolean isSetAsyncTimeoutCalled() { return false; }
+    public void disableAsyncSupport() {}
+    public Collection<Part> getParts() {return null;}
+    public Part getPart(String name) {return null;}
+    public boolean authenticate(HttpServletResponse response)
+        throws IOException, ServletException { return false; }
+    public void login(String username, String password)
+        throws ServletException {}
+    public void logout() throws ServletException {}
+    public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) { return null; }
+
+    // START CR 6415120
+    /**
+     * Set whether or not access to resources under WEB-INF or META-INF
+     * needs to be checked.
+     */
+    public void setCheckRestrictedResources(boolean check) {
+        checkRestrictedResources = check;
+    }
+
+    /**
+     * Return whether or not access to resources under WEB-INF or META-INF
+     * needs to be checked.
+     */
+    public boolean getCheckRestrictedResources() {
+        return checkRestrictedResources;
+    }
+    // END CR 6415120
+
+    // START SJSAS 6346226
+    /**
+     * Gets the jroute id of this request, which may have been 
+     * sent as a separate <code>JROUTE</code> cookie or appended to the
+     * session identifier encoded in the URI (if cookies have been disabled).
+     * 
+     * @return The jroute id of this request, or null if this request does not
+     * carry any jroute id
+     */
+    public String getJrouteId() {
+        return null;
+    }
+    // END SJSAS 6346226
+    
+    /**
+     * This object does not implement a session ID generator. Provide
+     * a dummy implementation so that the default one will be used.
+     */
+    public String generateSessionId() {
+        return null;
+    }
+
+    /**
+     * Gets the servlet context to which this servlet request was last
+     * dispatched.
+     *
+     * @return the servlet context to which this servlet request was last
+     * dispatched
+     */
+    public ServletContext getServletContext() {
+        return null;
+    }
+
+    public Session lockSession() {
+        return null;
+    }
+
+    public void unlockSession() {}
+
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/DummyResponse.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DummyResponse.java
new file mode 100644
index 0000000..b86820f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DummyResponse.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.Connector;
+import org.apache.catalina.Context;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.Request;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * Dummy response object, used for JSP precompilation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:34 $
+ */
+
+public class DummyResponse
+    implements HttpResponse, HttpServletResponse {
+
+    public DummyResponse() {
+    }
+
+
+    public void setAppCommitted(boolean appCommitted) {}
+    public boolean isAppCommitted() { return false; }
+    public Connector getConnector() { return null; }
+    public void setConnector(Connector connector) {}
+    public int getContentCount() { return -1; }
+    public Context getContext() { return null; }
+    public void setContext(Context context) {}
+    public boolean getIncluded() { return false; }
+    public void setIncluded(boolean included) {}
+    public String getInfo() { return null; }
+    public Request getRequest() { return null; }
+    public void setRequest(Request request) {}
+    public ServletResponse getResponse() { return null; }
+    public OutputStream getStream() { return null; }
+    public void setStream(OutputStream stream) {}
+    public void setSuspended(boolean suspended) {}
+    public boolean isSuspended() { return false; }
+    public void setError() {}
+    public boolean isError() { return false; }
+    public ServletOutputStream createOutputStream() throws IOException {
+        return null;
+    }
+    public void finishResponse() throws IOException {}
+    public int getContentLength() { return -1; }
+    public String getContentType() { return null; }
+    public PrintWriter getReporter() { return null; }
+    public void recycle() {}
+    public void write(int b) throws IOException {}
+    public void write(byte b[]) throws IOException {}
+    public void write(byte b[], int off, int len) throws IOException {}
+    public void flushBuffer() throws IOException {}
+    public int getBufferSize() { return -1; }
+    public String getCharacterEncoding() { return null; }
+    public void setCharacterEncoding(String charEncoding) {}
+    public ServletOutputStream getOutputStream() throws IOException {
+        return null;
+    }
+    public Locale getLocale() { return null; }
+    public PrintWriter getWriter() throws IOException { return null; }
+    public boolean isCommitted() { return false; }
+    public void reset() {}
+    public void resetBuffer() {}
+    
+    public void resetBuffer(boolean resetWriterStreamFlags) {}
+    public void setBufferSize(int size) {}
+    public void setContentLength(int length) {}
+    public void setContentLengthLong(long length) {}
+    public void setContentType(String type) {}
+    public void setLocale(Locale locale) {}
+
+    public String getHeader(String name) { return null; }
+    public Collection<String> getHeaderNames() { return null; }
+    public Collection<String> getHeaders(String name) { return null; }
+    public void addSessionCookieInternal(final Cookie cookie) {}
+    public String getMessage() { return null; }
+    public int getStatus() { return -1; }
+    public void reset(int status, String message) {}
+    public void addCookie(Cookie cookie) {}
+    public void addDateHeader(String name, long value) {}
+    public void addHeader(String name, String value) {}
+    public void addIntHeader(String name, int value) {}
+    public boolean containsHeader(String name) { return false; }
+    public String encodeRedirectURL(String url) { return null; }
+    public String encodeRedirectUrl(String url) { return null; }
+    public String encodeURL(String url) { return null; }
+    public String encodeUrl(String url) { return null; }
+    public String encode(String url) { return null; }
+    public void sendAcknowledgement() throws IOException {}
+    public void sendError(int status) throws IOException {}
+    public void sendError(int status, String message) throws IOException {}
+    public void sendRedirect(String location) throws IOException {}
+    public void setDateHeader(String name, long value) {}
+    public void setHeader(String name, String value) {}
+    public void setIntHeader(String name, int value) {}
+    public void setStatus(int status) {}
+    public void setStatus(int status, String message) {}
+    public void setDetailMessage(String msg) {}
+    public String getDetailMessage() { return null; }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/DynamicFilterRegistrationImpl.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DynamicFilterRegistrationImpl.java
new file mode 100644
index 0000000..26d77c5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DynamicFilterRegistrationImpl.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.deploy.FilterDef;
+
+import javax.servlet.FilterRegistration;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+
+
+public class DynamicFilterRegistrationImpl
+    extends FilterRegistrationImpl
+    implements FilterRegistration.Dynamic {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    /**
+     * Constructor
+     */
+    public DynamicFilterRegistrationImpl(FilterDef filterDef,
+            StandardContext ctx) {
+        super(filterDef, ctx);
+    }
+
+    public void setAsyncSupported(boolean isAsyncSupported) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DYNAMIC_FILTER_REGISTRATION_ALREADY_INIT),
+                                              new Object[] {"async-supported", filterDef.getFilterName(),
+                                                            ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        filterDef.setIsAsyncSupported(isAsyncSupported);
+    }
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/DynamicServletRegistrationImpl.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DynamicServletRegistrationImpl.java
new file mode 100644
index 0000000..4462bc8
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/DynamicServletRegistrationImpl.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.core;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletSecurityElement;
+import java.util.Collections;
+import java.util.Set;
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+import java.text.MessageFormat;
+
+import org.apache.catalina.LogFacade;
+
+
+public class DynamicServletRegistrationImpl
+    extends ServletRegistrationImpl
+    implements ServletRegistration.Dynamic {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    /**
+     * Constructor
+     */
+    public DynamicServletRegistrationImpl(StandardWrapper wrapper,
+            StandardContext ctx) {
+        super(wrapper, ctx);
+    }
+
+    public void setLoadOnStartup(int loadOnStartup) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DYNAMIC_SERVLET_REGISTRATION_ALREADY_INIT),
+                                              new Object[] {"load-on-startup", wrapper.getName(), ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        wrapper.setLoadOnStartup(loadOnStartup);
+    }
+
+    public void setAsyncSupported(boolean isAsyncSupported) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DYNAMIC_SERVLET_REGISTRATION_ALREADY_INIT),
+                                              new Object[] {"load-on-startup", wrapper.getName(), ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        wrapper.setIsAsyncSupported(isAsyncSupported);
+    }
+
+    public Set<String> setServletSecurity(ServletSecurityElement constraint) {
+        Set<String> emptySet = Collections.emptySet();
+        return Collections.unmodifiableSet(emptySet);
+    }
+
+    public void setMultipartConfig(MultipartConfigElement mpConfig) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DYNAMIC_SERVLET_REGISTRATION_ALREADY_INIT),
+                    new Object[] {"multipart-config", wrapper.getName(), ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        wrapper.setMultipartLocation(mpConfig.getLocation());
+        wrapper.setMultipartMaxFileSize(mpConfig.getMaxFileSize());
+        wrapper.setMultipartMaxRequestSize(mpConfig.getMaxRequestSize());
+        wrapper.setMultipartFileSizeThreshold(
+            mpConfig.getFileSizeThreshold());
+    }
+
+    public void setRunAsRole(String roleName) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DYNAMIC_SERVLET_REGISTRATION_ALREADY_INIT),
+                    new Object[] {"run-as", wrapper.getName(), ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        wrapper.setRunAs(roleName);
+    }
+
+    protected void setServletClassName(String className) {
+        wrapper.setServletClassName(className);
+    }
+
+    protected void setServletClass(Class <? extends Servlet> clazz) {
+        wrapper.setServletClass(clazz);
+    }
+
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/FilterRegistrationImpl.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/FilterRegistrationImpl.java
new file mode 100644
index 0000000..5efafe3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/FilterRegistrationImpl.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.deploy.FilterDef;
+import org.apache.catalina.deploy.FilterMap;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.FilterRegistration;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.Logger;
+
+public class FilterRegistrationImpl implements FilterRegistration {
+
+    protected FilterDef filterDef;
+    protected StandardContext ctx;
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    /**
+     * Constructor
+     */
+    FilterRegistrationImpl(FilterDef filterDef, StandardContext ctx) {
+        this.filterDef = filterDef;
+        this.ctx = ctx;
+    }
+
+
+    public String getName() {
+        return filterDef.getFilterName();
+    }
+
+
+    public FilterDef getFilterDefinition() {
+        return filterDef;
+    }
+
+
+    public String getClassName() {
+        return filterDef.getFilterClassName();
+    }
+
+
+    public boolean setInitParameter(String name, String value) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FILTER_REGISTRATION_ALREADY_INIT),
+                                              new Object[] {"init parameter", filterDef.getFilterName(),
+                                                            ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        return filterDef.setInitParameter(name, value, false);
+    }
+
+
+    public String getInitParameter(String name) {
+        return filterDef.getInitParameter(name);
+    }
+
+
+    public Set<String> setInitParameters(Map<String, String> initParameters) {
+        return filterDef.setInitParameters(initParameters);
+    }
+
+
+    public Map<String, String> getInitParameters() {
+        return filterDef.getInitParameters();
+    }
+
+
+    public void addMappingForServletNames(
+            EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
+            String... servletNames) {
+
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FILTER_REGISTRATION_ALREADY_INIT),
+                                              new Object[] {"servlet-name mapping", filterDef.getFilterName(),
+                                                            ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if ((servletNames==null) || (servletNames.length==0)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FILTER_REGISTRATION_MAPPING_SERVLET_NAME_EXCEPTION),
+                                              new Object[]  {filterDef.getFilterName(), ctx.getName()});
+            throw new IllegalArgumentException(msg);
+        }
+
+        for (String servletName : servletNames) {
+            FilterMap fmap = new FilterMap();
+            fmap.setFilterName(filterDef.getFilterName());
+            fmap.setServletName(servletName);
+            fmap.setDispatcherTypes(dispatcherTypes);
+
+            ctx.addFilterMap(fmap, isMatchAfter);
+        }
+    }
+
+
+    public Collection<String> getServletNameMappings() {
+        return ctx.getServletNameFilterMappings(getName());
+    }
+
+
+    public void addMappingForUrlPatterns(
+            EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
+            String... urlPatterns) {
+
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FILTER_REGISTRATION_ALREADY_INIT),
+                                              new Object[] {"url-pattern mapping", filterDef.getFilterName(),
+                                                            ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if ((urlPatterns==null) || (urlPatterns.length==0)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FILTER_REGISTRATION_MAPPING_URL_PATTERNS_EXCEPTION),
+                                              new Object[] {filterDef.getFilterName(), ctx.getName()});
+            throw new IllegalArgumentException(msg);
+        }
+
+        for (String urlPattern : urlPatterns) {
+            FilterMap fmap = new FilterMap();
+            fmap.setFilterName(filterDef.getFilterName());
+            fmap.setURLPattern(urlPattern);
+            fmap.setDispatcherTypes(dispatcherTypes);
+
+            ctx.addFilterMap(fmap, isMatchAfter);
+        }
+    }
+
+
+    public Collection<String> getUrlPatternMappings() {
+        return ctx.getUrlPatternFilterMappings(getName());
+    }
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/NamingContextListener.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/NamingContextListener.java
new file mode 100644
index 0000000..224c231
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/NamingContextListener.java
@@ -0,0 +1,1090 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.*;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.deploy.*;
+import org.apache.naming.*;
+
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Helper class used to initialize and populate the JNDI context associated
+ * with each context and server.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.7 $ $Date: 2006/11/06 21:13:38 $
+ */
+public class NamingContextListener
+    implements LifecycleListener, ContainerListener, PropertyChangeListener {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new naming context listener.
+     */
+    public NamingContextListener() {
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "new NamingContextListener");
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Name of the associated naming context.
+     */
+    protected String name = "/";
+
+
+    /**
+     * Associated container.
+     */
+    protected Object container = null;
+
+
+    /**
+     * Debugging level.
+     */
+    protected int debug = 0;
+
+
+    /**
+     * Initialized flag.
+     */
+    protected boolean initialized = false;
+
+
+    /**
+     * Associated naming resources.
+     */
+    protected NamingResources namingResources = null;
+
+
+    /**
+     * Associated JNDI context.
+     */
+    protected NamingContext namingContext = null;
+
+
+    /**
+     * Comp context.
+     */
+    protected javax.naming.Context compCtx = null;
+
+
+    /**
+     * Env context.
+     */
+    protected javax.naming.Context envCtx = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the "debug" property.
+     */
+    public int getDebug() {
+
+        return (this.debug);
+
+    }
+
+
+    /**
+     * Set the "debug" property.
+     *
+     * @param debug The new debug level
+     */
+    public void setDebug(int debug) {
+
+        this.debug = debug;
+
+    }
+
+
+    /**
+     * Return the "name" property.
+     */
+    public String getName() {
+
+        return (this.name);
+
+    }
+
+
+    /**
+     * Set the "name" property.
+     *
+     * @param name The new name
+     */
+    public void setName(String name) {
+
+        this.name = name;
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "setName " + name);
+    }
+
+
+    /**
+     * Return the associated naming context.
+     */
+    public NamingContext getNamingContext() {
+
+        return (this.namingContext);
+
+    }
+
+
+    // ---------------------------------------------- LifecycleListener Methods
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     *
+     * @param event LifecycleEvent that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        container = event.getLifecycle();
+
+        if (container instanceof Context) {
+            namingResources = ((Context) container).getNamingResources();
+        } else if (container instanceof Server) {
+            namingResources = ((Server) container).getGlobalNamingResources();
+        } else {
+            return;
+        }
+
+        if (Lifecycle.START_EVENT.equals(event.getType())) {
+
+            if (initialized)
+                return;
+
+            Hashtable<String, Object> contextEnv = new Hashtable<String, Object>();
+            try {
+                namingContext = new NamingContext(contextEnv, getName());
+            } catch (NamingException e) {
+                // Never happens
+            }
+            ContextAccessController.setSecurityToken(getName(), container);
+            ContextBindings.bindContext(container, namingContext, container);
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Bound " + container);
+            }
+
+            // Setting the context in read/write mode
+            ContextAccessController.setWritable(getName(), container);
+
+            try {
+                createNamingContext();
+            } catch (NamingException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CREATION_NAMING_CONTEXT_FAILED), e);
+                log(msg);
+            }
+			
+            namingResources.addPropertyChangeListener(this);
+
+            // Binding the naming context to the class loader
+            if (container instanceof Context) {
+                // Setting the context in read only mode
+                ContextAccessController.setReadOnly(getName());
+                try {
+                    ContextBindings.bindClassLoader
+                        (container, container, 
+                         ((Container) container).getLoader().getClassLoader());
+                } catch (NamingException e) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+                    log(msg);
+                }
+            }
+
+            if (container instanceof Server) {
+                org.apache.naming.factory.ResourceLinkFactory.setGlobalContext
+                    (namingContext);
+                try {
+                    ContextBindings.bindClassLoader
+                        (container, container, 
+                         this.getClass().getClassLoader());
+                } catch (NamingException e) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+                    log(msg);
+                }
+                if (container instanceof StandardServer) {
+                    ((StandardServer) container).setGlobalNamingContext
+                        (namingContext);
+                }
+            }
+
+            initialized = true;
+
+        } else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
+
+            if (!initialized)
+                return;
+
+            // Setting the context in read/write mode
+            ContextAccessController.setWritable(getName(), container);
+            ContextBindings.unbindContext(container, container);
+
+            if (container instanceof Context) {
+                ContextBindings.unbindClassLoader
+                    (container, container, 
+                     ((Container) container).getLoader().getClassLoader());
+            }
+
+            if (container instanceof Server) {
+                namingResources.removePropertyChangeListener(this);
+                ContextBindings.unbindClassLoader
+                    (container, container, 
+                     this.getClass().getClassLoader());
+            }
+
+            ContextAccessController.unsetSecurityToken(getName(), container);
+
+            namingContext = null;
+            envCtx = null;
+            compCtx = null;
+            initialized = false;
+
+        }
+
+    }
+
+
+    // ---------------------------------------------- ContainerListener Methods
+
+
+    /**
+     * Acknowledge the occurrence of the specified event.
+     * Note: Will never be called when the listener is associated to a Server,
+     * since it is not a Container.
+     *
+     * @param event ContainerEvent that has occurred
+     */
+    public void containerEvent(ContainerEvent event) {
+
+        if (!initialized)
+            return;
+
+        // Setting the context in read/write mode
+        ContextAccessController.setWritable(getName(), container);
+
+        String type = event.getType();
+
+        if (type.equals("addEjb")) {
+
+            String ejbName = (String) event.getData();
+            if (ejbName != null) {
+                ContextEjb ejb = namingResources.findEjb(ejbName);
+                addEjb(ejb);
+            }
+
+        } else if (type.equals("addEnvironment")) {
+
+            String environmentName = (String) event.getData();
+            if (environmentName != null) {
+                ContextEnvironment env = 
+                    namingResources.findEnvironment(environmentName);
+                addEnvironment(env);
+            }
+
+        } else if ((type.equals("addResourceParams")) 
+                   || (type.equals("removeResourceParams"))) {
+
+            String resourceParamsName = (String) event.getData();
+            if (resourceParamsName != null) {
+                ContextEjb ejb = namingResources.findEjb(resourceParamsName);
+                if (ejb != null) {
+                    removeEjb(resourceParamsName);
+                    addEjb(ejb);
+                }
+                ContextResource resource = 
+                    namingResources.findResource(resourceParamsName);
+                if (resource != null) {
+                    removeResource(resourceParamsName);
+                    addResource(resource);
+                }
+                String resourceEnvRefValue = 
+                    namingResources.findResourceEnvRef(resourceParamsName);
+                if (resourceEnvRefValue != null) {
+                    removeResourceEnvRef(resourceParamsName);
+                    addResourceEnvRef(resourceParamsName, resourceEnvRefValue);
+                }
+                ContextResourceLink resourceLink = 
+                    namingResources.findResourceLink(resourceParamsName);
+                if (resourceLink != null) {
+                    removeResourceLink(resourceParamsName);
+                    addResourceLink(resourceLink);
+                }
+            }
+
+        } else if (type.equals("addLocalEjb")) {
+
+            String localEjbName = (String) event.getData();
+            if (localEjbName != null) {
+                ContextLocalEjb localEjb = 
+                    namingResources.findLocalEjb(localEjbName);
+                addLocalEjb(localEjb);
+            }
+
+        } else if (type.equals("addResource")) {
+
+            String resourceName = (String) event.getData();
+            if (resourceName != null) {
+                ContextResource resource = 
+                    namingResources.findResource(resourceName);
+                addResource(resource);
+            }
+
+        } else if (type.equals("addResourceLink")) {
+
+            String resourceLinkName = (String) event.getData();
+            if (resourceLinkName != null) {
+                ContextResourceLink resourceLink = 
+                    namingResources.findResourceLink(resourceLinkName);
+                addResourceLink(resourceLink);
+            }
+
+        } else if (type.equals("addResourceEnvRef")) {
+
+            String resourceEnvRefName = (String) event.getData();
+            if (resourceEnvRefName != null) {
+                String resourceEnvRefValue = 
+                    namingResources.findResourceEnvRef(resourceEnvRefName);
+                addResourceEnvRef(resourceEnvRefName, resourceEnvRefValue);
+            }
+
+        } else if (type.equals("removeEjb")) {
+
+            String ejbName = (String) event.getData();
+            if (ejbName != null) {
+                removeEjb(ejbName);
+            }
+
+        } else if (type.equals("removeEnvironment")) {
+
+            String environmentName = (String) event.getData();
+            if (environmentName != null) {
+                removeEnvironment(environmentName);
+            }
+
+        } else if (type.equals("removeLocalEjb")) {
+
+            String localEjbName = (String) event.getData();
+            if (localEjbName != null) {
+                removeLocalEjb(localEjbName);
+            }
+
+        } else if (type.equals("removeResource")) {
+
+            String resourceName = (String) event.getData();
+            if (resourceName != null) {
+                removeResource(resourceName);
+            }
+
+        } else if (type.equals("removeResourceLink")) {
+
+            String resourceLinkName = (String) event.getData();
+            if (resourceLinkName != null) {
+                removeResourceLink(resourceLinkName);
+            }
+
+        } else if (type.equals("removeResourceEnvRef")) {
+
+            String resourceEnvRefName = (String) event.getData();
+            if (resourceEnvRefName != null) {
+                removeResourceEnvRef(resourceEnvRefName);
+            }
+
+        }
+
+        // Setting the context in read only mode
+        ContextAccessController.setReadOnly(getName());
+
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Process property change events. 
+     *
+     * @param event The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        if (!initialized)
+            return;
+
+        Object source = event.getSource();
+        if (source == namingResources) {
+
+            // Setting the context in read/write mode
+            ContextAccessController.setWritable(getName(), container);
+
+            processGlobalResourcesChange(event.getPropertyName(),
+                                         event.getOldValue(),
+                                         event.getNewValue());
+
+            // Setting the context in read only mode
+            ContextAccessController.setReadOnly(getName());
+
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Process a property change on the naming resources, by making the
+     * corresponding addition or removal to the associated JNDI context.
+     *
+     * @param name Property name of the change to be processed
+     * @param oldValue The old value (or <code>null</code> if adding)
+     * @param newValue The new value (or <code>null</code> if removing)
+     */
+    private void processGlobalResourcesChange(String name,
+                                              Object oldValue,
+                                              Object newValue) {
+
+        if (name.equals("ejb")) {
+            if (oldValue != null) {
+                ContextEjb ejb = (ContextEjb) oldValue;
+                if (ejb.getName() != null) {
+                    removeEjb(ejb.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextEjb ejb = (ContextEjb) newValue;
+                if (ejb.getName() != null) {
+                    addEjb(ejb);
+                }
+            }
+        } else if (name.equals("environment")) {
+            if (oldValue != null) {
+                ContextEnvironment env = (ContextEnvironment) oldValue;
+                if (env.getName() != null) {
+                    removeEnvironment(env.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextEnvironment env = (ContextEnvironment) newValue;
+                if (env.getName() != null) {
+                    addEnvironment(env);
+                }
+            }
+        } else if (name.equals("localEjb")) {
+            if (oldValue != null) {
+                ContextLocalEjb ejb = (ContextLocalEjb) oldValue;
+                if (ejb.getName() != null) {
+                    removeLocalEjb(ejb.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextLocalEjb ejb = (ContextLocalEjb) newValue;
+                if (ejb.getName() != null) {
+                    addLocalEjb(ejb);
+                }
+            }
+        } else if (name.equals("resource")) {
+            if (oldValue != null) {
+                ContextResource resource = (ContextResource) oldValue;
+                if (resource.getName() != null) {
+                    removeResource(resource.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextResource resource = (ContextResource) newValue;
+                if (resource.getName() != null) {
+                    addResource(resource);
+                }
+            }
+        } else if (name.equals("resourceEnvRef")) {
+            if (oldValue != null) {
+                String update = (String) oldValue;
+                int colon = update.indexOf(':');
+                removeResourceEnvRef(update.substring(0, colon));
+            }
+            if (newValue != null) {
+                String update = (String) newValue;
+                int colon = update.indexOf(':');
+                addResourceEnvRef(update.substring(0, colon),
+                                  update.substring(colon + 1));
+            }
+        } else if (name.equals("resourceLink")) {
+            if (oldValue != null) {
+                ContextResourceLink rl = (ContextResourceLink) oldValue;
+                if (rl.getName() != null) {
+                    removeResourceLink(rl.getName());
+                }
+            }
+            if (newValue != null) {
+                ContextResourceLink rl = (ContextResourceLink) newValue;
+                if (rl.getName() != null) {
+                    addResourceLink(rl);
+                }
+            }
+        } else if (name.equals("resourceParams")) {
+            String resourceParamsName = null;
+            ResourceParams rp = null;
+            if (oldValue != null) {
+                rp = (ResourceParams) oldValue;
+            }
+            if (newValue != null) {
+                rp = (ResourceParams) newValue;
+            }
+            if (rp != null) {
+                resourceParamsName = rp.getName();
+            }
+            if (resourceParamsName != null) {
+                ContextEjb ejb = namingResources.findEjb(resourceParamsName);
+                if (ejb != null) {
+                    removeEjb(resourceParamsName);
+                    addEjb(ejb);
+                }
+                ContextResource resource = 
+                    namingResources.findResource(resourceParamsName);
+                if (resource != null) {
+                    removeResource(resourceParamsName);
+                    addResource(resource);
+                }
+                String resourceEnvRefValue = 
+                    namingResources.findResourceEnvRef(resourceParamsName);
+                if (resourceEnvRefValue != null) {
+                    removeResourceEnvRef(resourceParamsName);
+                    addResourceEnvRef(resourceParamsName, resourceEnvRefValue);
+                }
+                ContextResourceLink resourceLink = 
+                    namingResources.findResourceLink(resourceParamsName);
+                if (resourceLink != null) {
+                    removeResourceLink(resourceParamsName);
+                    addResourceLink(resourceLink);
+                }
+            }
+        }
+
+
+    }
+
+
+    /**
+     * Create and initialize the JNDI naming context.
+     */
+    private void createNamingContext()
+        throws NamingException {
+
+        // Creating the comp subcontext
+        if (container instanceof Server) {
+            compCtx = namingContext;
+            envCtx = namingContext;
+        } else {
+            compCtx = namingContext.createSubcontext("comp");
+            envCtx = compCtx.createSubcontext("env");
+        }
+
+        int i;
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Creating JNDI naming context");
+
+        if (namingResources == null) {
+            namingResources = new NamingResources();
+            namingResources.setContainer(container);
+        }
+
+        // Resource links
+        ContextResourceLink[] resourceLinks = 
+            namingResources.findResourceLinks();
+        for (i = 0; i < resourceLinks.length; i++) {
+            addResourceLink(resourceLinks[i]);
+        }
+
+        // Resources
+        ContextResource[] resources = namingResources.findResources();
+        for (i = 0; i < resources.length; i++) {
+            addResource(resources[i]);
+        }
+
+        // Resources Env
+        String[] resourceEnvRefs = namingResources.findResourceEnvRefs();
+        for (i = 0; i < resourceEnvRefs.length; i++) {
+            String key = resourceEnvRefs[i];
+            String type = namingResources.findResourceEnvRef(key);
+            addResourceEnvRef(key, type);
+        }
+
+        // Environment entries
+        ContextEnvironment[] contextEnvironments = 
+            namingResources.findEnvironments();
+        for (i = 0; i < contextEnvironments.length; i++) {
+            addEnvironment(contextEnvironments[i]);
+        }
+
+        // EJB references
+        ContextEjb[] ejbs = namingResources.findEjbs();
+        for (i = 0; i < ejbs.length; i++) {
+            addEjb(ejbs[i]);
+        }
+
+        // Binding a User Transaction reference
+        if (container instanceof Context) {
+            try {
+                Reference ref = new TransactionRef();
+                compCtx.bind("UserTransaction", ref);
+                addAdditionalParameters
+                    (namingResources, ref, "UserTransaction");
+            } catch (NamingException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+                log(msg);
+            }
+        }
+
+        // Binding the resources directory context
+        if (container instanceof Context) {
+            try {
+                compCtx.bind("Resources", 
+                             ((Container) container).getResources());
+            } catch (NamingException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+                log(msg);
+            }
+        }
+
+    }
+
+
+    /**
+     * Set the specified EJBs in the naming context.
+     */
+    public void addEjb(ContextEjb ejb) {
+
+        // Create a reference to the EJB.
+        Reference ref = new EjbRef
+            (ejb.getType(), ejb.getHome(), ejb.getRemote(), ejb.getLink());
+        // Adding the additional parameters, if any
+        addAdditionalParameters(ejb.getNamingResources(), ref, ejb.getName());
+        try {
+            createSubcontexts(envCtx, ejb.getName());
+            envCtx.bind(ejb.getName(), ref);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified environment entries in the naming context.
+     */
+    public void addEnvironment(ContextEnvironment env) {
+
+        Object value = null;
+        // Instantiating a new instance of the correct object type, and
+        // initializing it.
+        String type = env.getType();
+        try {
+            if (type.equals("java.lang.String")) {
+                value = env.getValue();
+            } else if (type.equals("java.lang.Byte")) {
+                if (env.getValue() == null) {
+                    value = Byte.valueOf((byte) 0);
+                } else {
+                    value = Byte.decode(env.getValue());
+                }
+            } else if (type.equals("java.lang.Short")) {
+                if (env.getValue() == null) {
+                    value = Short.valueOf((short) 0);
+                } else {
+                    value = Short.decode(env.getValue());
+                }
+            } else if (type.equals("java.lang.Integer")) {
+                if (env.getValue() == null) {
+                    value = Integer.valueOf(0);
+                } else {
+                    value = Integer.decode(env.getValue());
+                }
+            } else if (type.equals("java.lang.Long")) {
+                if (env.getValue() == null) {
+                    value = Long.valueOf(0);
+                } else {
+                    value = Long.decode(env.getValue());
+                }
+            } else if (type.equals("java.lang.Boolean")) {
+                value = Boolean.valueOf(env.getValue());
+            } else if (type.equals("java.lang.Double")) {
+                if (env.getValue() == null) {
+                    value = Double.valueOf(0);
+                } else {
+                    value = Double.valueOf(env.getValue());
+                }
+            } else if (type.equals("java.lang.Float")) {
+                if (env.getValue() == null) {
+                    value = Float.valueOf(0);
+                } else {
+                    value = Float.valueOf(env.getValue());
+                }
+            } else if (type.equals("java.lang.Character")) {
+                if (env.getValue() == null) {
+                    value = Character.valueOf((char) 0);
+                } else {
+                    if (env.getValue().length() == 1) {
+                        value = Character.valueOf(env.getValue().charAt(0));
+                    } else {
+                        throw new IllegalArgumentException();
+                    }
+                }
+            } else {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ENV_ENTRY_INVALID_TYPE), env.getName());
+                log(msg);
+            }
+        } catch (NumberFormatException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ENV_ENTRY_INVALID_VALUE), env.getName());
+            log(msg);
+        } catch (IllegalArgumentException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ENV_ENTRY_INVALID_VALUE), env.getName());
+            log(msg);
+        }
+
+        // Binding the object to the appropriate name
+        if (value != null) {
+            try {
+                if (debug >= 2)
+                    log("  Adding environment entry " + env.getName());
+                createSubcontexts(envCtx, env.getName());
+                envCtx.bind(env.getName(), value);
+            } catch (NamingException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ENV_ENTRY_INVALID_VALUE), env.getName());
+                log(msg);
+            }
+        }
+
+    }
+
+
+    /**
+     * Set the specified local EJBs in the naming context.
+     */
+    public void addLocalEjb(ContextLocalEjb localEjb) {
+
+
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void addResource(ContextResource resource) {
+
+        // Create a reference to the resource.
+        Reference ref = new ResourceRef
+            (resource.getType(), resource.getDescription(),
+             resource.getScope(), resource.getAuth());
+        // Adding the additional parameters, if any
+        addAdditionalParameters(resource.getNamingResources(), ref, 
+                                resource.getName());
+        try {
+            if (debug >= 2) {
+                log("  Adding resource ref " + resource.getName());
+                log("  " + ref);
+            }
+            createSubcontexts(envCtx, resource.getName());
+            envCtx.bind(resource.getName(), ref);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void addResourceEnvRef(String name, String type) {
+
+        // Create a reference to the resource env.
+        Reference ref = new ResourceEnvRef(type);
+        // Adding the additional parameters, if any
+        addAdditionalParameters(null, ref, name);
+        try {
+            if (debug >= 2)
+                log("  Adding resource env ref " + name);
+            createSubcontexts(envCtx, name);
+            envCtx.bind(name, ref);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified resource link in the naming context.
+     */
+    public void addResourceLink(ContextResourceLink resourceLink) {
+
+        // Create a reference to the resource.
+        Reference ref = new ResourceLinkRef
+            (resourceLink.getType(), resourceLink.getGlobal());
+        // Adding the additional parameters, if any
+        addAdditionalParameters(resourceLink.getNamingResources(), ref, 
+                                resourceLink.getName());
+        try {
+            if (debug >= 2)
+                log("  Adding resource link " + resourceLink.getName());
+            createSubcontexts(envCtx, resourceLink.getName());
+            envCtx.bind(resourceLink.getName(), ref);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified EJBs in the naming context.
+     */
+    public void removeEjb(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified environment entries in the naming context.
+     */
+    public void removeEnvironment(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified local EJBs in the naming context.
+     */
+    public void removeLocalEjb(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void removeResource(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void removeResourceEnvRef(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Set the specified resources in the naming context.
+     */
+    public void removeResourceLink(String name) {
+
+        try {
+            envCtx.unbind(name);
+        } catch (NamingException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.BIND_OBJECT_FAILED), e);
+            log(msg);
+        }
+
+    }
+
+
+    /**
+     * Create all intermediate subcontexts.
+     */
+    private void createSubcontexts(javax.naming.Context ctx, String name)
+        throws NamingException {
+        javax.naming.Context currentContext = ctx;
+        StringTokenizer tokenizer = new StringTokenizer(name, "/");
+        while (tokenizer.hasMoreTokens()) {
+            String token = tokenizer.nextToken();
+            if ((!token.equals("")) && (tokenizer.hasMoreTokens())) {
+                try {
+                    currentContext = currentContext.createSubcontext(token);
+                } catch (NamingException e) {
+                    // Silent catch. Probably an object is already bound in
+                    // the context.
+                    currentContext =
+                        (javax.naming.Context) currentContext.lookup(token);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Add additional parameters to the reference.
+     */
+    private void addAdditionalParameters(NamingResources resources, 
+                                         Reference ref, String name) {
+        if (resources == null) {
+            resources = namingResources;
+        }
+        ResourceParams resourceParameters = resources.findResourceParams(name);
+        if (debug >= 2)
+            log("  Resource parameters for " + name + " = " +
+                resourceParameters);
+        if (resourceParameters == null)
+            return;
+        Hashtable<String, String> params = resourceParameters.getParameters();
+        Enumeration<String> enumeration = params.keys();
+        while (enumeration.hasMoreElements()) {
+            String paramName = enumeration.nextElement();
+            String paramValue = params.get(paramName);
+            StringRefAddr refAddr = new StringRefAddr(paramName, paramValue);
+            ref.add(refAddr);
+        }
+    }
+
+
+    /**
+     * Log the specified message to our current Logger (if any).
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+        message = neutralizeForLog(message);
+        if (!(container instanceof Container)) {
+            if (log.isLoggable(Level.INFO)) {
+                // Did not localize this message
+                log.log(Level.INFO, logName() + ": " + message);
+            }
+            return;
+        }
+        org.apache.catalina.Logger logger =
+            ((Container) container).getLogger();
+        if (logger != null) {
+            logger.log(logName() + ": " + message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, logName() + ": " + message);
+            }
+        }
+    }
+
+
+    /**
+     * Log the specified message and exception to our current Logger
+     * (if any).
+     *
+     * @param message Message to be logged
+     * @param t Related exception
+     */
+    protected void log(String message, Throwable t) {
+        message = neutralizeForLog(message);
+        if (!(container instanceof Container)) {
+            log.log(Level.WARNING, logName() + ": " + message, t);
+            return;
+        }
+        org.apache.catalina.Logger logger =
+            ((Container) container).getLogger();
+        if (logger != null) {
+            logger.log(logName() + ": " + message, t,
+                org.apache.catalina.Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, logName() + ": " + message, t);
+        }
+    }
+
+
+    /**
+     * Return the abbreviated name of this container for logging messsages
+     */
+    protected String logName() {
+
+        String className = this.getClass().getName();
+        int period = className.lastIndexOf(".");
+        if (period >= 0)
+            className = className.substring(period + 1);
+        return (className + "[" + getName() + "]");
+
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/RequestFacadeHelper.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/RequestFacadeHelper.java
new file mode 100644
index 0000000..ecb95dd
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/RequestFacadeHelper.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Session;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.connector.SessionTracker;
+
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+import javax.servlet.ServletRequest;
+
+/**
+ * This class exposes some of the functionality of
+ * org.apache.catalina.connector.Request and
+ * org.apache.catalina.connector.Response.
+ *
+ * It is in this package for purpose of package visibility
+ * of methods.
+ *
+ * @author Shing Wai Chan
+ */
+public class RequestFacadeHelper {
+    //use the same resource properties as in org.apache.catalina.connector.RequestFacade
+
+    private Request request;
+
+    private Response response;
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    public RequestFacadeHelper(Request request) {
+        this.request = request;
+        this.response = (Response)request.getResponse();
+    }
+
+    public static RequestFacadeHelper getInstance(ServletRequest srvRequest) {
+        RequestFacadeHelper reqFacHelper =
+           (RequestFacadeHelper) srvRequest.getAttribute(Globals.REQUEST_FACADE_HELPER);
+        return reqFacHelper;
+    }
+
+    /**
+     * Increment the depth of application dispatch
+     */
+    int incrementDispatchDepth() {
+        validateRequest();
+        return request.incrementDispatchDepth();
+    }
+
+    /**
+     * Decrement the depth of application dispatch
+     */
+    int decrementDispatchDepth() {
+        validateRequest();
+        return request.decrementDispatchDepth();
+    }
+
+    /**
+     * Check if the application dispatching has reached the maximum
+     */
+    boolean isMaxDispatchDepthReached() {
+        validateRequest();
+        return request.isMaxDispatchDepthReached();
+    }
+
+    void track(Session localSession) {
+        validateRequest();
+        SessionTracker sessionTracker = (SessionTracker)
+            request.getNote(Globals.SESSION_TRACKER);
+        if (sessionTracker != null) {
+            sessionTracker.track(localSession);
+        }
+    }
+
+    String getContextPath(boolean maskDefaultContextMapping) {
+        validateRequest();
+        return request.getContextPath(maskDefaultContextMapping);
+    }
+
+    void disableAsyncSupport() {
+        validateRequest();
+        request.disableAsyncSupport();
+    }
+
+    // --- for Response ---
+
+    // START SJSAS 6374990
+    boolean isResponseError() {
+        validateResponse();
+        return response.isError();
+    }
+
+    String getResponseMessage() {
+        validateResponse();
+        return response.getMessage();
+    }
+
+    int getResponseContentCount() {
+        validateResponse();
+        return response.getContentCount();
+    }
+    // END SJSAS 6374990
+
+    void resetResponse() {
+        validateResponse();
+        response.setSuspended(false);
+        response.setAppCommitted(false);
+    }
+
+
+    public void clear() {
+        request = null;
+        response = null;
+    }
+
+    private void validateRequest() {
+        if (request == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.VALIDATE_REQUEST_EXCEPTION));
+        }
+    }
+
+    private void validateResponse() {
+        if (response == null) {
+            throw new IllegalStateException(rb.getString(LogFacade.VALIDATE_RESPONSE_EXCEPTION));
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ServletRegistrationImpl.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ServletRegistrationImpl.java
new file mode 100644
index 0000000..9a39bc6
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ServletRegistrationImpl.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.core;
+
+import javax.servlet.ServletRegistration;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+
+import org.apache.catalina.LogFacade;
+
+public class ServletRegistrationImpl implements ServletRegistration {
+
+
+    protected StandardWrapper wrapper;
+    protected StandardContext ctx;
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    /**
+     * Constructor
+     */
+    public ServletRegistrationImpl(StandardWrapper wrapper,
+                                      StandardContext ctx) {
+        this.wrapper = wrapper;
+        this.ctx = ctx;
+    }
+
+    public String getName() {
+        return wrapper.getName();
+    }
+
+    public StandardContext getContext() {
+        return ctx;
+    }
+
+    public StandardWrapper getWrapper() {
+        return wrapper;
+    }
+
+    public String getClassName() {
+        return wrapper.getServletClassName();
+    }
+
+    public String getJspFile() {
+        return wrapper.getJspFile();
+    }
+
+    public boolean setInitParameter(String name, String value) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_REGISTRATION_ALREADY_INIT),
+                                              new Object[] {"init parameter", wrapper.getName(),
+                                                            ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+        return wrapper.setInitParameter(name, value, false);
+    }
+
+    public String getInitParameter(String name) {
+        return wrapper.getInitParameter(name);
+    }
+
+    public Set<String> setInitParameters(Map<String, String> initParameters) {
+        return wrapper.setInitParameters(initParameters);
+    }
+
+    public Map<String, String> getInitParameters() {
+        return wrapper.getInitParameters();
+    }
+
+    public Set<String> addMapping(String... urlPatterns) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_REGISTRATION_ALREADY_INIT),
+                                              new Object[] {"mapping", wrapper.getName(),
+                                                            ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if (urlPatterns == null || urlPatterns.length == 0) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_REGISTRATION_MAPPING_URL_PATTERNS_EXCEPTION),
+                                              new Object[] {wrapper.getName(), ctx.getName()});
+            throw new IllegalArgumentException(msg);
+        }
+
+        return ctx.addServletMapping(wrapper.getName(), urlPatterns);
+    }
+
+    public Collection<String> getMappings() {
+        return wrapper.getMappings();
+    }
+
+    public String getRunAsRole() {
+        return wrapper.getRunAs();
+    }
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/SessionCookieConfigImpl.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/SessionCookieConfigImpl.java
new file mode 100644
index 0000000..713d759
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/SessionCookieConfigImpl.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+import javax.servlet.SessionCookieConfig;
+
+/**
+ * Class that may be used to configure various properties of cookies 
+ * used for session tracking purposes.
+ */
+public class SessionCookieConfigImpl implements SessionCookieConfig {
+
+    private String name;
+    private String domain;
+    private String path;
+    private String comment;
+    private boolean httpOnly = true;
+    private boolean secure;
+    private StandardContext ctx;
+    private int maxAge = -1;
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+
+    /**
+     * Constructor
+     */
+    public SessionCookieConfigImpl(StandardContext ctx) {
+        this.ctx = ctx;
+    }
+
+
+    /**
+     * @param name the cookie name to use
+     *
+     * @throws IllegalStateException if the <tt>ServletContext</tt>
+     * from which this <tt>SessionCookieConfig</tt> was acquired has
+     * already been initialized
+     */
+    public void setName(String name) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SESSION_COOKIE_CONFIG_ALREADY_INIT),
+                                              new Object[] {"name", ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        this.name = name;
+        ctx.setSessionCookieName(name);
+    }
+
+
+    /**
+     * @return the cookie name set via {@link #setName}, or
+     * <tt>JSESSIONID</tt> if {@link #setName} was never called
+     */
+    public String getName() {
+        return name;
+    }
+
+
+    /**
+     * @param domain the cookie domain to use
+     *
+     * @throws IllegalStateException if the <tt>ServletContext</tt>
+     * from which this <tt>SessionCookieConfig</tt> was acquired has
+     * already been initialized
+     */
+    public void setDomain(String domain) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SESSION_COOKIE_CONFIG_ALREADY_INIT),
+                                              new Object[] {"dnmain", ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        this.domain = domain;
+    }
+
+
+    /**
+     * @return the cookie domain set via {@link #setDomain}, or
+     * <tt>null</tt> if {@link #setDomain} was never called
+     */
+    public String getDomain() {
+        return domain;
+    }
+
+
+    /**
+     * @param path the cookie path to use
+     *
+     * @throws IllegalStateException if the <tt>ServletContext</tt>
+     * from which this <tt>SessionCookieConfig</tt> was acquired has
+     * already been initialized
+     */
+    public void setPath(String path) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SESSION_COOKIE_CONFIG_ALREADY_INIT),
+                                              new Object[] {"path", ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        this.path = path;
+    }
+
+
+    /**
+     * @return the cookie path set via {@link #setPath}, or the context
+     * path of the <tt>ServletContext</tt> from which this 
+     * <tt>SessionCookieConfig</tt> was acquired if {@link #setPath}
+     * was never called
+     */
+    public String getPath() {
+        return path;
+    }
+
+
+    /**
+     * @param comment the cookie comment to use
+     *
+     * @throws IllegalStateException if the <tt>ServletContext</tt>
+     * from which this <tt>SessionCookieConfig</tt> was acquired has
+     * already been initialized
+     */
+    public void setComment(String comment) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SESSION_COOKIE_CONFIG_ALREADY_INIT),
+                                              new Object[] {"comment", ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        this.comment = comment;
+    }
+
+
+    /**
+     * @return the cookie comment set via {@link #setComment}, or
+     * <tt>null</tt> if {@link #setComment} was never called
+     */
+    public String getComment() {
+        return comment;
+    }
+
+
+    /**
+     * @param httpOnly true if the session tracking cookies created
+     * on behalf of the <tt>ServletContext</tt> from which this
+     * <tt>SessionCookieConfig</tt> was acquired shall be marked as
+     * <i>HttpOnly</i>, false otherwise
+     *
+     * @throws IllegalStateException if the <tt>ServletContext</tt>
+     * from which this <tt>SessionCookieConfig</tt> was acquired has
+     * already been initialized
+     */
+    public void setHttpOnly(boolean httpOnly) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SESSION_COOKIE_CONFIG_ALREADY_INIT),
+                                              new Object[] {"httpOnly", ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        this.httpOnly = httpOnly;
+    }
+
+
+    /**
+     * @return true if the session tracking cookies created on behalf of the
+     * <tt>ServletContext</tt> from which this <tt>SessionCookieConfig</tt>
+     * was acquired will be marked as <i>HttpOnly</i>, false otherwise
+     */
+    public boolean isHttpOnly() {
+        return httpOnly;
+    }
+
+
+    /**
+     * @param secure true if the session tracking cookies created on
+     * behalf of the <tt>ServletContext</tt> from which this
+     * <tt>SessionCookieConfig</tt> was acquired shall be marked as
+     * <i>secure</i> even if the request that initiated the corresponding
+     * session is using plain HTTP instead of HTTPS, and false if they
+     * shall be marked as <i>secure</i> only if the request that initiated
+     * the corresponding session was also secure
+     *
+     * @throws IllegalStateException if the <tt>ServletContext</tt>
+     * from which this <tt>SessionCookieConfig</tt> was acquired has
+     * already been initialized
+     */
+    public void setSecure(boolean secure) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SESSION_COOKIE_CONFIG_ALREADY_INIT),
+                                              new Object[] {"secure", ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        this.secure = secure;
+    }
+
+
+    /**
+     * @return true if the session tracking cookies created on behalf of the
+     * <tt>ServletContext</tt> from which this <tt>SessionCookieConfig</tt>
+     * was acquired will be marked as <i>secure</i> even if the request
+     * that initiated the corresponding session is using plain HTTP
+     * instead of HTTPS, and false if they will be marked as <i>secure</i>
+     * only if the request that initiated the corresponding session was
+     * also secure
+     */
+    public boolean isSecure() {
+        return secure;
+    }
+
+
+    public void setMaxAge(int maxAge) {
+        if (ctx.isContextInitializedCalled()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SESSION_COOKIE_CONFIG_ALREADY_INIT),
+                                              new Object[] {"maxAge", ctx.getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        this.maxAge = maxAge;
+    }
+
+
+    public int getMaxAge() {
+        return maxAge;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardContext.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardContext.java
new file mode 100644
index 0000000..61d0ec3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardContext.java
@@ -0,0 +1,7828 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.glassfish.grizzly.http.server.util.AlternateDocBase;
+import org.glassfish.grizzly.http.server.util.Mapper;
+import org.glassfish.grizzly.http.server.util.MappingData;
+import org.apache.catalina.*;
+import org.apache.catalina.deploy.*;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.session.ManagerBase;
+import org.apache.catalina.session.PersistentManagerBase;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.catalina.startup.ContextConfig;
+import org.apache.catalina.util.*;
+import org.apache.naming.ContextBindings;
+import org.apache.naming.resources.BaseDirContext;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.apache.naming.resources.FileDirContext;
+import org.apache.naming.resources.WebDirContext;
+import org.apache.naming.resources.ProxyDirContext;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.WARDirContext;
+import org.glassfish.hk2.classmodel.reflect.Types;
+import org.glassfish.web.loader.WebappClassLoader;
+import org.glassfish.web.loader.ServletContainerInitializerUtil;
+import org.glassfish.web.valve.GlassFishValve;
+
+import javax.management.*;
+import javax.naming.Binding;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.*;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionIdListener;
+import javax.servlet.http.HttpSessionListener;
+import javax.servlet.http.HttpUpgradeHandler;
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URLDecoder;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import javax.servlet.http.HttpServletMapping;
+import org.apache.catalina.connector.MappingImpl;
+import org.glassfish.grizzly.http.util.CharChunk;
+import org.glassfish.grizzly.http.util.MessageBytes;
+
+/**
+ * Standard implementation of the <b>Context</b> interface.  Each
+ * child container must be a Wrapper implementation to process the
+ * requests directed to a particular servlet.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.48 $ $Date: 2007/07/25 00:52:04 $
+ */
+
+public class StandardContext
+    extends ContainerBase
+    implements Context, ServletContext
+{
+    private static final String DEFAULT_RESPONSE_CHARACTER_ENCODING = "ISO-8859-1";
+
+    // have two similar messages
+    // have two similar messages
+    // have to similar messages
+
+    private static final ClassLoader standardContextClassLoader =
+        StandardContext.class.getClassLoader();
+
+    private static final Set<SessionTrackingMode> DEFAULT_SESSION_TRACKING_MODES =
+        EnumSet.of(SessionTrackingMode.COOKIE);
+
+    /**
+     * Array containing the safe characters set.
+     */
+    protected static final URLEncoder urlEncoder;
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardContext/1.0";
+
+    private static final RuntimePermission GET_CLASSLOADER_PERMISSION =
+        new RuntimePermission("getClassLoader");
+    
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+        urlEncoder = new URLEncoder();
+        urlEncoder.addSafeCharacter('~');
+        urlEncoder.addSafeCharacter('-');
+        urlEncoder.addSafeCharacter('_');
+        urlEncoder.addSafeCharacter('.');
+        urlEncoder.addSafeCharacter('*');
+        urlEncoder.addSafeCharacter('/');
+    }
+
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Create a new StandardContext component with the default basic Valve.
+     */
+    public StandardContext() {
+        pipeline.setBasic(new StandardContextValve());
+        namingResources.setContainer(this);
+        if (Globals.IS_SECURITY_ENABLED) {
+            mySecurityManager = AccessController.doPrivileged(
+                    new PrivilegedCreateSecurityManager());
+        }
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The alternate deployment descriptor name.
+     */
+    private String altDDName = null;
+
+    /**
+     * The antiJARLocking flag for this Context.
+     */
+    private boolean antiJARLocking = false;
+
+    /**
+     * Associated host name.
+     */
+    private String hostName;
+
+    /**
+     * The list of instantiated application event listeners
+     */
+    private List<EventListener> eventListeners =
+        new ArrayList<EventListener>();
+
+    /**
+     * The list of ServletContextListeners
+     */
+    protected ArrayList<ServletContextListener> contextListeners =
+        new ArrayList<ServletContextListener>();
+
+    /**
+     * The list of HttpSessionListeners
+     */
+    private List<HttpSessionListener> sessionListeners =
+        new ArrayList<HttpSessionListener>();
+
+    /**
+     * The set of application parameters defined for this application.
+     */
+    private List<ApplicationParameter> applicationParameters =
+        new ArrayList<ApplicationParameter>();
+
+    /**
+     * The application available flag for this Context.
+     */
+    private boolean available = false;
+
+    /**
+     * The broadcaster that sends j2ee notifications.
+     */
+    private NotificationBroadcasterSupport broadcaster = null;
+
+    /**
+     * The Locale to character set mapper for this application.
+     */
+    private CharsetMapper charsetMapper = null;
+
+    /**
+     * The Java class name of the CharsetMapper class to be created.
+     */
+    private String charsetMapperClass = CharsetMapper.class.getName();
+
+    /**
+     * The request character encoding.
+     */
+    private String requestCharacterEncoding = null;
+
+    /**
+     * The response character encoding.
+     */
+    private String responseCharacterEncoding = null;
+
+    /**
+     * The path to a file to save this Context information.
+     */
+    private String configFile = null;
+
+    /**
+     * The "correctly configured" flag for this Context.
+     */
+    private boolean configured = false;
+
+    /**
+     * The security constraints for this web application.
+     */
+    private List<SecurityConstraint> constraints =
+        new ArrayList<SecurityConstraint>();
+
+    /**
+     * The ServletContext implementation associated with this Context.
+     */
+    // START RIMOD 4894300
+    /*
+    private transient ApplicationContext context = null;
+    */
+    protected ApplicationContext context = null;
+    // END RIMOD 4894300
+
+    /**
+     *  Is the context initialized.
+     */
+    private boolean isContextInitializedCalled = false;
+
+    /**
+     * Compiler classpath to use.
+     */
+    private String compilerClasspath = null;
+
+    /**
+     * Should we attempt to use cookies for session id communication?
+     */
+    private boolean cookies = true;
+
+    /**
+     * true if the rewriting of URLs with the jsessionids of HTTP
+     * sessions belonging to this context is enabled, false otherwise
+     */
+    private boolean enableURLRewriting = true;
+
+    /**
+     * Should we allow the <code>ServletContext.getContext()</code> method
+     * to access the context of other web applications in this server?
+     */
+    private boolean crossContext = false;
+
+    /**
+     * The "follow standard delegation model" flag that will be used to
+     * configure our ClassLoader.
+     */
+    private boolean delegate = false;
+
+    /**
+     * The display name of this web application.
+     */
+    private String displayName = null;
+
+    /**
+     * Override the default web xml location. ContextConfig is not configurable
+     * so the setter is not used.
+     */
+    private String defaultWebXml;
+
+    /**
+     * The distributable flag for this web application.
+     */
+    private boolean distributable = false;
+
+    /**
+     * Thread local data used during request dispatch.
+     */
+    private ThreadLocal<DispatchData> dispatchData =
+        new ThreadLocal<DispatchData>();
+
+    /**
+     * The document root for this web application.
+     */
+    private String docBase = null;
+
+    /**
+     * The exception pages for this web application, keyed by fully qualified
+     * class name of the Java exception.
+     */
+    private Map<String, ErrorPage> exceptionPages =
+        new HashMap<String, ErrorPage>();
+
+    /**
+     * The default error page (error page that was declared
+     * without any exception-type and error-code).
+     */
+    private ErrorPage defaultErrorPage;
+
+    /**
+     * The set of filter configurations (and associated filter instances) we
+     * have initialized, keyed by filter name.
+     */
+    private Map<String, FilterConfig> filterConfigs =
+        new HashMap<String, FilterConfig>();
+
+    /**
+     * The set of filter definitions for this application, keyed by
+     * filter name.
+     */
+    private Map<String, FilterDef> filterDefs = new HashMap<String, FilterDef>();
+
+    /**
+     * The list of filter mappings for this application, in the order
+     * they were defined in the deployment descriptor.
+     */
+    private List<FilterMap> filterMaps = new ArrayList<FilterMap>();
+
+    /**
+     * The list of classnames of InstanceListeners that will be added
+     * to each newly created Wrapper by <code>createWrapper()</code>.
+     */
+    private ArrayList<String> instanceListeners = new ArrayList<String>();
+
+    /**
+     * The set of already instantiated InstanceListeners that will be added
+     * to each newly created Wrapper by <code>createWrapper()</code>.
+     */
+    private List<InstanceListener> instanceListenerInstances = new ArrayList<InstanceListener>();
+
+    /**
+     * The login configuration descriptor for this web application.
+     */
+    private LoginConfig loginConfig = null;
+
+    /**
+     * The mapper associated with this context.
+     */
+    private Mapper mapper = new Mapper();
+
+    /**
+     * The naming context listener for this web application.
+     */
+    private NamingContextListener namingContextListener = null;
+
+    /**
+     * The naming resources for this web application.
+     */
+    private NamingResources namingResources = new NamingResources();
+
+    /**
+     * The message destinations for this web application.
+     */
+    private Map<String, MessageDestination> messageDestinations = new HashMap<String, MessageDestination>();
+
+    /**
+     * The MIME mappings for this web application, keyed by extension.
+     */
+    private Map<String,String> mimeMappings = new HashMap<String,String>();
+
+    /**
+     * The context initialization parameters for this web application,
+     * keyed by name.
+     */
+    private HashMap<String, String> parameters = new HashMap<String, String>();
+
+    /**
+     * The request processing pause flag (while reloading occurs)
+     */
+    private boolean paused = false;
+
+    /**
+     * The public identifier of the DTD for the web application deployment
+     * descriptor version we are currently parsing.  This is used to support
+     * relaxed validation rules when processing version 2.2 web.xml files.
+     */
+    private String publicId = null;
+
+    /**
+     * The reloadable flag for this web application.
+     */
+    private boolean reloadable = false;
+
+    /**
+     * Unpack WAR property.
+     */
+    private boolean unpackWAR = true;
+
+    /**
+     * The DefaultContext override flag for this web application.
+     */
+    private boolean override = false;
+
+    /**
+     * The original document root for this web application.
+     */
+    private String originalDocBase = null;
+
+    /**
+     * The privileged flag for this web application.
+     */
+    private boolean privileged = false;
+
+    /**
+     * Should the next call to <code>addWelcomeFile()</code> cause replacement
+     * of any existing welcome files?  This will be set before processing the
+     * web application's deployment descriptor, so that application specified
+     * choices <strong>replace</strong>, rather than append to, those defined
+     * in the global descriptor.
+     */
+    private boolean replaceWelcomeFiles = false;
+
+    /**
+     * With proxy caching disabled, setting this flag to true adds
+     * Pragma and Cache-Control headers with "No-cache" as value.
+     * Setting this flag to false does not add any Pragma header,
+     * but sets the Cache-Control header to "private".
+     */
+    private boolean securePagesWithPragma = true;
+
+    /**
+     * The security role mappings for this application, keyed by role
+     * name (as used within the application).
+     */
+    private Map<String, String> roleMappings = new HashMap<String, String>();
+
+    /**
+     * The security roles for this application
+     */
+    private List<String> securityRoles = new ArrayList<String>();
+
+    /**
+     * The servlet mappings for this web application, keyed by
+     * matching pattern.
+     */
+    private final Map<String, String> servletMappings = new HashMap<String, String>();
+
+    /**
+     * The session timeout (in minutes) for this web application.
+     */
+    private int sessionTimeout = 30;
+
+    /**
+     * Has the session timeout (in minutes) for this web application
+     * been over-ridden by web-xml
+     * HERCULES:add
+     */
+    private boolean sessionTimeoutOveridden = false;
+
+    /**
+     * The notification sequence number.
+     */
+    private long sequenceNumber = 0;
+
+    /**
+     * The status code error pages for this web application, keyed by
+     * HTTP status code (as an Integer).
+     */
+    private final Map<Integer, ErrorPage> statusPages =
+        new HashMap<Integer, ErrorPage>();
+
+    /**
+     * Amount of ms that the container will wait for servlets to unload.
+     */
+    private long unloadDelay = 2000;
+
+    /**
+     * The watched resources for this application.
+     */
+    private List<String> watchedResources =
+            Collections.synchronizedList(new ArrayList<String>());
+
+    /**
+     * The welcome files for this application.
+     */
+    private String[] welcomeFiles = new String[0];
+
+    /**
+     * The list of classnames of LifecycleListeners that will be added
+     * to each newly created Wrapper by <code>createWrapper()</code>.
+     */
+    private ArrayList<String> wrapperLifecycles = new ArrayList<String>();
+
+    /**
+     * The list of classnames of ContainerListeners that will be added
+     * to each newly created Wrapper by <code>createWrapper()</code>.
+     */
+    private List<String> wrapperListeners = new ArrayList<String>();
+
+    /**
+     * The pathname to the work directory for this context (relative to
+     * the server's home if not absolute).
+     */
+    private String workDir = null;
+
+    /**
+     * JNDI use flag.
+     */
+    private boolean useNaming = true;
+
+    /**
+     * Filesystem based flag.
+     */
+    private boolean filesystemBased = false;
+
+    /**
+     * Name of the associated naming context.
+     */
+    private String namingContextName = null;
+
+    /**
+     * Frequency of the session expiration, and related manager operations.
+     * Manager operations will be done once for the specified amount of
+     * backgrondProcess calls (ie, the lower the amount, the most often the
+     * checks will occur).
+     */
+    private int managerChecksFrequency = 6;
+
+    /**
+     * Iteration count for background processing.
+     */
+    private int count = 0;
+
+    /**
+     * Caching allowed flag.
+     */
+    private boolean cachingAllowed = true;
+
+    /**
+     * Case sensitivity.
+     */
+    protected boolean caseSensitive = true;
+
+    /**
+     * Allow linking.
+     */
+    protected boolean allowLinking = false;
+
+    /**
+     * Cache max size in KB.
+     */
+    protected int cacheMaxSize = 10240; // 10 MB
+
+    /**
+     * Cache TTL in ms.
+     */
+    protected int cacheTTL = 5000;
+
+    /**
+     * Non proxied resources.
+     */
+    private DirContext webappResources = null;
+
+    /*
+     * Time (in milliseconds) it took to start this context
+     */
+    private long startupTime;
+
+    /*
+     * Time (in milliseconds since January 1, 1970, 00:00:00) when this
+     * context was started
+     */
+    private long startTimeMillis;
+
+    private long tldScanTime;
+
+    // START SJSWS 6324431
+    // Should the filter and security mapping be done
+    // in a case sensitive manner
+    protected boolean caseSensitiveMapping = true;
+    // END SJSWS 6324431
+
+    // START S1AS8PE 4817642
+    /**
+     * The flag that specifies whether to reuse the session id (if any) from
+     * the request for newly created sessions
+     */
+    private boolean reuseSessionID = false;
+    // END S1AS8PE 4817642
+
+    // START RIMOD 4642650
+    /**
+     * The flag that specifies whether this context allows sendRedirect() to
+     * redirect to a relative URL.
+     */
+    private boolean allowRelativeRedirect = false;
+
+    // END RIMOD 4642650
+
+    /** Name of the engine. If null, the domain is used.
+     */
+    private String engineName = null;
+    /* SJSAS 6340499
+    private String j2EEApplication="none";
+     */
+    // START SJSAS 6340499
+    private String j2EEApplication="null";
+    // END SJSAS 6340499
+    private String j2EEServer="none";
+
+    // START IASRI 4823322
+    /**
+     * List of configured Auditors for this context.
+     */
+    private Auditor[] auditors = null;
+    // END IASRI 4823322
+
+    // START RIMOD 4868393
+    /**
+     * used to create unique id for each app instance.
+     */
+    private static AtomicInteger instanceIDCounter = new AtomicInteger(1);
+    // END RIMOD 4868393
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+    private boolean webXmlValidation = false;
+
+    private String jvmRoute;
+
+    /**
+     * Attribute value used to turn on/off XML namespace validation
+     */
+    private boolean webXmlNamespaceAware = false;
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+    private boolean tldValidation = false;
+
+    /**
+     * Attribute value used to turn on/off TLD XML namespace validation
+     */
+    private boolean tldNamespaceAware = false;
+
+    /**
+     * Is the context contains the JSF servlet.
+     */
+    protected boolean isJsfApplication = false;
+
+    // START S1AS8PE 4965017
+    private boolean isReload = false;
+    // END S1AS8PE 4965017
+
+    /**
+     * Alternate doc base resources
+     */
+    private ArrayList<AlternateDocBase> alternateDocBases = null;
+
+    private boolean useMyFaces;
+
+    private Set<SessionTrackingMode> sessionTrackingModes;
+
+    /**
+     * Encoded path.
+     */
+    private String encodedPath = null;
+
+    /**
+     * Session cookie config
+     */
+    private SessionCookieConfig sessionCookieConfig;
+
+    /**
+     * The name of the session tracking cookies created by this context
+     * Cache the name here as the getSessionCookieConfig() is synchronized.
+     */
+    private String sessionCookieName = Globals.SESSION_COOKIE_NAME;
+
+    private boolean sessionCookieNameInitialized = false;
+
+    protected ConcurrentMap<String, ServletRegistrationImpl> servletRegisMap =
+        new ConcurrentHashMap<String, ServletRegistrationImpl>();
+
+    protected ConcurrentMap<String, FilterRegistrationImpl> filterRegisMap =
+        new ConcurrentHashMap<String, FilterRegistrationImpl>();
+
+    /**
+     * The list of ordered libs, which is used as the value of the
+     * ServletContext attribute with name javax.servlet.context.orderedLibs
+     */
+    private List<String> orderedLibs;
+
+    // <jsp-config> related info aggregated from web.xml and web-fragment.xml
+    private JspConfigDescriptor jspConfigDesc;
+
+    // ServletContextListeners may be registered (via
+    // ServletContext#addListener) only within the scope of
+    // ServletContainerInitializer#onStartup
+    private boolean isProgrammaticServletContextListenerRegistrationAllowed = false;
+
+    /*
+     * Security manager responsible for enforcing permission check on
+     * ServletContext#getClassLoader
+     */
+    private MySecurityManager mySecurityManager;
+
+    // Iterable over all ServletContainerInitializers that were discovered
+    private Iterable<ServletContainerInitializer> servletContainerInitializers = null;
+
+    // The major Servlet spec version of the web.xml
+    private int effectiveMajorVersion = 0;
+
+    // The minor Servlet spec version of the web.xml
+    private int effectiveMinorVersion = 0;
+
+    // Created via embedded API
+    private boolean isEmbedded = false;
+
+    protected boolean directoryDeployed = false;
+
+    protected boolean showArchivedRealPathEnabled = true;
+
+    protected int servletReloadCheckSecs = 1;
+
+    // ----------------------------------------------------- Context Properties
+
+    public String getEncodedPath() {
+        return encodedPath;
+    }
+
+    @Override
+    public void setName( String name ) {
+        super.setName( name );
+        encodedPath = urlEncoder.encode(name);
+    }
+
+    /**
+     * Is caching allowed ?
+     */
+    public boolean isCachingAllowed() {
+        return cachingAllowed;
+    }
+
+    /**
+     * Set caching allowed flag.
+     */
+    public void setCachingAllowed(boolean cachingAllowed) {
+        this.cachingAllowed = cachingAllowed;
+    }
+
+    /**
+     * Set case sensitivity.
+     */
+    public void setCaseSensitive(boolean caseSensitive) {
+        this.caseSensitive = caseSensitive;
+    }
+
+    /**
+     * Is case sensitive ?
+     */
+    public boolean isCaseSensitive() {
+        return caseSensitive;
+    }
+
+    // START SJSWS 6324431
+    /**
+     * Set case sensitivity for filter and security constraint mappings.
+     */
+    public void setCaseSensitiveMapping(boolean caseSensitiveMap) {
+        caseSensitiveMapping = caseSensitiveMap;
+    }
+
+    /**
+     * Are filters and security constraints mapped in a case sensitive manner?
+     */
+    public boolean isCaseSensitiveMapping() {
+        return caseSensitiveMapping;
+    }
+    // END SJSWS 6324431
+
+    /**
+     * Set allow linking.
+     */
+    public void setAllowLinking(boolean allowLinking) {
+        this.allowLinking = allowLinking;
+    }
+
+    /**
+     * Is linking allowed.
+     */
+    public boolean isAllowLinking() {
+        return allowLinking;
+    }
+
+    /**
+     * Set cache TTL.
+     */
+    public void setCacheTTL(int cacheTTL) {
+        this.cacheTTL = cacheTTL;
+    }
+
+    /**
+     * Get cache TTL.
+     */
+    public int getCacheTTL() {
+        return cacheTTL;
+    }
+
+    /**
+     * Return the maximum size of the cache in KB.
+     */
+    public int getCacheMaxSize() {
+        return cacheMaxSize;
+    }
+
+    /**
+     * Set the maximum size of the cache in KB.
+     */
+    public void setCacheMaxSize(int cacheMaxSize) {
+        this.cacheMaxSize = cacheMaxSize;
+    }
+
+    /**
+     * Return the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     */
+    public boolean getDelegate() {
+        return 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;
+        support.firePropertyChange("delegate", Boolean.valueOf(oldDelegate),
+                                   Boolean.valueOf(this.delegate));
+    }
+
+    /**
+     * Returns true if the internal naming support is used.
+     */
+    public boolean isUseNaming() {
+        synchronized (this) {
+            return useNaming;
+        }
+    }
+
+    /**
+     * Enables or disables naming.
+     */
+    public void setUseNaming(boolean useNaming) {
+        this.useNaming = useNaming;
+    }
+
+    /**
+     * @return true if the resources associated with this context are
+     * filesystem based, false otherwise
+     */
+    public boolean isFilesystemBased() {
+        return filesystemBased;
+    }
+
+    /**
+     * @return the list of initialized application event listeners
+     * of this application, in the order in which they have been specified
+     * in the deployment descriptor
+     */
+    @Override
+    public List<EventListener> getApplicationEventListeners() {
+        return eventListeners;
+    }
+
+    public List<HttpSessionListener> getSessionListeners() {
+        return sessionListeners;
+    }
+
+    /**
+     * Return the application available flag for this Context.
+     */
+    @Override
+    public boolean getAvailable() {
+        return available;
+    }
+
+    /**
+     * Set the application available flag for this Context.
+     *
+     * @param available The new application available flag
+     */
+    @Override
+    public void setAvailable(boolean available) {
+        boolean oldAvailable = this.available;
+        this.available = available;
+        support.firePropertyChange("available", Boolean.valueOf(oldAvailable),
+            Boolean.valueOf(this.available));
+    }
+
+    /**
+     * Return the antiJARLocking flag for this Context.
+     */
+    public boolean getAntiJARLocking() {
+
+        return (this.antiJARLocking);
+
+    }
+
+    /**
+     * Set the antiJARLocking feature for this Context.
+     *
+     * @param antiJARLocking The new flag value
+     */
+    public void setAntiJARLocking(boolean antiJARLocking) {
+
+        boolean oldAntiJARLocking = this.antiJARLocking;
+        this.antiJARLocking = antiJARLocking;
+        support.firePropertyChange("antiJARLocking",
+                oldAntiJARLocking,
+                this.antiJARLocking);
+
+    }
+
+    /**
+     * Return the Locale to character set mapper for this Context.
+     */
+    @Override
+    public CharsetMapper getCharsetMapper() {
+
+        // Create a mapper the first time it is requested
+        if (this.charsetMapper == null) {
+            try {
+                Class clazz = Class.forName(charsetMapperClass);
+                this.charsetMapper =
+                  (CharsetMapper) clazz.newInstance();
+            } catch (Throwable t) {
+                this.charsetMapper = new CharsetMapper();
+            }
+        }
+
+        return (this.charsetMapper);
+    }
+
+    /**
+     * Set the Locale to character set mapper for this Context.
+     *
+     * @param mapper The new mapper
+     */
+    @Override
+    public void setCharsetMapper(CharsetMapper mapper) {
+        CharsetMapper oldCharsetMapper = this.charsetMapper;
+        this.charsetMapper = mapper;
+        if( mapper != null )
+            this.charsetMapperClass= mapper.getClass().getName();
+        support.firePropertyChange("charsetMapper", oldCharsetMapper,
+                                   this.charsetMapper);
+    }
+
+    @Override
+    public String getRequestCharacterEncoding() {
+        return this.requestCharacterEncoding;
+    }
+
+    @Override
+    public void setRequestCharacterEncoding(String encoding) {
+        this.requestCharacterEncoding = encoding;
+    }
+
+    @Override
+    public String getResponseCharacterEncoding() {
+        return this.responseCharacterEncoding;
+    }
+
+    @Override
+    public void setResponseCharacterEncoding(String encoding) {
+        this.responseCharacterEncoding = encoding;
+    }
+
+    /**
+     * @return the path to a file to save this Context information
+     */
+    @Override
+    public String getConfigFile() {
+        return configFile;
+    }
+
+    /**
+     * Set the path to a file to save this Context information.
+     *
+     * @param configFile The path to a file to save this Context information
+     */
+    @Override
+    public void setConfigFile(String configFile) {
+        this.configFile = configFile;
+    }
+
+    /**
+     * @return the "correctly configured" flag for this Context
+     */
+    @Override
+    public boolean getConfigured() {
+        return configured;
+    }
+
+    /**
+     * Sets the "correctly configured" flag for this Context.  This can be
+     * set to false by startup listeners that detect a fatal configuration
+     * error to avoid the application from being made available.
+     *
+     * @param configured The new correctly configured flag
+     */
+    @Override
+    public void setConfigured(boolean configured) {
+        boolean oldConfigured = this.configured;
+        this.configured = configured;
+        support.firePropertyChange("configured",
+            Boolean.valueOf(oldConfigured), Boolean.valueOf(this.configured));
+    }
+
+    /**
+     * @return the "use cookies for session ids" flag
+     */
+    @Override
+    public boolean getCookies() {
+        return cookies;
+    }
+
+    /**
+     * Set the "use cookies for session ids" flag.
+     *
+     * @param cookies The new flag
+     */
+    @Override
+    public void setCookies(boolean cookies) {
+        boolean oldCookies = this.cookies;
+        this.cookies = cookies;
+        support.firePropertyChange("cookies",
+                                   Boolean.valueOf(oldCookies),
+                                   Boolean.valueOf(this.cookies));
+    }
+
+    /**
+     * Checks whether the rewriting of URLs with the jsessionids of
+     * HTTP sessions belonging to this context is enabled or not.
+     *
+     * @return true if the rewriting of URLs with the jsessionids of HTTP
+     * sessions belonging to this context is enabled, false otherwise
+     */
+    @Override
+    public boolean isEnableURLRewriting() {
+        return enableURLRewriting;
+    }
+
+    /**
+     * Enables or disables the rewriting of URLs with the jsessionids of
+     * HTTP sessions belonging to this context.
+     *
+     * @param enableURLRewriting true if the rewriting of URLs with the
+     * jsessionids of HTTP sessions belonging to this context should be
+     * enabled, false otherwise
+     */
+    @Override
+    public void setEnableURLRewriting(boolean enableURLRewriting) {
+        boolean oldEnableURLRewriting = this.enableURLRewriting;
+        this.enableURLRewriting = enableURLRewriting;
+        support.firePropertyChange("enableURLRewriting",
+                                   Boolean.valueOf(oldEnableURLRewriting),
+                                   Boolean.valueOf(this.enableURLRewriting));
+    }
+
+
+    /**
+     * @return the "allow crossing servlet contexts" flag
+     */
+    @Override
+    public boolean getCrossContext() {
+        return (this.crossContext);
+    }
+
+    /**
+     * Sets the "allow crossing servlet contexts" flag.
+     *
+     * @param crossContext The new cross contexts flag
+     */
+    @Override
+    public void setCrossContext(boolean crossContext) {
+
+        boolean oldCrossContext = this.crossContext;
+        this.crossContext = crossContext;
+        support.firePropertyChange("crossContext",
+                                   Boolean.valueOf(oldCrossContext),
+                                   Boolean.valueOf(this.crossContext));
+    }
+
+    public String getDefaultWebXml() {
+        return defaultWebXml;
+    }
+
+    /** Set the location of the default web xml that will be used.
+     * If not absolute, it'll be made relative to the engine's base dir
+     * ( which defaults to catalina.base system property ).
+     *
+     * XXX  If a file is not found - we can attempt a getResource()
+     *
+     * @param defaultWebXml
+     */
+    public void setDefaultWebXml(String defaultWebXml) {
+        this.defaultWebXml = defaultWebXml;
+    }
+
+    /**
+     * Gets the time (in milliseconds) it took to start this context.
+     *
+     * @return Time (in milliseconds) it took to start this context.
+     */
+    public long getStartupTime() {
+        return startupTime;
+    }
+
+    public void setStartupTime(long startupTime) {
+        this.startupTime = startupTime;
+    }
+
+    public long getTldScanTime() {
+        return tldScanTime;
+    }
+
+    public void setTldScanTime(long tldScanTime) {
+        this.tldScanTime = tldScanTime;
+    }
+
+    /**
+     * Return the display name of this web application.
+     */
+    @Override
+    public String getDisplayName() {
+
+        return (this.displayName);
+
+    }
+
+    /**
+     * Return the alternate Deployment Descriptor name.
+     */
+    @Override
+    public String getAltDDName(){
+        return altDDName;
+    }
+
+    /**
+     * Set an alternate Deployment Descriptor name.
+     */
+    @Override
+    public void setAltDDName(String altDDName) {
+        this.altDDName = altDDName;
+        if (context != null) {
+            context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
+            context.setAttributeReadOnly(Globals.ALT_DD_ATTR);
+        }
+    }
+
+    /**
+     * Return the compiler classpath.
+     */
+    public String getCompilerClasspath(){
+        return compilerClasspath;
+    }
+
+    /**
+     * Set the compiler classpath.
+     */
+    public void setCompilerClasspath(String compilerClasspath) {
+        this.compilerClasspath = compilerClasspath;
+    }
+
+    /**
+     * Set the display name of this web application.
+     *
+     * @param displayName The new display name
+     */
+    @Override
+    public void setDisplayName(String displayName) {
+        String oldDisplayName = this.displayName;
+        this.displayName = displayName;
+        support.firePropertyChange("displayName", oldDisplayName,
+                                   this.displayName);
+    }
+
+    /**
+     * Return the distributable flag for this web application.
+     */
+    @Override
+    public boolean getDistributable() {
+        return distributable;
+    }
+
+    /**
+     * Set the distributable flag for this web application.
+     *
+     * @param distributable The new distributable flag
+     */
+    @Override
+    public void setDistributable(boolean distributable) {
+        boolean oldDistributable = this.distributable;
+        this.distributable = distributable;
+        support.firePropertyChange("distributable",
+                                   Boolean.valueOf(oldDistributable),
+                                   Boolean.valueOf(this.distributable));
+
+        // Bugzilla 32866
+        if(getManager() != null) {
+            if(log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Propagating distributable=" + distributable
+                        + " to manager");
+            }
+            getManager().setDistributable(distributable);
+        }
+    }
+
+    /**
+     * Return the document root for this Context.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     */
+    @Override
+    public String getDocBase() {
+        return docBase;
+    }
+
+    /**
+     * Set the document root for this Context.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     *
+     * @param docBase The new document root
+     */
+    @Override
+    public void setDocBase(String docBase) {
+        synchronized (this) {
+            this.docBase = docBase;
+        }
+    }
+
+    /**
+     * Configures this context's alternate doc base mappings.
+     *
+     * @param urlPattern
+     * @param docBase
+     */
+    public void addAlternateDocBase(String urlPattern, String docBase) {
+
+        if (urlPattern == null || docBase == null) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.MISS_PATH_OR_URL_PATTERN_EXCEPTION));
+        }
+
+        AlternateDocBase alternateDocBase = new AlternateDocBase();
+        alternateDocBase.setUrlPattern(urlPattern);
+        alternateDocBase.setDocBase(docBase);
+        alternateDocBase.setBasePath(getBasePath(docBase));
+
+        if (alternateDocBases == null) {
+            alternateDocBases = new ArrayList<AlternateDocBase>();
+        }
+        alternateDocBases.add(alternateDocBase);
+    }
+
+    /**
+     * Gets this context's configured alternate doc bases.
+     *
+     * @return This context's configured alternate doc bases
+     */
+    public ArrayList<AlternateDocBase> getAlternateDocBases() {
+        return alternateDocBases;
+    }
+
+    /**
+     * Return the frequency of manager checks.
+     */
+    public int getManagerChecksFrequency() {
+        return managerChecksFrequency;
+    }
+
+    /**
+     * Set the manager checks frequency.
+     *
+     * @param managerChecksFrequency the new manager checks frequency
+     */
+    public void setManagerChecksFrequency(int managerChecksFrequency) {
+
+        if (managerChecksFrequency <= 0) {
+            return;
+        }
+
+        int oldManagerChecksFrequency = this.managerChecksFrequency;
+        this.managerChecksFrequency = managerChecksFrequency;
+        support.firePropertyChange("managerChecksFrequency",
+            Integer.valueOf(oldManagerChecksFrequency),
+            Integer.valueOf(this.managerChecksFrequency));
+    }
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    @Override
+    public String getInfo() {
+        return (info);
+    }
+
+    public void setJvmRoute(String jvmRoute) {
+        this.jvmRoute = jvmRoute;
+    }
+
+    public String getJvmRoute() {
+        return jvmRoute;
+    }
+
+    public String getEngineName() {
+        if( engineName != null ) return engineName;
+        return domain;
+    }
+
+    public void setEngineName(String engineName) {
+        this.engineName = engineName;
+    }
+
+    public String getJ2EEApplication() {
+        return j2EEApplication;
+    }
+
+    public void setJ2EEApplication(String j2EEApplication) {
+        this.j2EEApplication = j2EEApplication;
+    }
+
+    public String getJ2EEServer() {
+        return j2EEServer;
+    }
+
+    public void setJ2EEServer(String j2EEServer) {
+        this.j2EEServer = j2EEServer;
+    }
+
+    /**
+     * Return the login configuration descriptor for this web application.
+     */
+    @Override
+    public LoginConfig getLoginConfig() {
+        return (this.loginConfig);
+    }
+
+    /**
+     * Set the login configuration descriptor for this web application.
+     *
+     * @param config The new login configuration
+     */
+    @Override
+    public void setLoginConfig(LoginConfig config) {
+
+        // Validate the incoming property value
+        if (config == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.LOGIN_CONFIG_REQUIRED_EXCEPTION));
+        String loginPage = config.getLoginPage();
+        if ((loginPage != null) && !loginPage.startsWith("/")) {
+            if (isServlet22()) {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, LogFacade.FORM_LOGIN_PAGE_FINE, loginPage);
+                }
+                config.setLoginPage("/" + loginPage);
+            } else {
+                String msg = MessageFormat.format(rb.getString(LogFacade.LOGIN_CONFIG_LOGIN_PAGE_EXCEPTION), loginPage);
+                throw new IllegalArgumentException(msg);
+            }
+        }
+        String errorPage = config.getErrorPage();
+        if ((errorPage != null) && !errorPage.startsWith("/")) {
+            if (isServlet22()) {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, LogFacade.FORM_ERROR_PAGE_FINE, errorPage);
+                }
+                config.setErrorPage("/" + errorPage);
+            } else {
+                String msg = MessageFormat.format(rb.getString(LogFacade.LOGIN_CONFIG_ERROR_PAGE_EXCEPTION), errorPage);
+                throw new IllegalArgumentException(msg);
+            }
+        }
+
+        // Process the property setting change
+        LoginConfig oldLoginConfig = this.loginConfig;
+        this.loginConfig = config;
+        support.firePropertyChange("loginConfig",
+                                   oldLoginConfig, this.loginConfig);
+    }
+
+    /**
+     * Get the mapper associated with the context.
+     */
+    @Override
+    public Mapper getMapper() {
+        return mapper;
+    }
+
+    /**
+     * Sets a new pipeline
+     */
+    public void restrictedSetPipeline(Pipeline pl) {
+        synchronized (this) {
+            pl.setBasic(new StandardContextValve());
+            pipeline = pl;
+            hasCustomPipeline = true;
+        }
+    }
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    @Override
+    public NamingResources getNamingResources() {
+        return namingResources;
+    }
+
+    /**
+     * Set the naming resources for this web application.
+     *
+     * @param namingResources The new naming resources
+     */
+    @Override
+    public void setNamingResources(NamingResources namingResources) {
+
+        // Process the property setting change
+        NamingResources oldNamingResources = this.namingResources;
+        this.namingResources = namingResources;
+        support.firePropertyChange("namingResources",
+                                   oldNamingResources, this.namingResources);
+    }
+
+    /**
+     * Return the context path for this Context.
+     */
+    @Override
+    public String getPath() {
+        return (getName());
+    }
+
+    /**
+     * Set the context path for this Context.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  The context path is used as the "name" of
+     * a Context, because it must be unique.
+     *
+     * @param path The new context path
+     */
+    @Override
+    public void setPath(String path) {
+        // XXX  Use host in name
+        /* GlassFish Issue 2339
+        setName(RequestUtil.URLDecode(path));
+         */
+        // START GlassFish Issue 2339
+        setName(RequestUtil.urlDecode(path, "UTF-8"));
+        // END GlassFish Issue 2339
+    }
+
+    /**
+     * Return the public identifier of the deployment descriptor DTD that is
+     * currently being parsed.
+     */
+    @Override
+    public String getPublicId() {
+        return publicId;
+    }
+
+    /**
+     * Set the public identifier of the deployment descriptor DTD that is
+     * currently being parsed.
+     *
+     * @param publicId The public identifier
+     */
+    @Override
+    public void setPublicId(String publicId) {
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Setting deployment descriptor public ID to '" +
+                    publicId + "'");
+
+        String oldPublicId = this.publicId;
+        this.publicId = publicId;
+        support.firePropertyChange("publicId", oldPublicId, publicId);
+    }
+
+    /**
+     * Return the reloadable flag for this web application.
+     */
+    @Override
+    public boolean getReloadable() {
+        return reloadable;
+    }
+
+    /**
+     * Return the DefaultContext override flag for this web application.
+     */
+    @Override
+    public boolean getOverride() {
+        return override;
+    }
+
+    /**
+     * Gets the original document root for this Context, which can be an
+     * absolute pathname, a relative pathname, or a URL.
+     *
+     * Is only set as deployment has change docRoot!
+     */
+    public String getOriginalDocBase() {
+        return (this.originalDocBase);
+    }
+
+    /**
+     * Set the original document root for this Context, which can be an
+     * absolute pathname, a relative pathname, or a URL.
+     *
+     * @param docBase The original document root
+     */
+    public void setOriginalDocBase(String docBase) {
+        this.originalDocBase = docBase;
+    }
+
+    /**
+     * Return the privileged flag for this web application.
+     */
+    @Override
+    public boolean getPrivileged() {
+        return (this.privileged);
+    }
+
+    /**
+     * Set the privileged flag for this web application.
+     *
+     * @param privileged The new privileged flag
+     */
+    @Override
+    public void setPrivileged(boolean privileged) {
+        boolean oldPrivileged = this.privileged;
+        this.privileged = privileged;
+        support.firePropertyChange("privileged",
+                                   Boolean.valueOf(oldPrivileged),
+                                   Boolean.valueOf(this.privileged));
+    }
+
+    /**
+     * Set the reloadable flag for this web application.
+     *
+     * @param reloadable The new reloadable flag
+     */
+    @Override
+    public void setReloadable(boolean reloadable) {
+        boolean oldReloadable = this.reloadable;
+        this.reloadable = reloadable;
+        support.firePropertyChange("reloadable",
+                                   Boolean.valueOf(oldReloadable),
+                                   Boolean.valueOf(this.reloadable));
+    }
+
+    /**
+     * Set the DefaultContext override flag for this web application.
+     *
+     * @param override The new override flag
+     */
+    @Override
+    public void setOverride(boolean override) {
+        boolean oldOverride = this.override;
+        this.override = override;
+        support.firePropertyChange("override",
+                                   Boolean.valueOf(oldOverride),
+                                   Boolean.valueOf(this.override));
+    }
+
+    // START SJSAS 8.1 5049111
+    /**
+     * Scan the parent when searching for TLD listeners.
+     */
+    @Override
+    public boolean isJsfApplication(){
+        return isJsfApplication;
+    }
+    // END SJSAS 8.1 5049111
+
+
+    // START SJSAS 6253524
+    /**
+     * Indicates whether this web module contains any ad-hoc paths.
+     *
+     * An ad-hoc path is a servlet path that is mapped to a servlet
+     * not declared in the web module's deployment descriptor.
+     *
+     * A web module all of whose mappings are for ad-hoc paths is called an
+     * ad-hoc web module.
+     *
+     * @return true if this web module contains any ad-hoc paths, false
+     * otherwise
+     */
+    @Override
+    public boolean hasAdHocPaths() {
+        return false;
+    }
+
+    /**
+     * Returns the name of the ad-hoc servlet responsible for servicing the
+     * given path.
+     *
+     * @param path The path to service
+     *
+     * @return The name of the ad-hoc servlet responsible for servicing the
+     * given path, or null if the given path is not an ad-hoc path
+     */
+    @Override
+    public String getAdHocServletName(String path) {
+        return null;
+    }
+    // END SJSAS 6253524
+
+    /**
+     * Return the "replace welcome files" property.
+     */
+    public boolean isReplaceWelcomeFiles() {
+        return replaceWelcomeFiles;
+    }
+
+    /**
+     * Set the "replace welcome files" property.
+     *
+     * @param replaceWelcomeFiles The new property value
+     */
+    public void setReplaceWelcomeFiles(boolean replaceWelcomeFiles) {
+
+        boolean oldReplaceWelcomeFiles = this.replaceWelcomeFiles;
+        this.replaceWelcomeFiles = replaceWelcomeFiles;
+        support.firePropertyChange("replaceWelcomeFiles",
+                                   Boolean.valueOf(oldReplaceWelcomeFiles),
+                                   Boolean.valueOf(this.replaceWelcomeFiles));
+    }
+
+    /**
+     * Returns the value of the securePagesWithPragma property.
+     */
+    public boolean isSecurePagesWithPragma() {
+        return securePagesWithPragma;
+    }
+
+    /**
+     * Sets the securePagesWithPragma property of this Context.
+     *
+     * Setting this property to true will result in Pragma and Cache-Control
+     * headers with a value of "No-cache" if proxy caching has been disabled.
+     *
+     * Setting this property to false will not add any Pragma header,
+     * but will set the Cache-Control header to "private".
+     *
+     * @param securePagesWithPragma true if Pragma and Cache-Control headers
+     * are to be set to "No-cache" if proxy caching has been disabled, false
+     * otherwise
+     */
+    @Override
+    public void setSecurePagesWithPragma(boolean securePagesWithPragma) {
+
+        boolean oldSecurePagesWithPragma = this.securePagesWithPragma;
+        this.securePagesWithPragma = securePagesWithPragma;
+        support.firePropertyChange("securePagesWithPragma",
+                                   Boolean.valueOf(oldSecurePagesWithPragma),
+                                   Boolean.valueOf(this.securePagesWithPragma));
+    }
+
+    public void setUseMyFaces(boolean useMyFaces) {
+        this.useMyFaces = useMyFaces;
+    }
+
+    public boolean isUseMyFaces() {
+        return useMyFaces;
+    }
+
+    /**
+     * Return the servlet context for which this Context is a facade.
+     */
+    @Override
+    public ServletContext getServletContext() {
+        if (context == null) {
+            context = new ApplicationContext(this);
+            if (altDDName != null
+                    && context.getAttribute(Globals.ALT_DD_ATTR) == null){
+                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
+                context.setAttributeReadOnly(Globals.ALT_DD_ATTR);
+            }
+        }
+
+        return context.getFacade();
+    }
+
+    /**
+     * Return the default session timeout (in minutes) for this
+     * web application.
+     */
+    @Override
+    public int getSessionTimeout() {
+        return sessionTimeout;
+    }
+
+    /**
+     * Is the session timeout (in minutes) for this
+     * web application over-ridden from the default
+     * HERCULES:add
+     */
+    public boolean isSessionTimeoutOveridden() {
+        return sessionTimeoutOveridden;
+    }
+
+    /**
+     * Set the default session timeout (in minutes) for this
+     * web application.
+     *
+     * @param timeout The new default session timeout
+     */
+    @Override
+    public void setSessionTimeout(int timeout) {
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                    new Object[] {"setSessionTimeout", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        int oldSessionTimeout = this.sessionTimeout;
+
+        /*
+         * SRV.13.4 ("Deployment Descriptor"):
+         * If the timeout is 0 or less, the container ensures the default
+         * behaviour of sessions is never to time out.
+         */
+        this.sessionTimeout = (timeout == 0) ? -1 : timeout;
+        support.firePropertyChange("sessionTimeout",
+                                   Integer.valueOf(oldSessionTimeout),
+                                   Integer.valueOf(this.sessionTimeout));
+        //HERCULES:add
+        sessionTimeoutOveridden = true;
+        //end HERCULES:add
+    }
+
+    /**
+     * Return the value of the unloadDelay flag.
+     */
+    public long getUnloadDelay() {
+
+        return (this.unloadDelay);
+
+    }
+
+    /**
+     * Set the value of the unloadDelay flag, which represents the amount
+     * of ms that the container will wait when unloading servlets.
+     * Setting this to a small value may cause more requests to fail
+     * to complete when stopping a web application.
+     *
+     * @param unloadDelay The new value
+     */
+    public void setUnloadDelay(long unloadDelay) {
+
+        long oldUnloadDelay = this.unloadDelay;
+        this.unloadDelay = unloadDelay;
+        support.firePropertyChange("unloadDelay",
+                                   Long.valueOf(oldUnloadDelay),
+                                   Long.valueOf(this.unloadDelay));
+
+    }
+
+    /**
+     * Unpack WAR flag accessor.
+     */
+    public boolean getUnpackWAR() {
+        return unpackWAR;
+    }
+
+    /**
+     * Unpack WAR flag mutator.
+     */
+    public void setUnpackWAR(boolean unpackWAR) {
+        this.unpackWAR = unpackWAR;
+    }
+
+    /**
+     * Set the resources DirContext object with which this Container is
+     * associated.
+     *
+     * @param resources The newly associated DirContext
+     */
+    @Override
+    public synchronized void setResources(DirContext resources) {
+
+        if (started) {
+            throw new IllegalStateException(rb.getString(LogFacade.RESOURCES_STARTED));
+        }
+
+        DirContext oldResources = this.webappResources;
+        if (oldResources == resources)
+            return;
+
+        if (resources instanceof BaseDirContext) {
+            BaseDirContext baseDirContext = (BaseDirContext)resources;
+            baseDirContext.setCached(isCachingAllowed());
+            baseDirContext.setCacheTTL(getCacheTTL());
+            baseDirContext.setCacheMaxSize(getCacheMaxSize());
+        }
+        if (resources instanceof FileDirContext) {
+            filesystemBased = true;
+            FileDirContext fileDirContext = (FileDirContext)resources;
+            fileDirContext.setCaseSensitive(isCaseSensitive());
+            fileDirContext.setAllowLinking(isAllowLinking());
+        }
+        this.webappResources = resources;
+
+        // The proxied resources will be refreshed on start
+        this.resources = null;
+
+        support.firePropertyChange("resources", oldResources,
+                                   this.webappResources);
+
+    }
+
+    private synchronized void setAlternateResources(
+                            AlternateDocBase alternateDocBase,
+                            DirContext resources) {
+
+        if (started) {
+            throw new IllegalStateException(rb.getString(LogFacade.RESOURCES_STARTED));
+        }
+
+        final DirContext oldResources = ContextsAdapterUtility.unwrap(
+                alternateDocBase.getWebappResources());
+        
+        if (oldResources == resources)
+            return;
+
+        if (resources instanceof BaseDirContext) {
+            ((BaseDirContext) resources).setCached(isCachingAllowed());
+            ((BaseDirContext) resources).setCacheTTL(getCacheTTL());
+            ((BaseDirContext) resources).setCacheMaxSize(getCacheMaxSize());
+        }
+        if (resources instanceof FileDirContext) {
+            filesystemBased = true;
+            ((FileDirContext) resources).setCaseSensitive(isCaseSensitive());
+            ((FileDirContext) resources).setAllowLinking(isAllowLinking());
+        }
+        alternateDocBase.setWebappResources(ContextsAdapterUtility.wrap(resources));
+        // The proxied resources will be refreshed on start
+        alternateDocBase.setResources(null);
+    }
+
+    // START S1AS8PE 4817642
+    /**
+     * Return the "reuse session IDs when creating sessions" flag
+     */
+    @Override
+    public boolean getReuseSessionID() {
+        return reuseSessionID;
+    }
+
+    /**
+     * Set the "reuse session IDs when creating sessions" flag
+     *
+     * @param reuse The new value for the flag
+     */
+    @Override
+    public void setReuseSessionID(boolean reuse) {
+        reuseSessionID = reuse;
+    }
+    // END S1AS8PE 4817642
+
+
+
+    // START RIMOD 4642650
+    /**
+     * Return whether this context allows sendRedirect() to redirect
+     * to a relative URL.
+     *
+     * The default value for this property is 'false'.
+     */
+    @Override
+    public boolean getAllowRelativeRedirect() {
+
+        return allowRelativeRedirect;
+
+    }
+
+
+    /**
+     * Set whether this context allows sendRedirect() to redirect
+     * to a relative URL.
+     *
+     * @param allowRelativeURLs The new value for this property.
+     *                          The default value for this property is
+     *                          'false'.
+     */
+    @Override
+    public void setAllowRelativeRedirect(boolean allowRelativeURLs) {
+
+        allowRelativeRedirect = allowRelativeURLs;
+
+    }
+
+
+    // END RIMOD 4642650
+
+    // START IASRI 4823322
+    /**
+     * Get Auditors associated with this context, if any.
+     *
+     * @return array of Auditor objects, or null
+     *
+     */
+    @Override
+    public Auditor[] getAuditors() {
+        return auditors;
+    }
+
+
+    /**
+     * Set the Auditors associated with this context.
+     *
+     * @param auditor array of Auditor objects
+     *
+     */
+    @Override
+    public void setAuditors(Auditor[] auditor) {
+        this.auditors=auditor;
+    }
+    // END IASRI 4823322
+
+
+    // START S1AS8PE 4965017
+    public void setReload(boolean isReload) {
+        this.isReload = isReload;
+    }
+
+    public boolean isReload() {
+        return isReload;
+    }
+    // END S1AS8PE 4965017
+
+    public void setEmbedded(boolean isEmbedded) {
+        this.isEmbedded = isEmbedded;
+    }
+
+    public boolean isEmbedded() {
+        return isEmbedded;
+    }
+
+    /**
+     * Should we generate directory listings?
+     */
+    protected boolean directoryListing = false;
+
+    /**
+     * Enables or disables directory listings on this <tt>Context</tt>.
+     */
+    public void setDirectoryListing(boolean directoryListing) {
+        this.directoryListing = directoryListing;
+        Wrapper wrapper = (Wrapper) findChild(
+                org.apache.catalina.core.Constants.DEFAULT_SERVLET_NAME);
+        if (wrapper !=null) {
+            Servlet servlet = ((StandardWrapper)wrapper).getServlet();
+            if (servlet instanceof DefaultServlet) {
+                ((DefaultServlet)servlet).setListings(directoryListing);
+            }
+        }
+    }
+
+    /**
+     * Checks whether directory listings are enabled or disabled on this
+     * <tt>Context</tt>.
+     */
+    public boolean isDirectoryListing() {
+        return directoryListing;
+    }
+
+    // ------------------------------------------------------ Public Properties
+
+
+    /**
+     * Return the Locale to character set mapper class for this Context.
+     */
+    public String getCharsetMapperClass() {
+
+        return (this.charsetMapperClass);
+
+    }
+
+
+    /**
+     * Set the Locale to character set mapper class for this Context.
+     *
+     * @param mapper The new mapper class
+     */
+    public void setCharsetMapperClass(String mapper) {
+
+        String oldCharsetMapperClass = this.charsetMapperClass;
+        this.charsetMapperClass = mapper;
+        support.firePropertyChange("charsetMapperClass",
+                                   oldCharsetMapperClass,
+                                   this.charsetMapperClass);
+
+    }
+
+
+    /**
+     * Get the absolute path to the work dir.
+     *
+     * @return the absolute path to the work dir
+     */
+    public String getWorkPath() {
+        if (getWorkDir() == null) {
+            return null;
+        }
+        File workDir = new File(getWorkDir());
+        if (!workDir.isAbsolute()) {
+            File catalinaHome = engineBase();
+            String catalinaHomePath = null;
+            try {
+                catalinaHomePath = catalinaHome.getCanonicalPath();
+                workDir = new File(catalinaHomePath,
+                        getWorkDir());
+            } catch (IOException e) {
+            }
+        }
+              return workDir.getAbsolutePath();
+    }
+
+    /**
+     * Return the work directory for this Context.
+     */
+    public String getWorkDir() {
+
+        return (this.workDir);
+
+    }
+
+
+    /**
+     * Set the work directory for this Context.
+     *
+     * @param workDir The new work directory
+     */
+    public void setWorkDir(String workDir) {
+        synchronized (this) {
+            this.workDir = workDir;
+            if (started) {
+                postWorkDirectory();
+            }
+        }
+    }
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Adds the Listener with the given class name that is declared in the
+     * deployment descriptor to the set of Listeners configured for this
+     * application.
+     *
+     * @param listener the fully qualified class name of the Listener
+     */
+    public void addApplicationListener(String listener) {
+        addListener(listener, false);
+    }
+
+    /**
+     * Add a new application parameter for this application.
+     *
+     * @param parameter The new application parameter
+     */
+    public void addApplicationParameter(ApplicationParameter parameter) {
+        String newName = parameter.getName();
+        Iterator<ApplicationParameter> i =
+            applicationParameters.iterator();
+        while (i.hasNext()) {
+            ApplicationParameter applicationParameter = i.next();
+            if (newName.equals(applicationParameter.getName())) {
+                if (applicationParameter.getOverride()) {
+                    applicationParameter.setValue(parameter.getValue());
+                }
+                return;
+            }
+        }
+
+        applicationParameters.add(parameter);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addApplicationParameter", parameter);
+        }
+    }
+
+    /**
+     * Adds the given child Container to this context.
+     *
+     * @param child the child Container to add
+     *
+     * @exception IllegalArgumentException if the given child Container is
+     * not an instance of Wrapper
+     */
+    @Override
+    public void addChild(Container child) {
+        addChild(child, false, true);
+    }
+
+    /**
+     * Adds the given child (Servlet) to this context.
+     *
+     * @param child the child (Servlet) to add
+     * @param isProgrammatic true if the given child (Servlet) is being
+     * added via one of the programmatic interfaces, and false if it is
+     * declared in the deployment descriptor
+     * @param createRegistration true if a ServletRegistration needs to be
+     * created for the given child, and false if a (preliminary)
+     * ServletRegistration had already been created (which would be the
+     * case if the Servlet had been declared in the deployment descriptor
+     * without any servlet-class, and the servlet-class was later provided
+     * via ServletContext#addServlet)
+     *
+     * @exception IllegalArgumentException if the given child Container is
+     * not an instance of Wrapper
+     */
+    protected void addChild(Container child, boolean isProgrammatic,
+            boolean createRegistration) {
+
+        if (!(child instanceof Wrapper)) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NO_WRAPPER_EXCEPTION));
+        }
+
+        Wrapper wrapper = (Wrapper) child;
+        String wrapperName = child.getName();
+
+        if (createRegistration) {
+            ServletRegistrationImpl regis = null;
+            if (isProgrammatic ||
+                    (null == wrapper.getServletClassName() &&
+                        null == wrapper.getJspFile())) {
+                regis = createDynamicServletRegistrationImpl(
+                    (StandardWrapper) wrapper);
+            } else {
+                regis = createServletRegistrationImpl(
+                    (StandardWrapper) wrapper);
+            }
+            servletRegisMap.put(wrapperName, regis);
+            if (null == wrapper.getServletClassName() &&
+                    null == wrapper.getJspFile()) {
+                /*
+                 * Preliminary registration for Servlet that was declared
+                 * without any servlet-class. Once the registration is
+                 * completed via ServletContext#addServlet, addChild will
+                 * be called again, and 'wrapper' will have been configured
+                 * with a proper class name at that time
+                 */
+                return;
+            }
+        }
+
+        if ("javax.faces.webapp.FacesServlet".equals(
+                wrapper.getServletClassName())) {
+            isJsfApplication = true;
+        }
+
+        // Global JspServlet
+        Wrapper oldJspServlet = null;
+
+        // Allow webapp to override JspServlet inherited from global web.xml.
+        boolean isJspServlet = "jsp".equals(wrapperName);
+        if (isJspServlet) {
+            oldJspServlet = (Wrapper) findChild("jsp");
+            if (oldJspServlet != null) {
+                removeChild(oldJspServlet);
+            }
+        }
+
+        String jspFile = wrapper.getJspFile();
+        if ((jspFile != null) && !jspFile.startsWith("/")) {
+            if (isServlet22()) {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, LogFacade.JSP_FILE_FINE, jspFile);
+                }
+                wrapper.setJspFile("/" + jspFile);
+            } else {
+                String msg = MessageFormat.format(rb.getString(LogFacade.WRAPPER_ERROR_EXCEPTION), jspFile);
+                throw new IllegalArgumentException(msg);
+            }
+        }
+
+        super.addChild(child);
+
+        // START SJSAS 6342808
+        /* SJSWS 6362207
+        if (started) {
+        */
+        // START SJSWS 6362207
+        if (getAvailable()) {
+        // END SJSWS 6362207
+            /*
+             * If this StandardContext has already been started, we need to
+             * register the newly added child with JMX. Any children that were
+             * added before this StandardContext was started have already been
+             * registered with JMX (as part of StandardContext.start()).
+             */
+            if (wrapper instanceof StandardWrapper) {
+                ((StandardWrapper) wrapper).registerJMX( this );
+            }
+        }
+        // END SJSAS 6342808
+
+        if (isJspServlet && oldJspServlet != null) {
+            /*
+             * The webapp-specific JspServlet inherits all the mappings
+             * specified in the global web.xml, and may add additional ones.
+             */
+            String[] jspMappings = oldJspServlet.findMappings();
+            for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
+                addServletMapping(jspMappings[i], wrapperName);
+            }
+        }
+    }
+
+    protected ServletRegistrationImpl createServletRegistrationImpl(
+            StandardWrapper wrapper) {
+        return new ServletRegistrationImpl(wrapper, this);
+    }
+
+    protected ServletRegistrationImpl createDynamicServletRegistrationImpl(
+            StandardWrapper wrapper) {
+        return new DynamicServletRegistrationImpl(wrapper, this);
+    }
+
+    /**
+     * Add a security constraint to the set for this web application.
+     */
+    @Override
+    public void addConstraint(SecurityConstraint constraint) {
+
+        // Validate the proposed constraint
+        SecurityCollection collections[] = constraint.findCollections();
+        for(SecurityCollection collection : collections) {
+            String patterns[] = collection.findPatterns();
+            for(int j = 0; j < patterns.length; j++) {
+                patterns[j] = adjustURLPattern(patterns[j]);
+                if(!validateURLPattern(patterns[j])) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.SECURITY_CONSTRAINT_PATTERN_EXCEPTION),
+                                                      patterns[j]);
+                    throw new IllegalArgumentException(msg);
+                }
+            }
+        }
+
+        // Add this constraint to the set for our web application
+        constraints.add(constraint);
+    }
+
+    /**
+     * Add an EJB resource reference for this web application.
+     *
+     * @param ejb New EJB resource reference
+     */
+    public void addEjb(ContextEjb ejb) {
+        namingResources.addEjb(ejb);
+        if (notifyContainerListeners) {
+           fireContainerEvent("addEjb", ejb.getName());
+        }
+    }
+
+    /**
+     * Add an environment entry for this web application.
+     *
+     * @param environment New environment entry
+     */
+    public void addEnvironment(ContextEnvironment environment) {
+
+        ContextEnvironment env = findEnvironment(environment.getName());
+        if ((env != null) && !env.getOverride())
+            return;
+        namingResources.addEnvironment(environment);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addEnvironment", environment.getName());
+        }
+    }
+
+
+    /**
+     * Add resource parameters for this web application.
+     *
+     * @param resourceParameters New resource parameters
+     */
+    public void addResourceParams(ResourceParams resourceParameters) {
+        namingResources.addResourceParams(resourceParameters);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addResourceParams",
+                               resourceParameters.getName());
+        }
+    }
+
+    /**
+     * Add an error page for the specified error or Java exception.
+     *
+     * @param errorPage The error page definition to be added
+     */
+    @Override
+    public void addErrorPage(ErrorPage errorPage) {
+        // Validate the input parameters
+        if (errorPage == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.ERROR_PAGE_REQUIRED_EXCEPTION));
+        String location = errorPage.getLocation();
+        if ((location != null) && !location.startsWith("/")) {
+            if (isServlet22()) {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, LogFacade.ERROR_PAGE_LOCATION_EXCEPTION);
+                }
+                errorPage.setLocation("/" + location);
+            } else {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_PAGE_LOCATION_EXCEPTION), location);
+                throw new IllegalArgumentException(msg);
+            }
+        }
+
+        // Add the specified error page to our internal collections
+        String exceptionType = errorPage.getExceptionType();
+        if (exceptionType != null) {
+            synchronized (exceptionPages) {
+                exceptionPages.put(exceptionType, errorPage);
+            }
+        } else if (errorPage.getErrorCode() > 0) {
+            synchronized (statusPages) {
+                int errorCode = errorPage.getErrorCode();
+                if ((errorCode >= 400) && (errorCode < 600)) {
+                    statusPages.put(errorCode, errorPage);
+                } else {
+                    log.log(Level.SEVERE, LogFacade.INVALID_ERROR_PAGE_CODE_EXCEPTION, errorCode);
+                }
+            }
+        } else {
+            defaultErrorPage = errorPage;
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addErrorPage", errorPage);
+        }
+    }
+
+    /**
+     * Add a filter definition to this Context.
+     *
+     * @param filterDef The filter definition to be added
+     */
+    public void addFilterDef(FilterDef filterDef) {
+        addFilterDef(filterDef, false, true);
+    }
+
+    public void addFilterDef(FilterDef filterDef, boolean isProgrammatic,
+            boolean createRegistration) {
+        if (createRegistration) {
+            FilterRegistrationImpl regis = null;
+            if (isProgrammatic || null == filterDef.getFilterClassName()) {
+                regis = new DynamicFilterRegistrationImpl(filterDef, this);
+            } else {
+                regis = new FilterRegistrationImpl(filterDef, this);
+            }
+            filterRegisMap.put(filterDef.getFilterName(), regis);
+            if (null == filterDef.getFilterClassName()) {
+                /*
+                 * Preliminary registration for Filter that was declared
+                 * without any filter-class. Once the registration is
+                 * completed via ServletContext#addFilter, addFilterDef will
+                 * be called again, and 'filterDef' will have been configured
+                 * with a proper class name at that time
+                 */
+                return;
+            }
+        }
+
+        synchronized (filterDefs) {
+            filterDefs.put(filterDef.getFilterName(), filterDef);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addFilterDef", filterDef);
+        }
+    }
+
+    /**
+     * Add multiple filter mappings to this Context.
+     *
+     * @param filterMaps The filter mappings to be added
+     *
+     * @exception IllegalArgumentException if the specified filter name
+     *  does not match an existing filter definition, or the filter mapping
+     *  is malformed
+     */
+    public void addFilterMaps(FilterMaps filterMaps) {
+        String[] servletNames = filterMaps.getServletNames();
+        String[] urlPatterns = filterMaps.getURLPatterns();
+        for (String servletName : servletNames) {
+            FilterMap fmap = new FilterMap();
+            fmap.setFilterName(filterMaps.getFilterName());
+            fmap.setServletName(servletName);
+            fmap.setDispatcherTypes(filterMaps.getDispatcherTypes());
+            addFilterMap(fmap);
+        }
+        for (String urlPattern : urlPatterns) {
+            FilterMap fmap = new FilterMap();
+            fmap.setFilterName(filterMaps.getFilterName());
+            fmap.setURLPattern(urlPattern);
+            fmap.setDispatcherTypes(filterMaps.getDispatcherTypes());
+            addFilterMap(fmap);
+        }
+    }
+
+    /**
+     * Add a filter mapping to this Context.
+     *
+     * @param filterMap The filter mapping to be added
+     *
+     * @exception IllegalArgumentException if the specified filter name
+     *  does not match an existing filter definition, or the filter mapping
+     *  is malformed
+     */
+    @Override
+    public void addFilterMap(FilterMap filterMap) {
+        addFilterMap(filterMap, true);
+    }
+
+
+    /**
+     * Add a filter mapping to this Context.
+     *
+     * @param filterMap The filter mapping to be added
+     *
+     * @param isMatchAfter true if the given filter mapping should be matched
+     * against requests after any declared filter mappings of this servlet
+     * context, and false if it is supposed to be matched before any declared
+     * filter mappings of this servlet context
+     *
+     * @exception IllegalArgumentException if the specified filter name
+     *  does not match an existing filter definition, or the filter mapping
+     *  is malformed
+     *
+     */
+    public void addFilterMap(FilterMap filterMap, boolean isMatchAfter) {
+
+        // Validate the proposed filter mapping
+        String filterName = filterMap.getFilterName();
+        String servletName = filterMap.getServletName();
+        String urlPattern = filterMap.getURLPattern();
+        if (null == filterRegisMap.get(filterName)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FILTER_MAPPING_NAME_EXCEPTION), filterName);
+            throw new IllegalArgumentException(msg);
+        }
+        if ((servletName == null) && (urlPattern == null)) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.FILTER_MAPPING_EITHER_EXCEPTION));
+        }
+        if ((servletName != null) && (urlPattern != null)) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.FILTER_MAPPING_EITHER_EXCEPTION));
+        }
+        // Because filter-pattern is new in 2.3, no need to adjust
+        // for 2.2 backwards compatibility
+        if ((urlPattern != null) && !validateURLPattern(urlPattern)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FILTER_MAPPING_INVALID_URL_EXCEPTION), urlPattern);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Add this filter mapping to our registered set
+        if (isMatchAfter) {
+            filterMaps.add(filterMap);
+        } else {
+            filterMaps.add(0, filterMap);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addFilterMap", filterMap);
+        }
+    }
+
+    /**
+     * Gets the current servlet name mappings of the Filter with
+     * the given name.
+     */
+    public Collection<String> getServletNameFilterMappings(String filterName) {
+        HashSet<String> mappings = new HashSet<String>();
+        synchronized (filterMaps) {
+            for (FilterMap fm : filterMaps) {
+                if (filterName.equals(fm.getFilterName()) &&
+                        fm.getServletName() != null) {
+                    mappings.add(fm.getServletName());
+                }
+            }
+        }
+        return mappings;
+    }
+
+    /**
+     * Gets the current URL pattern mappings of the Filter with the given
+     * name.
+     */
+    public Collection<String> getUrlPatternFilterMappings(String filterName) {
+        HashSet<String> mappings = new HashSet<String>();
+        synchronized (filterMaps) {
+            for (FilterMap fm : filterMaps) {
+                if (filterName.equals(fm.getFilterName()) &&
+                        fm.getURLPattern() != null) {
+                    mappings.add(fm.getURLPattern());
+                }
+            }
+        }
+        return mappings;
+    }
+
+    /**
+     * Adds the filter with the given name and class name to this servlet
+     * context.
+     */
+    public FilterRegistration.Dynamic addFilter(
+            String filterName, String className) {
+
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                              new Object[] {"addFilter", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if (filterName == null || filterName.length() == 0) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_EMPTY_FILTER_NAME_EXCEPTION));
+        }
+
+        synchronized (filterDefs) {
+            // Make sure filter name is unique for this context
+            if (findFilterDef(filterName) != null) {
+                return null;
+            }
+
+            DynamicFilterRegistrationImpl regis =
+                (DynamicFilterRegistrationImpl) filterRegisMap.get(
+                    filterName);
+            FilterDef filterDef = null;
+            if (null == regis) {
+                filterDef = new FilterDef();
+            } else {
+                // Complete preliminary filter registration
+                filterDef = regis.getFilterDefinition();
+            }
+
+            filterDef.setFilterName(filterName);
+            filterDef.setFilterClassName(className);
+
+            addFilterDef(filterDef, true, (regis == null));
+            if (null == regis) {
+                regis = (DynamicFilterRegistrationImpl)
+                    filterRegisMap.get(filterName);
+            }
+
+            return regis;
+        }
+    }
+
+    /*
+     * Registers the given filter instance with this ServletContext
+     * under the given <tt>filterName</tt>.
+     */
+    @Override
+    public FilterRegistration.Dynamic addFilter(
+            String filterName, Filter filter) {
+
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                              new Object[] {"addFilter", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if (filterName == null || filterName.length() == 0) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_EMPTY_FILTER_NAME_EXCEPTION));
+        }
+
+        if (filter == null) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_FILTER_INSTANCE_EXCEPTION));
+        }
+
+        /*
+         * Make sure the given Filter instance is unique across all deployed
+         * contexts
+         */
+        Container host = getParent();
+        if (host != null) {
+            for (Container child : host.findChildren()) {
+                if (child == this) {
+                    // Our own context will be checked further down
+                    continue;
+                }
+                if (((StandardContext) child).hasFilter(filter)) {
+                    return null;
+                }
+            }
+        }
+
+        /*
+         * Make sure the given Filter name and instance are unique within
+         * this context
+         */
+        synchronized (filterDefs) {
+            for (Map.Entry<String, FilterDef> e : filterDefs.entrySet()) {
+                if (filterName.equals(e.getKey()) ||
+                        filter == e.getValue().getFilter()) {
+                    return null;
+                }
+            }
+
+            DynamicFilterRegistrationImpl regis =
+                (DynamicFilterRegistrationImpl) filterRegisMap.get(
+                    filterName);
+            FilterDef filterDef = null;
+            if (null == regis) {
+                filterDef = new FilterDef();
+            } else {
+                // Complete preliminary filter registration
+                filterDef = regis.getFilterDefinition();
+            }
+
+            filterDef.setFilterName(filterName);
+            filterDef.setFilter(filter);
+
+            addFilterDef(filterDef, true, (regis == null));
+            if (null == regis) {
+                regis = (DynamicFilterRegistrationImpl)
+                    filterRegisMap.get(filterName);
+            }
+
+            return regis;
+        }
+    }
+
+    /**
+     * Checks whether this context contains the given Filter instance
+     */
+    public boolean hasFilter(Filter filter) {
+        for (Map.Entry<String, FilterDef> e : filterDefs.entrySet()) {
+            if (filter == e.getValue().getFilter()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Adds the filter with the given name and class type to this servlet
+     * context.
+     */
+    @Override
+    public FilterRegistration.Dynamic addFilter(String filterName,
+            Class <? extends Filter> filterClass) {
+
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                             new Object[] {"addFilter", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if (filterName == null || filterName.length() == 0) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_EMPTY_FILTER_NAME_EXCEPTION));
+        }
+
+        synchronized (filterDefs) {
+            if (findFilterDef(filterName) != null) {
+                return null;
+            }
+
+            DynamicFilterRegistrationImpl regis =
+                (DynamicFilterRegistrationImpl) filterRegisMap.get(
+                    filterName);
+            FilterDef filterDef = null;
+            if (null == regis) {
+                filterDef = new FilterDef();
+            } else {
+                // Complete preliminary filter registration
+                filterDef = regis.getFilterDefinition();
+            }
+
+            filterDef.setFilterName(filterName);
+            filterDef.setFilterClass(filterClass);
+
+            addFilterDef(filterDef, true, (regis == null));
+            if (null == regis) {
+                regis = (DynamicFilterRegistrationImpl)
+                    filterRegisMap.get(filterName);
+            }
+
+            return regis;
+        }
+    }
+
+    /**
+     * Instantiates the given Filter class and performs any required
+     * resource injection into the new Filter instance before returning
+     * it.
+     */
+    @Override
+    public <T extends Filter> T createFilter(Class<T> clazz)
+            throws ServletException {
+        try {
+            return createFilterInstance(clazz);
+        } catch (Throwable t) {
+            throw new ServletException("Unable to create Filter from " +
+                                       "class " + clazz.getName(), t);
+        }
+    }
+
+    /**
+     * Gets the FilterRegistration corresponding to the filter with the
+     * given <tt>filterName</tt>.
+     */
+    @Override
+    public FilterRegistration getFilterRegistration(String filterName) {
+        return filterRegisMap.get(filterName);
+    }
+
+    /**
+     * Gets a Map of the FilterRegistration objects corresponding to all
+     * currently registered filters.
+     */
+    @Override
+    public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
+        return Collections.unmodifiableMap(filterRegisMap);
+    }
+
+    /**
+     * Gets the session tracking cookie configuration of this
+     * <tt>ServletContext</tt>.
+     */
+    @Override
+    public synchronized SessionCookieConfig getSessionCookieConfig() {
+        if (sessionCookieConfig == null) {
+            sessionCookieConfig = new SessionCookieConfigImpl(this);
+        }
+        return sessionCookieConfig;
+    }
+
+    /**
+     * Sets the name that will be assigned to any session tracking
+     * cookies created on behalf of this context
+     */
+    void setSessionCookieName(String sessionCookieName) {
+        this.sessionCookieName = sessionCookieName;
+        sessionCookieNameInitialized = true;
+    }
+
+    /**
+     * Gets the name that will be assigned to any session tracking
+     * cookies created on behalf of this context
+     */
+    @Override
+    public String getSessionCookieName() {
+        return sessionCookieName;
+    }
+
+    /**
+     * @return the name that will be assigned to any session tracking
+     * parameter created on behalf of this context
+     */
+    @Override
+    public String getSessionParameterName() {
+        if (sessionCookieNameInitialized) {
+            if (sessionCookieName != null && (!sessionCookieName.isEmpty())) {
+               return sessionCookieName;
+            }
+        }
+
+        return Globals.SESSION_PARAMETER_NAME;
+    }
+
+    /**
+     * Sets the session tracking modes that are to become effective for this
+     * <tt>ServletContext</tt>.
+     */
+    @Override
+    public void setSessionTrackingModes(
+            Set<SessionTrackingMode> sessionTrackingModes) {
+
+        if (sessionTrackingModes.contains(SessionTrackingMode.SSL)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.UNSUPPORTED_TRACKING_MODE_EXCEPTION),
+                                              new Object[] {SessionTrackingMode.SSL, getName()});
+            throw new IllegalArgumentException(msg);
+        }
+
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                              new Object[] {"setSessionTrackingModes", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        this.sessionTrackingModes =
+            Collections.unmodifiableSet(sessionTrackingModes);
+
+        if (sessionTrackingModes.contains(SessionTrackingMode.COOKIE)) {
+            setCookies(true);
+        } else {
+            setCookies(false);
+        }
+
+        if (sessionTrackingModes.contains(SessionTrackingMode.URL)) {
+            setEnableURLRewriting(true);
+        } else {
+            setEnableURLRewriting(false);
+        }
+    }
+
+    /**
+     * Gets the session tracking modes that are supported by default for this
+     * <tt>ServletContext</tt>.
+     *
+     * @return set of the session tracking modes supported by default for
+     * this <tt>ServletContext</tt>
+     */
+    @Override
+    public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+        return EnumSet.copyOf(DEFAULT_SESSION_TRACKING_MODES);
+    }
+
+    /**
+     * Gets the session tracking modes that are in effect for this
+     * <tt>ServletContext</tt>.
+     *
+     * @return set of the session tracking modes in effect for this
+     * <tt>ServletContext</tt>
+     */
+    @Override
+    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+        return (sessionTrackingModes != null ? new HashSet<>(sessionTrackingModes) :
+                getDefaultSessionTrackingModes());
+    }
+
+    /**
+     * Adds the listener with the given class name to this ServletContext.
+     *
+     * @param className the fully qualified class name of the listener
+     */
+    @Override
+    public void addListener(String className) {
+        addListener(className, true);
+    }
+
+
+    /**
+     * Adds the listener with the given class name to this ServletContext.
+     *
+     * @param className the fully qualified class name of the listener
+     * @param isProgrammatic true if the listener is being added
+     * programmatically, and false if it has been declared in the deployment
+     * descriptor
+     */
+    private void addListener(String className, boolean isProgrammatic) {
+        EventListener listener = null;
+        try {
+            listener = loadListener(getClassLoader(), className);
+        } catch(Throwable t) {
+            throw new IllegalArgumentException(t);
+        }
+
+        addListener(listener, isProgrammatic);
+    }
+
+    /**
+     * Adds the given listener instance to this ServletContext.
+     *
+     * @param t the listener to be added
+     */
+    @Override
+    public <T extends EventListener> void addListener(T t) {
+        addListener(t, true);
+    }
+
+    /**
+     * Adds the given listener instance to this ServletContext.
+     *
+     * @param t the listener to be added
+     * @param isProgrammatic true if the listener is being added
+     * programmatically, and false if it has been declared in the deployment
+     * descriptor
+     */
+    private <T extends EventListener> void addListener(T t,
+            boolean isProgrammatic) {
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                              new Object[] {"addListener", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if ((t instanceof ServletContextListener) && isProgrammatic &&
+                !isProgrammaticServletContextListenerRegistrationAllowed) {
+            throw new IllegalArgumentException("Not allowed to register " +
+                "ServletContextListener programmatically");
+        }
+
+        boolean added = false;
+
+        if (t instanceof ServletContextAttributeListener ||
+                t instanceof ServletRequestAttributeListener ||
+                t instanceof ServletRequestListener ||
+                t instanceof HttpSessionAttributeListener ||
+                t instanceof HttpSessionIdListener) {
+            eventListeners.add(t);
+            added = true;
+        }
+
+        if (t instanceof HttpSessionListener) {
+            sessionListeners.add((HttpSessionListener) t);
+            if (!added) {
+                added = true;
+            }
+        }
+
+        if (t instanceof ServletContextListener) {
+            ServletContextListener proxy = (ServletContextListener) t;
+            if (isProgrammatic) {
+                proxy = new RestrictedServletContextListener(
+                    (ServletContextListener) t);
+            }
+            // Always add the JSF listener as the first element,
+            // see GlassFish Issue 2563 for details
+            boolean isFirst =
+                "com.sun.faces.config.ConfigureListener".equals(
+                    t.getClass().getName());
+            if (isFirst) {
+                contextListeners.add(0, proxy);
+            } else {
+                contextListeners.add(proxy);
+            }
+            if (!added) {
+                added = true;
+            }
+        }
+
+        if (!added) {
+            throw new IllegalArgumentException("Invalid listener type " +
+                t.getClass().getName());
+        }
+    }
+
+    /**
+     * Adds a listener of the given class type to this ServletContext.
+     */
+    @Override
+    public void addListener(Class <? extends EventListener> listenerClass) {
+        EventListener listener = null;
+        try {
+            listener = createListenerInstance(listenerClass);
+        } catch(Throwable t) {
+             throw new IllegalArgumentException(t);
+        }    
+        
+        addListener(listener);
+    }
+
+    /**
+     * Instantiates the given EventListener class and performs any
+     * required resource injection into the new EventListener instance
+     * before returning it.
+     */
+    @Override
+    public <T extends EventListener> T createListener(Class<T> clazz)
+            throws ServletException {
+        if (!ServletContextListener.class.isAssignableFrom(clazz) &&
+                !ServletContextAttributeListener.class.isAssignableFrom(clazz) &&
+                !ServletRequestListener.class.isAssignableFrom(clazz) &&
+                !ServletRequestAttributeListener.class.isAssignableFrom(clazz) &&
+                !HttpSessionAttributeListener.class.isAssignableFrom(clazz) &&
+                !HttpSessionIdListener.class.isAssignableFrom(clazz) &&
+                !HttpSessionListener.class.isAssignableFrom(clazz)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_ADD_LISTENER_EXCEPTION),
+                                              new Object[] {clazz.getName()});
+            throw new IllegalArgumentException(msg);
+        }
+
+        try {
+            return createListenerInstance(clazz);
+        } catch (Throwable t) {
+            throw new ServletException(t);
+        }
+    }
+
+    public void setJspConfigDescriptor(JspConfigDescriptor jspConfigDesc) {
+        this.jspConfigDesc = jspConfigDesc;
+    }
+
+    /**
+     * Gets the <code>&lt;jsp-config&gt;</code> related configuration
+     * that was aggregated over the <code>web.xml</code> and
+     * <code>web-fragment.xml</code> resources of the web application
+     * represented by this ServletContext.
+     */
+    @Override
+    public JspConfigDescriptor getJspConfigDescriptor() {
+        return jspConfigDesc;
+    }
+
+    /**
+     * Gets the class loader of the web application represented by this
+     * ServletContext.
+     */
+    @Override
+    public ClassLoader getClassLoader() {
+        ClassLoader webappLoader = (getLoader() != null) ?
+            getLoader().getClassLoader() : null;
+        if (webappLoader == null) {
+            return null;
+        }
+        if (mySecurityManager != null) {
+            mySecurityManager.checkGetClassLoaderPermission(webappLoader);
+        }
+        return webappLoader;
+    }
+
+    @Override
+    public void declareRoles(String... roleNames) {
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                              new Object[] {"declareRoles", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        for (String roleName : roleNames) {
+            addSecurityRole(roleName);
+        }
+    }
+
+    public void setEffectiveMajorVersion(int effectiveMajorVersion) {
+        this.effectiveMajorVersion = effectiveMajorVersion;
+    }
+
+    @Override
+    public int getEffectiveMajorVersion() {
+        return effectiveMajorVersion;
+    }
+
+    public void setEffectiveMinorVersion(int effectiveMinorVersion) {
+        this.effectiveMinorVersion = effectiveMinorVersion;
+    }
+
+    @Override
+    public int getEffectiveMinorVersion() {
+        return effectiveMinorVersion;
+    }
+
+    @Override
+    public String getVirtualServerName() {
+        String virtualServerName = null;
+        Container parent = getParent();
+        if (parent != null) {
+            virtualServerName = parent.getName();
+        }
+        return virtualServerName;
+    }
+
+    /**
+     * Add the classname of an InstanceListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of an InstanceListener class
+     */
+    @Override
+    public void addInstanceListener(String listener) {
+        instanceListeners.add(listener);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addInstanceListener", listener);
+        }
+    }
+
+    public void addInstanceListener(InstanceListener listener) {
+        instanceListenerInstances.add(listener);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addInstanceListener", listener);
+        }
+    }
+
+    /**
+     * Add the given URL pattern as a jsp-property-group.  This maps
+     * resources that match the given pattern so they will be passed
+     * to the JSP container.  Though there are other elements in the
+     * property group, we only care about the URL pattern here.  The
+     * JSP container will parse the rest.
+     *
+     * @param pattern URL pattern to be mapped
+     */
+    @Override
+    public void addJspMapping(String pattern) {
+        String servletName = findServletMapping("*.jsp");
+        if (servletName == null) {
+            servletName = "jsp";
+        }
+
+        if( findChild(servletName) != null) {
+            addServletMapping(pattern, servletName, true);
+        } else {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Skipping " + pattern + " , no servlet "
+                        + servletName);
+            }
+        }
+    }
+
+    /**
+     * Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4)
+     *
+     * @param locale locale to map an encoding for
+     * @param encoding encoding to be used for a give locale
+     */
+    @Override
+    public void addLocaleEncodingMappingParameter(String locale, String encoding){
+        getCharsetMapper().addCharsetMappingFromDeploymentDescriptor(locale, encoding);
+    }
+
+    /**
+     * Add a local EJB resource reference for this web application.
+     *
+     * @param ejb New EJB resource reference
+     */
+    @Override
+    public void addLocalEjb(ContextLocalEjb ejb) {
+        namingResources.addLocalEjb(ejb);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addLocalEjb", ejb.getName());
+        }
+    }
+
+    /**
+     * Add a message destination for this web application.
+     *
+     * @param md New message destination
+     */
+    public void addMessageDestination(MessageDestination md) {
+        synchronized (messageDestinations) {
+            messageDestinations.put(md.getName(), md);
+        }
+        if (notifyContainerListeners) {
+            fireContainerEvent("addMessageDestination", md.getName());
+        }
+    }
+
+    /**
+     * Add a message destination reference for this web application.
+     *
+     * @param mdr New message destination reference
+     */
+    public void addMessageDestinationRef(MessageDestinationRef mdr) {
+        namingResources.addMessageDestinationRef(mdr);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addMessageDestinationRef", mdr.getName());
+        }
+    }
+
+    /**
+     * Add a new MIME mapping, replacing any existing mapping for
+     * the specified extension.
+     *
+     * @param extension Filename extension being mapped
+     * @param mimeType Corresponding MIME type
+     */
+    public void addMimeMapping(String extension, String mimeType) {
+        mimeMappings.put(extension.toLowerCase(Locale.ENGLISH), mimeType);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addMimeMapping", extension);
+        }
+    }
+
+    /**
+     * Add a new context initialization parameter.
+     *
+     * @param name Name of the new parameter
+     * @param value Value of the new  parameter
+     *
+     * @exception IllegalArgumentException if the name or value is missing,
+     *  or if this context initialization parameter has already been
+     *  registered
+     */
+    public void addParameter(String name, String value) {
+        // Validate the proposed context initialization parameter
+        if ((name == null) || (value == null)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.PARAMETER_REQUIRED_EXCEPTION), name);
+            throw new IllegalArgumentException(msg);
+        }
+        if (parameters.get(name) != null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DUPLICATE_PARAMETER_EXCEPTION), name);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Add this parameter to our defined set
+        synchronized (parameters) {
+            parameters.put(name, value);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addParameter", name);
+        }
+    }
+
+
+    /**
+     * Add a resource reference for this web application.
+     *
+     * @param resource New resource reference
+     */
+    public void addResource(ContextResource resource) {
+        namingResources.addResource(resource);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addResource", resource.getName());
+        }
+    }
+
+
+    /**
+     * Add a resource environment reference for this web application.
+     *
+     * @param name The resource environment reference name
+     * @param type The resource environment reference type
+     */
+    public void addResourceEnvRef(String name, String type) {
+        namingResources.addResourceEnvRef(name, type);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addResourceEnvRef", name);
+        }
+    }
+
+    /**
+     * Add a resource link for this web application.
+     *
+     * @param resourceLink New resource link
+     */
+    public void addResourceLink(ContextResourceLink resourceLink) {
+        namingResources.addResourceLink(resourceLink);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addResourceLink", resourceLink.getName());
+        }
+    }
+
+
+    /**
+     * Add a security role reference for this web application.
+     *
+     * @param role Security role used in the application
+     * @param link Actual security role to check for
+     */
+    public void addRoleMapping(String role, String link) {
+        synchronized (roleMappings) {
+            roleMappings.put(role, link);
+        }
+        if (notifyContainerListeners) {
+            fireContainerEvent("addRoleMapping", role);
+        }
+    }
+
+    /**
+     * Add a new security role for this web application.
+     *
+     * @param role New security role
+     */
+    public void addSecurityRole(String role) {
+        securityRoles.add(role);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addSecurityRole", role);
+        }
+    }
+
+    /**
+     * Adds the given servlet mappings to this Context.
+     *
+     * <p>If any of the specified URL patterns are already mapped to a
+     * different Servlet, no updates will be performed.
+     *
+     * @param servletMap the Servlet mappings containing the Servlet name
+     * and URL patterns
+     *
+     * @return the (possibly empty) Set of URL patterns that are already
+     * mapped to a different Servlet
+     *
+     * @exception IllegalArgumentException if the specified servlet name
+     * is not known to this Context
+     */
+     public Set<String> addServletMapping(ServletMap servletMap) {
+         return addServletMapping(servletMap.getServletName(),
+                                  servletMap.getURLPatterns());
+     }
+
+    /**
+     * Adds the given servlet mappings to this Context.
+     *
+     * <p>If any of the specified URL patterns are already mapped to a
+     * different Servlet, no updates will be performed.
+     *
+     * @param name the Servlet name
+     * @param urlPatterns the URL patterns
+     *
+     * @return the (possibly empty) Set of URL patterns that are already
+     * mapped to a different Servlet
+     *
+     * @exception IllegalArgumentException if the specified servlet name
+     * is not known to this Context
+     */
+    public Set<String> addServletMapping(String name,
+                                         String[] urlPatterns) {
+        Set<String> conflicts = null;
+
+        synchronized (servletMappings) {
+            for (String pattern : urlPatterns) {
+                pattern = adjustURLPattern(RequestUtil.urlDecode(pattern));
+                if (!validateURLPattern(pattern)) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_MAPPING_INVALID_URL_EXCEPTION), pattern);
+                    throw new IllegalArgumentException(msg);
+                }
+
+                // Ignore any conflicts with the container provided
+                // Default- and JspServlet
+                String existing = servletMappings.get(pattern);
+                if (existing != null &&
+                        !existing.equals(Constants.DEFAULT_SERVLET_NAME) &&
+                        !existing.equals(Constants.JSP_SERVLET_NAME) &&
+                        !name.equals(Constants.DEFAULT_SERVLET_NAME) &&
+                        !name.equals(Constants.JSP_SERVLET_NAME)) {
+                    if (conflicts == null) {
+                        conflicts = new HashSet<String>();
+                    }
+                    conflicts.add(pattern);
+                }
+            }
+
+            if (conflicts == null) {
+                for (String urlPattern : urlPatterns) {
+                    addServletMapping(urlPattern, name, false);
+                }
+                return Collections.emptySet();
+            } else {
+                return conflicts;
+            }
+        }
+    }
+
+    /**
+     * Adds the given servlet mapping to this Context, overriding any
+     * existing mapping for the specified pattern.
+     *
+     * @param pattern URL pattern to be mapped
+     * @param name Name of the corresponding servlet to execute
+     *
+     * @exception IllegalArgumentException if the specified servlet name
+     *  is not known to this Context
+     */
+    @Override
+    public void addServletMapping(String pattern, String name) {
+        addServletMapping(pattern, name, false);
+    }
+
+    /**
+     * Adds the given servlet mapping to this Context, overriding any
+     * existing mapping for the specified pattern.
+     *
+     * @param pattern URL pattern to be mapped
+     * @param name Name of the corresponding servlet to execute
+     * @param jspWildCard true if name identifies the JspServlet
+     * and pattern contains a wildcard; false otherwise
+     *
+     * @exception IllegalArgumentException if the specified servlet name
+     * is not known to this Context
+     */
+    public void addServletMapping(String pattern, String name,
+                                  boolean jspWildCard) {
+        // Validate the proposed mapping
+        ServletRegistrationImpl regis = servletRegisMap.get(name);
+        if (null == regis) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_MAPPING_UNKNOWN_NAME_EXCEPTION), name);
+            throw new IllegalArgumentException(msg);
+        }
+
+        pattern = adjustURLPattern(RequestUtil.urlDecode(pattern));
+        if (!validateURLPattern(pattern)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_MAPPING_INVALID_URL_EXCEPTION), pattern);
+            throw new IllegalArgumentException(msg);
+        }
+
+        /*
+         * Add this mapping to our registered set. Make sure that it is
+         * possible to override the mappings of the container provided
+         * Default- and JspServlet, and that these servlets are prevented
+         * from overriding any user-defined mappings (depending on the order
+         * in which the contents of the default-web.xml are merged with those
+         * of the app's deployment descriptor).
+         * This is to prevent the DefaultServlet from hijacking '/', and the
+         * JspServlet from hijacking *.jsp(x).
+         */
+        synchronized (servletMappings) {
+            String existing = servletMappings.get(pattern);
+            if (existing != null) {
+                if (!existing.equals(Constants.DEFAULT_SERVLET_NAME) &&
+                        !existing.equals(Constants.JSP_SERVLET_NAME) &&
+                        !name.equals(Constants.DEFAULT_SERVLET_NAME) &&
+                        !name.equals(Constants.JSP_SERVLET_NAME)) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.DUPLICATE_SERVLET_MAPPING_EXCEPTION),
+                                                      new Object[] {name, pattern, existing});
+                    throw new IllegalArgumentException(msg);
+                }
+                if (existing.equals(Constants.DEFAULT_SERVLET_NAME) ||
+                        existing.equals(Constants.JSP_SERVLET_NAME)) {
+                    // Override the mapping of the container provided
+                    // Default- or JspServlet
+                    Wrapper wrapper = (Wrapper) findChild(existing);
+                    removePatternFromServlet(wrapper, pattern);
+                    mapper.removeWrapper(pattern);
+                    servletMappings.put(pattern, name);
+                }
+            } else {
+                servletMappings.put(pattern, name);
+            }
+        }
+
+        Wrapper wrapper = regis.getWrapper();
+        wrapper.addMapping(pattern);
+
+        // Update context mapper
+        mapper.addWrapper(pattern, wrapper, jspWildCard, name, true);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addServletMapping", pattern);
+        }
+    }
+
+    /*
+     * Adds the servlet with the given name and class name to this servlet
+     * context.
+     */
+    @Override
+    public ServletRegistration.Dynamic addServlet(
+            String servletName, String className) {
+
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                                      new Object[] {"addServlet", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if (servletName == null || servletName.length() == 0) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_EMPTY_SERVLET_NAME_EXCEPTION));
+        }
+
+        synchronized (children) {
+            if (findChild(servletName) == null) {
+                DynamicServletRegistrationImpl regis =
+                    (DynamicServletRegistrationImpl)
+                        servletRegisMap.get(servletName);
+                Wrapper wrapper = null;
+                if (regis == null) {
+                    wrapper = createWrapper();
+                    wrapper.setServletClassName(className);
+                } else {
+                    // Complete preliminary servlet registration
+                    wrapper = regis.getWrapper();
+                    regis.setServletClassName(className);
+                }
+                wrapper.setName(servletName);
+                addChild(wrapper, true, (null == regis));
+                if (null == regis) {
+                    regis = (DynamicServletRegistrationImpl)
+                        servletRegisMap.get(servletName);
+                }
+                return regis;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /*
+     * Registers the given servlet instance with this ServletContext
+     * under the given <tt>servletName</tt>.
+     */
+    @Override
+    public ServletRegistration.Dynamic addServlet(
+            String servletName, Servlet servlet) {
+        return addServlet(servletName, servlet, null, null);
+    }
+
+    /*
+     * Adds the servlet with the given name and class type to this servlet
+     * context.
+     */
+    @Override
+    public ServletRegistration.Dynamic addServlet(String servletName,
+            Class <? extends Servlet> servletClass) {
+
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                              new Object[] {"addServlet", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if (servletName == null || servletName.length() == 0) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_EMPTY_SERVLET_NAME_EXCEPTION));
+        }
+
+        // Make sure servlet name is unique for this context
+        synchronized (children) {
+            if (findChild(servletName) == null) {
+                DynamicServletRegistrationImpl regis =
+                    (DynamicServletRegistrationImpl)
+                        servletRegisMap.get(servletName);
+                Wrapper wrapper = null;
+                if (regis == null) {
+                    wrapper = createWrapper();
+                    wrapper.setServletClass(servletClass);
+                } else {
+                    // Complete preliminary servlet registration
+                    wrapper = regis.getWrapper();
+                    regis.setServletClass(servletClass);
+                }
+                wrapper.setName(servletName);
+                addChild(wrapper, true, (null == regis));
+                if (null == regis) {
+                    regis = (DynamicServletRegistrationImpl)
+                        servletRegisMap.get(servletName);
+                }
+                return regis;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Adds the given servlet instance with the given name to this servlet
+     * context and initializes it.
+     *
+     * <p>In order to add any URL patterns that will be mapped to the
+     * given servlet, addServletMappings must be used. If this context
+     * has already been started, the URL patterns must be passed to
+     * addServlet instead.
+     *
+     * @param servletName the servlet name
+     * @param instance the servlet instance
+     * @param initParams Map containing the initialization parameters for
+     * the servlet
+     *
+     * @throws ServletException if the servlet fails to be initialized
+     */
+    @Override
+    public ServletRegistration.Dynamic addServlet(String servletName,
+            Servlet instance, Map<String, String> initParams) {
+        return addServlet(servletName, instance, initParams, null);
+    }
+
+    /**
+     * Adds the given servlet instance with the given name and URL patterns
+     * to this servlet context, and initializes it.
+     *
+     * @param servletName the servlet name
+     * @param servlet the servlet instance
+     * @param initParams Map containing the initialization parameters for
+     * the servlet
+     * @param urlPatterns the URL patterns that will be mapped to the servlet
+     *
+     * @return the ServletRegistration through which the servlet may be
+     * further configured
+     */
+    @Override
+    public ServletRegistration.Dynamic addServlet(String servletName,
+            Servlet servlet, Map<String, String> initParams,
+            String... urlPatterns) {
+
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                   new Object[] {"addServlet", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if (servletName == null || servletName.length() == 0) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_EMPTY_SERVLET_NAME_EXCEPTION));
+        }
+
+        if (servlet == null) {
+            throw new NullPointerException(rb.getString(LogFacade.NULL_SERVLET_INSTANCE_EXCEPTION));
+        }
+
+        if (servlet instanceof SingleThreadModel) {
+            throw new IllegalArgumentException("Servlet implements " +
+                SingleThreadModel.class.getName());
+        }
+
+        /*
+         * Make sure the given Servlet instance is unique across all deployed
+         * contexts
+         */
+        Container host = getParent();
+        if (host != null) {
+            for (Container child : host.findChildren()) {
+                if (child == this) {
+                    // Our own context will be checked further down
+                    continue;
+                }
+                if (((StandardContext) child).hasServlet(servlet)) {
+                    return null;
+                }
+            }
+        }
+
+        /*
+         * Make sure the given Servlet name and instance are unique within
+         * this context
+         */
+        synchronized (children) {
+            for (Map.Entry<String, Container> e : children.entrySet()) {
+                if (servletName.equals(e.getKey()) ||
+                        servlet == ((StandardWrapper)e.getValue()).getServlet()) {
+                    return null;
+                }
+            }
+
+            DynamicServletRegistrationImpl regis =
+                (DynamicServletRegistrationImpl)
+                    servletRegisMap.get(servletName);
+            StandardWrapper wrapper = null;
+            if (regis == null) {
+                wrapper = (StandardWrapper) createWrapper();
+            } else {
+                // Complete preliminary servlet registration
+                wrapper = regis.getWrapper();
+            }
+
+            wrapper.setName(servletName);
+            wrapper.setServlet(servlet);
+            if (initParams != null) {
+                for (Map.Entry<String, String> e : initParams.entrySet()) {
+                    wrapper.addInitParameter(e.getKey(), e.getValue());
+                }
+            }
+
+            addChild(wrapper, true, (null == regis));
+            if (null == regis) {
+                regis = (DynamicServletRegistrationImpl)
+                    servletRegisMap.get(servletName);
+            }
+
+            if (urlPatterns != null) {
+                for (String urlPattern : urlPatterns) {
+                    addServletMapping(urlPattern, servletName, false);
+                }
+            }
+
+            return regis;
+        }
+    }
+
+    /*
+     * Adds the servlet with the given name and jsp file to this servlet
+     * context.
+     */
+    @Override
+    public ServletRegistration.Dynamic addJspFile(
+            String servletName, String jspFile) {
+
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                    new Object[] {"addJspFile", getName()});
+            throw new IllegalStateException(msg);
+        }
+
+        if (servletName == null || servletName.length() == 0) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.NULL_EMPTY_SERVLET_NAME_EXCEPTION));
+        }
+
+        synchronized (children) {
+            if (findChild(servletName) == null) {
+                DynamicServletRegistrationImpl regis =
+                        (DynamicServletRegistrationImpl)
+                                servletRegisMap.get(servletName);
+                Wrapper wrapper = null;
+                if (regis == null) {
+                    wrapper = createWrapper();
+                } else {
+                    // Override an existing registration
+                    wrapper = regis.getWrapper();
+                }
+                wrapper.setJspFile(jspFile);
+                wrapper.setName(servletName);
+                addChild(wrapper, true, (null == regis));
+                if (null == regis) {
+                    regis = (DynamicServletRegistrationImpl)
+                            servletRegisMap.get(servletName);
+                }
+                return regis;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * This method is overridden in web-glue to also remove the given
+     * mapping from the deployment backend's WebBundleDescriptor.
+     */
+    protected void removePatternFromServlet(Wrapper wrapper, String pattern) {
+        wrapper.removeMapping(pattern);
+    }
+
+    /**
+     * Checks whether this context contains the given Servlet instance
+     */
+    public boolean hasServlet(Servlet servlet) {
+        for (Map.Entry<String, Container> e : children.entrySet()) {
+            if (servlet == ((StandardWrapper)e.getValue()).getServlet()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Instantiates the given Servlet class and performs any required
+     * resource injection into the new Servlet instance before returning
+     * it.
+     */
+    @Override
+    public <T extends Servlet> T createServlet(Class<T> clazz)
+            throws ServletException {
+        try {
+            return createServletInstance(clazz);
+        } catch (Throwable t) {
+            throw new ServletException("Unable to create Servlet from " +
+                                       "class " + clazz.getName(), t);
+        }
+    }
+
+    /**
+     * Gets the ServletRegistration corresponding to the servlet with the
+     * given <tt>servletName</tt>.
+     */
+    @Override
+    public ServletRegistration getServletRegistration(String servletName) {
+        return servletRegisMap.get(servletName);
+    }
+
+    /**
+     * Gets a Map of the ServletRegistration objects corresponding to all
+     * currently registered servlets.
+     */
+    @Override
+    public Map<String, ? extends ServletRegistration> getServletRegistrations() {
+        return Collections.unmodifiableMap(servletRegisMap);
+    }
+
+    /**
+     * Add a new watched resource to the set recognized by this Context.
+     *
+     * @param name New watched resource file name
+     */
+    @Override
+    public void addWatchedResource(String name) {
+        watchedResources.add(name);
+        fireContainerEvent("addWatchedResource", name);
+    }
+
+    /**
+     * Add a new welcome file to the set recognized by this Context.
+     *
+     * @param name New welcome file name
+     */
+    @Override
+    public void addWelcomeFile(String name) {
+
+        // Welcome files from the application deployment descriptor
+        // completely replace those from the default conf/web.xml file
+        if (replaceWelcomeFiles) {
+            welcomeFiles = new String[0];
+            setReplaceWelcomeFiles(false);
+        }
+        String results[] = new String[welcomeFiles.length + 1];
+        for (int i = 0; i < welcomeFiles.length; i++)
+            results[i] = welcomeFiles[i];
+        results[welcomeFiles.length] = name;
+        welcomeFiles = results;
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addWelcomeFile", name);
+        }
+    }
+
+    /**
+     * Add the classname of a LifecycleListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of a LifecycleListener class
+     */
+    @Override
+    public void addWrapperLifecycle(String listener) {
+        wrapperLifecycles.add(listener);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addWrapperLifecycle", listener);
+        }
+    }
+
+    /**
+     * Add the classname of a ContainerListener to be added to each
+     * Wrapper appended to this Context.
+     *
+     * @param listener Java class name of a ContainerListener class
+     */
+    @Override
+    public void addWrapperListener(String listener) {
+        wrapperListeners.add(listener);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addWrapperListener", listener);
+        }
+    }
+
+    /**
+     * Factory method to create and return a new Wrapper instance, of
+     * the Java implementation class appropriate for this Context
+     * implementation.  The constructor of the instantiated Wrapper
+     * will have been called, but no properties will have been set.
+     */
+    @Override
+    public Wrapper createWrapper() {
+        Wrapper wrapper = new StandardWrapper();
+
+        synchronized (instanceListeners) {
+            for (String instanceListener : instanceListeners) {
+                try {
+                    Class clazz = Class.forName(instanceListener);
+                    wrapper.addInstanceListener((InstanceListener)clazz.newInstance());
+                } catch(Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.CREATING_INSTANCE_LISTENER_EXCEPTION),
+                                                      instanceListener);
+                    log.log(Level.SEVERE, msg, t);
+                    return (null);
+                }
+            }
+        }
+
+        synchronized (instanceListenerInstances) {
+            for(InstanceListener instanceListenerInstance : instanceListenerInstances) {
+                wrapper.addInstanceListener(instanceListenerInstance);
+            }
+        }
+
+        Iterator<String> i = wrapperLifecycles.iterator();
+        while (i.hasNext()) {
+            String wrapperLifecycle = i.next();
+            try {
+                Class clazz = Class.forName(wrapperLifecycle);
+                if(wrapper instanceof Lifecycle) {
+                    ((Lifecycle)wrapper).addLifecycleListener(
+                        (LifecycleListener)clazz.newInstance());
+                }
+            } catch(Throwable t) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CREATING_LIFECYCLE_LISTENER_EXCEPTION),
+                                                  wrapperLifecycle);
+                log.log(Level.SEVERE, msg, t);
+                return (null);
+            }
+        }
+
+        i = wrapperListeners.iterator();
+        while (i.hasNext()) {
+            String wrapperListener = i.next();
+            try {
+                Class clazz = Class.forName(wrapperListener);
+                wrapper.addContainerListener((ContainerListener)
+                    clazz.newInstance());
+            } catch(Throwable t) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CREATING_CONTAINER_LISTENER_EXCEPTION),
+                                                  wrapperListener);
+                log.log(Level.SEVERE, msg, t);
+                return (null);
+            }
+        }
+
+        return (wrapper);
+    }
+
+    /**
+     * Return the set of application parameters for this application.
+     */
+    @Override
+    public List<ApplicationParameter> findApplicationParameters() {
+        return applicationParameters;
+    }
+
+    /**
+     * Gets the security constraints defined for this web application.
+     */
+    @Override
+    public List<SecurityConstraint> getConstraints() {
+        return constraints;
+    }
+
+    /**
+     * Checks whether this web application has any security constraints
+     * defined.
+     */
+    @Override
+    public boolean hasConstraints() {
+        return !constraints.isEmpty();
+    }
+
+    /**
+     * Return the EJB resource reference with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired EJB resource reference
+     */
+    @Override
+    public ContextEjb findEjb(String name) {
+        return namingResources.findEjb(name);
+    }
+
+    /**
+     * Return the defined EJB resource references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    @Override
+    public ContextEjb[] findEjbs() {
+        return namingResources.findEjbs();
+    }
+
+    /**
+     * Return the environment entry with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired environment entry
+     */
+    @Override
+    public ContextEnvironment findEnvironment(String name) {
+        return namingResources.findEnvironment(name);
+    }
+
+    /**
+     * Return the set of defined environment entries for this web
+     * application.  If none have been defined, a zero-length array
+     * is returned.
+     */
+    @Override
+    public ContextEnvironment[] findEnvironments() {
+        return namingResources.findEnvironments();
+    }
+
+    /**
+     * Return the error page entry for the specified HTTP error code,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param errorCode Error code to look up
+     */
+    @Override
+    public ErrorPage findErrorPage(int errorCode) {
+        if ((errorCode >= 400) && (errorCode < 600)) {
+            return statusPages.get(errorCode);
+        }
+
+        return null;
+    }
+
+    /**
+     * Return the error page entry for the specified Java exception type,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param exceptionType Exception type to look up
+     */
+    @Override
+    public ErrorPage findErrorPage(String exceptionType) {
+        synchronized (exceptionPages) {
+            return exceptionPages.get(exceptionType);
+        }
+    }
+
+    /**
+     * Gets the default error page of this context.
+     *
+     * <p>A default error page is an error page that was declared without
+     * any exception-type and error-code.
+     *
+     * @return the default error page of this context, or null if this
+     * context does not have any default error page
+     */
+    @Override
+    public ErrorPage getDefaultErrorPage() {
+        return defaultErrorPage;
+    }
+
+    /**
+     * Return the filter definition for the specified filter name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param filterName Filter name to look up
+     */
+    @Override
+    public FilterDef findFilterDef(String filterName) {
+        synchronized (filterDefs) {
+            return filterDefs.get(filterName);
+        }
+    }
+
+    /**
+     * Return the set of defined filters for this Context.
+     */
+    @Override
+    public FilterDef[] findFilterDefs() {
+        synchronized (filterDefs) {
+            FilterDef results[] = new FilterDef[filterDefs.size()];
+            return filterDefs.values().toArray(results);
+        }
+    }
+
+    /**
+     * Return the list of filter mappings for this Context.
+     */
+    @Override
+    public List<FilterMap> findFilterMaps() {
+        return filterMaps;
+    }
+
+    /**
+     * Return the list of InstanceListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    @Override
+    public List<String> findInstanceListeners() {
+        return instanceListeners;
+    }
+
+    /**
+     * Return the local EJB resource reference with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired EJB resource reference
+     */
+    @Override
+    public ContextLocalEjb findLocalEjb(String name) {
+        return namingResources.findLocalEjb(name);
+    }
+
+    /**
+     * Return the defined local EJB resource references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    @Override
+    public ContextLocalEjb[] findLocalEjbs() {
+        return namingResources.findLocalEjbs();
+    }
+
+    /**
+     * FIXME: Fooling introspection ...
+     */
+    public Context findMappingObject() {
+        return (Context) getMappingObject();
+    }
+
+    /**
+     * Return the message destination with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired message destination
+     */
+    public MessageDestination findMessageDestination(String name) {
+        synchronized (messageDestinations) {
+            return messageDestinations.get(name);
+        }
+    }
+
+    /**
+     * Return the set of defined message destinations for this web
+     * application.  If none have been defined, a zero-length array
+     * is returned.
+     */
+    public MessageDestination[] findMessageDestinations() {
+        synchronized (messageDestinations) {
+            return messageDestinations.values().toArray(
+                new MessageDestination[messageDestinations.size()]);
+        }
+    }
+
+    /**
+     * Return the message destination ref with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired message destination ref
+     */
+    public MessageDestinationRef findMessageDestinationRef(String name) {
+        return namingResources.findMessageDestinationRef(name);
+    }
+
+    /**
+     * Return the set of defined message destination refs for this web
+     * application.  If none have been defined, a zero-length array
+     * is returned.
+     */
+    public MessageDestinationRef[] findMessageDestinationRefs() {
+        return namingResources.findMessageDestinationRefs();
+    }
+
+    /**
+     * Return the MIME type to which the specified extension is mapped,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param extension Extension to map to a MIME type
+     */
+    @Override
+    public String findMimeMapping(String extension) {
+
+        return mimeMappings.get(extension.toLowerCase(Locale.ENGLISH));
+    }
+
+    /**
+     * Return the extensions for which MIME mappings are defined.  If there
+     * are none, a zero-length array is returned.
+     */
+    @Override
+    public String[] findMimeMappings() {
+        return mimeMappings.keySet().toArray(
+            new String[mimeMappings.size()]);
+    }
+
+    /**
+     * Return the value for the specified context initialization
+     * parameter name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the parameter to return
+     */
+    @Override
+    public String findParameter(String name) {
+        synchronized (parameters) {
+            return parameters.get(name);
+        }
+    }
+
+    /**
+     * Return the names of all defined context initialization parameters
+     * for this Context.  If no parameters are defined, a zero-length
+     * array is returned.
+     */
+    @Override
+    public String[] findParameters() {
+        synchronized (parameters) {
+            return parameters.keySet().toArray(new String[parameters.size()]);
+        }
+    }
+
+    /**
+     * Return the resource reference with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource reference
+     */
+    @Override
+    public ContextResource findResource(String name) {
+        return namingResources.findResource(name);
+    }
+
+    /**
+     * Return the resource environment reference type for the specified
+     * name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource environment reference
+     */
+    @Override
+    public String findResourceEnvRef(String name) {
+        return namingResources.findResourceEnvRef(name);
+    }
+
+    /**
+     * Return the set of resource environment reference names for this
+     * web application.  If none have been specified, a zero-length
+     * array is returned.
+     */
+    @Override
+    public String[] findResourceEnvRefs() {
+        return namingResources.findResourceEnvRefs();
+    }
+
+    /**
+     * Return the resource link with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource link
+     */
+    @Override
+    public ContextResourceLink findResourceLink(String name) {
+        return namingResources.findResourceLink(name);
+    }
+
+    /**
+     * Return the defined resource links for this application.  If
+     * none have been defined, a zero-length array is returned.
+     */
+    @Override
+    public ContextResourceLink[] findResourceLinks() {
+        return namingResources.findResourceLinks();
+    }
+
+    /**
+     * Return the defined resource references for this application.  If
+     * none have been defined, a zero-length array is returned.
+     */
+    @Override
+    public ContextResource[] findResources() {
+        return namingResources.findResources();
+    }
+
+    /**
+     * For the given security role (as used by an application), return the
+     * corresponding role name (as defined by the underlying Realm) if there
+     * is one.  Otherwise, return the specified role unchanged.
+     *
+     * @param role Security role to map
+     */
+    @Override
+    public String findRoleMapping(String role) {
+
+        String realRole = null;
+        synchronized (roleMappings) {
+            realRole = roleMappings.get(role);
+        }
+        if (realRole != null)
+            return (realRole);
+        else
+            return (role);
+    }
+
+    /**
+     * Checks if the given security role is defined for this application.
+     *
+     * @param role Security role to check for
+     *
+     * @return true if the specified security role is defined
+     * for this application, false otherwise
+     */
+    @Override
+    public boolean hasSecurityRole(String role) {
+        return securityRoles.contains(role);
+    }
+
+    /**
+     * Removes any security roles defined for this application.
+     */
+    @Override
+    public void removeSecurityRoles() {
+        // Inform interested listeners
+        if (notifyContainerListeners) {
+            Iterator<String> i = securityRoles.iterator();
+            while (i.hasNext()) {
+                fireContainerEvent("removeSecurityRole", i.next());
+            }
+        }
+        securityRoles.clear();
+    }
+
+    /**
+     * Return the servlet name mapped by the specified pattern (if any);
+     * otherwise return <code>null</code>.
+     *
+     * @param pattern Pattern for which a mapping is requested
+     */
+    @Override
+    public String findServletMapping(String pattern) {
+        synchronized (servletMappings) {
+            return servletMappings.get(pattern);
+        }
+    }
+
+    /**
+     * Return the patterns of all defined servlet mappings for this
+     * Context.  If no mappings are defined, a zero-length array is returned.
+     */
+    @Override
+    public String[] findServletMappings() {
+        synchronized (servletMappings) {
+            String results[] = new String[servletMappings.size()];
+            return
+                servletMappings.keySet().toArray(results);
+        }
+    }
+
+    /**
+     * Return the context-relative URI of the error page for the specified
+     * HTTP status code, if any; otherwise return <code>null</code>.
+     *
+     * @param status HTTP status code to look up
+     */
+    @Override
+    public ErrorPage findStatusPage(int status) {
+        return statusPages.get(status);
+    }
+
+    /**
+     * Return the set of HTTP status codes for which error pages have
+     * been specified.  If none are specified, a zero-length array
+     * is returned.
+     */
+    @Override
+    public int[] findStatusPages() {
+        synchronized (statusPages) {
+            int results[] = new int[statusPages.size()];
+            Iterator<Integer> elements = statusPages.keySet().iterator();
+            int i = 0;
+            while (elements.hasNext())
+                results[i++] = elements.next();
+            return results;
+        }
+    }
+
+    /**
+     * Return <code>true</code> if the specified welcome file is defined
+     * for this Context; otherwise return <code>false</code>.
+     *
+     * @param name Welcome file to verify
+     */
+    @Override
+    public boolean findWelcomeFile(String name) {
+        synchronized (welcomeFiles) {
+            for(String welcomeFile : welcomeFiles) {
+                if(name.equals(welcomeFile)) {
+                    return true;
+                }
+            }
+        }
+        return (false);
+    }
+
+    /**
+     * Gets the watched resources defined for this web application.
+     */
+    @Override
+    public List<String> getWatchedResources() {
+        return watchedResources;
+    }
+
+    /**
+     * Return the set of welcome files defined for this Context.  If none are
+     * defined, a zero-length array is returned.
+     */
+    @Override
+    public String[] findWelcomeFiles() {
+        return (welcomeFiles);
+    }
+
+    /**
+     * Return the list of LifecycleListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    @Override
+    public List<String> findWrapperLifecycles() {
+        return wrapperLifecycles;
+    }
+
+    /**
+     * Return the list of ContainerListener classes that will be added to
+     * newly created Wrappers automatically.
+     */
+    @Override
+    public List<String> findWrapperListeners() {
+        return wrapperListeners;
+    }
+
+    /**
+     * Gets the Authenticator of this Context.
+     *
+     * @return the Authenticator of this Context
+     */
+    @Override
+    public Authenticator getAuthenticator() {
+        Pipeline p = getPipeline();
+        if (p != null) {
+            for (GlassFishValve valve : p.getValves()) {
+                if (valve instanceof Authenticator) {
+                    return (Authenticator) valve;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Reload this web application, if reloading is supported.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  This method is designed to deal with
+     * reloads required by changes to classes in the underlying repositories
+     * of our class loader.  It does not handle changes to the web application
+     * deployment descriptor.  If that has occurred, you should stop this
+     * Context and create (and start) a new Context instance instead.
+     *
+     * @exception IllegalStateException if the <code>reloadable</code>
+     *  property is set to <code>false</code>.
+     */
+    @Override
+    public synchronized void reload() {
+
+        // Validate our current component state
+        if (!started) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CONTAINER_NOT_STARTED_EXCEPTION), logName());
+            throw new IllegalStateException(msg);
+        }
+        // Make sure reloading is enabled
+        //      if (!reloadable)
+        //          throw new IllegalStateException
+        //              (sm.getString("standardContext.notReloadable"));
+        //standardContext.notReloadable=PWC1287: Reloading is disabled on this Context
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.RELOADING_STARTED);
+        }
+
+        // Stop accepting requests temporarily
+        setPaused(true);
+
+        try {
+            stop();
+        } catch (LifecycleException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.STOPPING_CONTEXT_EXCEPTION), this);
+            log.log(Level.SEVERE, msg, e);
+        }
+
+        try {
+            start();
+        } catch (LifecycleException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.STARTING_CONTEXT_EXCEPTION), this);
+            log.log(Level.SEVERE, msg, e);
+        }
+
+        setPaused(false);
+
+    }
+
+    /**
+     * Remove the application parameter with the specified name from
+     * the set for this application.
+     *
+     * @param name Name of the application parameter to remove
+     */
+    @Override
+    public void removeApplicationParameter(String name) {
+        ApplicationParameter match = null;
+        Iterator<ApplicationParameter> i =
+            applicationParameters.iterator();
+        while (i.hasNext()) {
+            ApplicationParameter applicationParameter = i.next();
+            // Make sure this parameter is currently present
+            if (name.equals(applicationParameter.getName())) {
+                match = applicationParameter;
+                break;
+            }
+        }
+        if (match != null) {
+            applicationParameters.remove(match);
+            // Inform interested listeners
+            if (notifyContainerListeners) {
+                fireContainerEvent("removeApplicationParameter", name);
+            }
+        }
+    }
+
+    /**
+     * Removes the given child container.
+     *
+     * @param child the child container to be removed
+     *
+     * @exception IllegalArgumentException if the given child container is
+     * not an implementation of Wrapper
+     */
+    @Override
+    public void removeChild(Container child) {
+
+        if (!(child instanceof Wrapper))
+            throw new IllegalArgumentException(rb.getString(LogFacade.NO_WRAPPER_EXCEPTION));
+
+        super.removeChild(child);
+    }
+
+    /**
+     * Removes any security constraints from this web application.
+     */
+    @Override
+    public void removeConstraints() {
+        // Inform interested listeners
+        if (notifyContainerListeners) {
+            Iterator<SecurityConstraint> i = constraints.iterator();
+            while (i.hasNext()) {
+                fireContainerEvent("removeConstraint", i.next());
+            }
+        }
+        constraints.clear();
+    }
+
+    /**
+     * Remove any EJB resource reference with the specified name.
+     *
+     * @param name Name of the EJB resource reference to remove
+     */
+    public void removeEjb(String name) {
+
+        namingResources.removeEjb(name);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeEjb", name);
+        }
+    }
+
+    /**
+     * Remove any environment entry with the specified name.
+     *
+     * @param name Name of the environment entry to remove
+     */
+    @Override
+    public void removeEnvironment(String name) {
+        if (namingResources == null) {
+            return;
+        }
+        ContextEnvironment env = namingResources.findEnvironment(name);
+        if (env == null) {
+            throw new IllegalArgumentException
+                ("Invalid environment name '" + name + "'");
+        }
+
+        namingResources.removeEnvironment(name);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeEnvironment", name);
+        }
+    }
+
+    /**
+     * Removes any error page declarations.
+     */
+    @Override
+    public void removeErrorPages() {
+        synchronized (exceptionPages) {
+            if (notifyContainerListeners) {
+                for (ErrorPage errorPage : exceptionPages.values()) {
+                    fireContainerEvent("removeErrorPage", errorPage);
+                }
+            }
+            exceptionPages.clear();
+        }
+        synchronized (statusPages) {
+            if (notifyContainerListeners) {
+                for (ErrorPage statusPage : statusPages.values()) {
+                    fireContainerEvent("removeErrorPage", statusPage);
+                }
+            }
+            statusPages.clear();
+        }
+    }
+
+    /**
+     * Remove the specified filter definition from this Context, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param filterDef Filter definition to be removed
+     */
+    @Override
+    public void removeFilterDef(FilterDef filterDef) {
+
+        synchronized (filterDefs) {
+            filterDefs.remove(filterDef.getFilterName());
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeFilterDef", filterDef);
+        }
+    }
+
+    /**
+     * Removes any filter mappings from this Context.
+     */
+    @Override
+    public void removeFilterMaps() {
+        // Inform interested listeners
+        if (notifyContainerListeners) {
+            Iterator<FilterMap> i = filterMaps.iterator();
+            while (i.hasNext()) {
+                fireContainerEvent("removeFilterMap", i.next());
+            }
+        }
+        filterMaps.clear();
+    }
+
+    /**
+     * Remove a class name from the list of InstanceListener classes that
+     * will be added to newly created Wrappers.
+     *
+     * @param listener Class name of an InstanceListener class to be removed
+     */
+    @Override
+    public void removeInstanceListener(String listener) {
+        instanceListeners.remove(listener);
+        // Inform interested listeners
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeInstanceListener", listener);
+        }
+    }
+
+    /**
+     * Remove any local EJB resource reference with the specified name.
+     *
+     * @param name Name of the EJB resource reference to remove
+     */
+    @Override
+    public void removeLocalEjb(String name) {
+
+        namingResources.removeLocalEjb(name);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeLocalEjb", name);
+        }
+    }
+
+    /**
+     * Remove any message destination with the specified name.
+     *
+     * @param name Name of the message destination to remove
+     */
+    public void removeMessageDestination(String name) {
+
+        synchronized (messageDestinations) {
+            messageDestinations.remove(name);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeMessageDestination", name);
+        }
+    }
+
+    /**
+     * Remove any message destination ref with the specified name.
+     *
+     * @param name Name of the message destination ref to remove
+     */
+    public void removeMessageDestinationRef(String name) {
+
+        namingResources.removeMessageDestinationRef(name);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeMessageDestinationRef", name);
+        }
+    }
+
+    /**
+     * Remove the MIME mapping for the specified extension, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param extension Extension to remove the mapping for
+     */
+    @Override
+    public void removeMimeMapping(String extension) {
+
+        mimeMappings.remove(extension.toLowerCase(Locale.ENGLISH));
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeMimeMapping", extension);
+        }
+    }
+
+    /**
+     * Remove the context initialization parameter with the specified
+     * name, if it exists; otherwise, no action is taken.
+     *
+     * @param name Name of the parameter to remove
+     */
+    @Override
+    public void removeParameter(String name) {
+
+        synchronized (parameters) {
+            parameters.remove(name);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeParameter", name);
+        }
+    }
+
+    /**
+     * Remove any resource reference with the specified name.
+     *
+     * @param resourceName Name of the resource reference to remove
+     */
+    @Override
+    public void removeResource(String resourceName) {
+        String decoded = URLDecoder.decode(resourceName);
+        if (namingResources == null) {
+            return;
+        }
+        ContextResource resource = namingResources.findResource(decoded);
+        if (resource == null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name '" + decoded + "'");
+        }
+
+        namingResources.removeResource(decoded);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeResource", decoded);
+        }
+    }
+
+    /**
+     * Remove any resource environment reference with the specified name.
+     *
+     * @param name Name of the resource environment reference to remove
+     */
+    @Override
+    public void removeResourceEnvRef(String name) {
+
+        namingResources.removeResourceEnvRef(name);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeResourceEnvRef", name);
+        }
+    }
+
+    /**
+     * Remove any resource link with the specified name.
+     *
+     * @param link Name of the resource link to remove
+     */
+    @Override
+    public void removeResourceLink(String link) {
+        String decoded = URLDecoder.decode(link);
+        if (namingResources == null) {
+            return;
+        }
+        ContextResourceLink resource = namingResources.findResourceLink(decoded);
+        if (resource == null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name '" + decoded + "'");
+        }
+
+        namingResources.removeResourceLink(decoded);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeResourceLink", decoded);
+        }
+    }
+
+    /**
+     * Remove any security role reference for the specified name
+     *
+     * @param role Security role (as used in the application) to remove
+     */
+    @Override
+    public void removeRoleMapping(String role) {
+        synchronized (roleMappings) {
+            roleMappings.remove(role);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeRoleMapping", role);
+        }
+    }
+
+    /**
+     * Remove any servlet mapping for the specified pattern, if it exists;
+     * otherwise, no action is taken.
+     *
+     * @param pattern URL pattern of the mapping to remove
+     */
+    @Override
+    public void removeServletMapping(String pattern) {
+
+        String name = null;
+        synchronized (servletMappings) {
+            name = servletMappings.remove(pattern);
+        }
+        Wrapper wrapper = (Wrapper) findChild(name);
+        if( wrapper != null ) {
+            wrapper.removeMapping(pattern);
+        }
+        mapper.removeWrapper(pattern);
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeServletMapping", pattern);
+        }
+    }
+
+    /**
+     * Checks whether this web application has any watched resources
+     * defined.
+     */
+    @Override
+    public boolean hasWatchedResources() {
+        return !watchedResources.isEmpty();
+    }
+
+    /**
+     * Clears any watched resources defined for this web application.
+     */
+    @Override
+    public void removeWatchedResources() {
+        synchronized (watchedResources) {
+            // Inform interested listeners
+            if (notifyContainerListeners) {
+                Iterator<String> i = watchedResources.iterator();
+                while (i.hasNext()) {
+                    fireContainerEvent("removeWatchedResource", i.next());
+                }
+            }
+            watchedResources.clear();
+        }
+    }
+
+    @Override
+    public void removeWelcomeFiles() {
+        if (notifyContainerListeners) {
+            for (String welcomeFile : welcomeFiles) {
+                fireContainerEvent("removeWelcomeFile", welcomeFile);
+            }
+        }
+        welcomeFiles = new String[0];
+    }
+
+    @Override
+    public void removeWrapperLifecycles() {
+        // Inform interested listeners
+        if (notifyContainerListeners) {
+            Iterator<String> i = wrapperLifecycles.iterator();
+            while (i.hasNext()) {
+                fireContainerEvent("removeWrapperLifecycle", i.next());
+            }
+        }
+        wrapperLifecycles.clear();
+    }
+
+    @Override
+    public void removeWrapperListeners() {
+        // Inform interested listeners
+        if (notifyContainerListeners) {
+            Iterator<String> i = wrapperListeners.iterator();
+            while (i.hasNext()) {
+                fireContainerEvent("removeWrapperListener", i.next());
+            }
+        }
+        wrapperListeners.clear();
+    }
+
+    @Override
+    public void fireRequestInitializedEvent(ServletRequest request) {
+        List<EventListener> listeners = getApplicationEventListeners();
+        ServletRequestEvent event = null;
+        if (!listeners.isEmpty()) {
+            event = new ServletRequestEvent(getServletContext(), request);
+            // create pre-service event
+            Iterator<EventListener> iter = listeners.iterator();
+            while (iter.hasNext()) {
+                EventListener eventListener = iter.next();
+                if (!(eventListener instanceof ServletRequestListener)) {
+                    continue;
+                }
+                ServletRequestListener listener =
+                    (ServletRequestListener) eventListener;
+                // START SJSAS 6329662
+                fireContainerEvent(ContainerEvent.BEFORE_REQUEST_INITIALIZED,
+                    listener);
+                // END SJSAS 6329662
+                try {
+                    listener.requestInitialized(event);
+                } catch (Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.REQUEST_INIT_EXCEPTION),
+                                                      listener.getClass().getName());
+                    log.log(Level.WARNING, msg, t);
+                    request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
+                // START SJSAS 6329662
+                } finally {
+                    fireContainerEvent(ContainerEvent.AFTER_REQUEST_INITIALIZED,
+                        listener);
+                // END SJSAS 6329662
+                }
+            }
+        }
+    }
+
+    @Override
+    public void fireRequestDestroyedEvent(ServletRequest request) {
+        List<EventListener> listeners = getApplicationEventListeners();
+        if (!listeners.isEmpty()) {
+            // create post-service event
+            ServletRequestEvent event = new ServletRequestEvent(getServletContext(),
+                request);
+            int len = listeners.size();
+            for (int i = 0; i < len; i++) {
+                EventListener eventListener = listeners.get((len - 1) - i);
+                if (!(eventListener instanceof ServletRequestListener)) {
+                    continue;
+                }
+                ServletRequestListener listener =
+                    (ServletRequestListener) eventListener;
+                // START SJSAS 6329662
+                fireContainerEvent(ContainerEvent.BEFORE_REQUEST_DESTROYED,
+                    listener);
+                // END SJSAS 6329662
+                try {
+                    listener.requestDestroyed(event);
+                } catch (Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.REQUEST_DESTROY_EXCEPTION),
+                            listener.getClass().getName());
+                    log.log(Level.WARNING, msg, t);
+                    request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
+                // START SJSAS 6329662
+                } finally {
+                    fireContainerEvent(ContainerEvent.AFTER_REQUEST_DESTROYED,
+                        listener);
+                // END SJSAS 6329662
+                }
+            }
+        }
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Configure and initialize the set of filters for this Context.
+     * Return <code>true</code> if all filter initialization completed
+     * successfully, or <code>false</code> otherwise.
+     */
+    public boolean filterStart() {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Starting filters");
+        // Instantiate and record a FilterConfig for each defined filter
+        boolean ok = true;
+        synchronized (filterConfigs) {
+            filterConfigs.clear();
+            for (String name : filterDefs.keySet()) {
+                String safeName = neutralizeForLog(name);
+                if(log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, " Starting filter '" + safeName + "'");
+                }
+                try {
+                    filterConfigs.put(name,
+                        new ApplicationFilterConfig(this,
+                                                    filterDefs.get(name)));
+                } catch(Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.STARTING_FILTER_EXCEPTION), safeName);
+                    getServletContext().log(msg, t);
+                    ok = false;
+                }
+            }
+        }
+
+        return (ok);
+
+    }
+
+    /**
+     * Finalize and release the set of filters for this Context.
+     * Return <code>true</code> if all filter finalization completed
+     * successfully, or <code>false</code> otherwise.
+     */
+    public boolean filterStop() {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Stopping filters");
+
+        // Release all Filter and FilterConfig instances
+        synchronized (filterConfigs) {
+            for (String filterName : filterConfigs.keySet()) {
+                String safeFilterName = neutralizeForLog(filterName);
+                if(log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, " Stopping filter '" + safeFilterName + "'");
+                }
+                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)filterConfigs.get(filterName);
+                filterConfig.release();
+            }
+            filterConfigs.clear();
+        }
+        return (true);
+    }
+
+    /**
+     * Find and return the initialized <code>FilterConfig</code> for the
+     * specified filter name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired filter
+     */
+    public FilterConfig findFilterConfig(String name) {
+        return filterConfigs.get(name);
+    }
+
+    /**
+     * Notifies all ServletContextListeners at their contextInitialized
+     * method.
+     */
+    protected void contextListenerStart() {
+        ServletContextEvent event = new ServletContextEvent(
+            getServletContext());
+        for (ServletContextListener listener : contextListeners) {
+            if (listener instanceof RestrictedServletContextListener) {
+                listener = ((RestrictedServletContextListener) listener).
+                    getNestedListener();
+                context.setRestricted(true);
+            }
+            try {
+                fireContainerEvent(ContainerEvent.BEFORE_CONTEXT_INITIALIZED,
+                                   listener);
+                listener.contextInitialized(event);
+            } finally {
+                context.setRestricted(false);
+                fireContainerEvent(ContainerEvent.AFTER_CONTEXT_INITIALIZED,
+                                   listener);
+            }
+        }
+
+        /*
+         * Make sure there are no preliminary servlet or filter
+         * registrations left after all listeners have been notified
+         */
+        Collection<ServletRegistrationImpl> servletRegistrations =
+            servletRegisMap.values();
+        for (ServletRegistrationImpl regis : servletRegistrations) {
+            if (null == regis.getClassName() && null == regis.getJspFile()) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_WITHOUT_ANY_CLASS_OR_JSP), regis.getName());
+                throw new IllegalStateException(msg);
+            }
+        }
+        Collection<FilterRegistrationImpl> filterRegistrations =
+            filterRegisMap.values();
+        for (FilterRegistrationImpl regis : filterRegistrations) {
+            if (null == regis.getClassName()) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.FILTER_WITHOUT_ANY_CLASS), regis.getName());
+                throw new IllegalStateException(msg);
+            }
+        }
+
+        isContextInitializedCalled = true;
+    }
+
+
+    /**
+     * Loads and instantiates the listener with the specified classname.
+     *
+     * @param loader the classloader to use
+     * @param listenerClassName the fully qualified classname to instantiate
+     *
+     * @return the instantiated listener
+     *
+     * @throws Exception if the specified classname fails to be loaded or
+     * instantiated
+     */
+    @SuppressWarnings("unchecked")
+    protected EventListener loadListener(ClassLoader loader,
+                                         String listenerClassName)
+            throws Exception {
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Configuring event listener class '" +
+                    neutralizeForLog(listenerClassName) + "'");
+        }
+        return createListener((Class<EventListener>)
+            loader.loadClass(listenerClassName));
+    }
+
+    /**
+     * Notifies all ServletContextListeners at their contextDestroyed
+     * method.
+     *
+     * @return <code>true</code> if the event was processed successfully,
+     * <code>false</code> otherwise.
+     */
+    private boolean contextListenerStop() {
+
+        boolean ok = true;
+
+        if (contextListeners.isEmpty()) {
+            return ok;
+        }
+
+        ServletContextEvent event = new ServletContextEvent(
+            getServletContext());
+        int len = contextListeners.size();
+        for (int i = 0; i < len; i++) {
+            // Invoke in reverse order of declaration
+            ServletContextListener listener =
+                contextListeners.get((len - 1) - i);
+            if (listener instanceof RestrictedServletContextListener) {
+                listener = ((RestrictedServletContextListener) listener).
+                    getNestedListener();
+                context.setRestricted(true);
+            }
+            try {
+                fireContainerEvent(ContainerEvent.BEFORE_CONTEXT_DESTROYED,
+                                   listener);
+                listener.contextDestroyed(event);
+                fireContainerEvent(ContainerEvent.AFTER_CONTEXT_DESTROYED,
+                                   listener);
+            } catch (Throwable t) {
+                context.setRestricted(false);
+                fireContainerEvent(ContainerEvent.AFTER_CONTEXT_DESTROYED,
+                                   listener);
+                String msg = MessageFormat.format(rb.getString(LogFacade.LISTENER_STOP_EXCEPTION),
+                                                  listener.getClass().getName());
+                getServletContext().log(msg, t);
+                ok = false;
+            }
+        }
+
+        contextListeners.clear();
+
+        return ok;
+    }
+
+    private void sessionListenerStop() {
+        for (HttpSessionListener listener : sessionListeners) {
+            // ServletContextListeners already had their PreDestroy called
+            if (!(listener instanceof ServletContextListener)) {
+                fireContainerEvent(ContainerEvent.PRE_DESTROY, listener);
+            }
+        }
+        sessionListeners.clear();
+    }
+
+    private boolean eventListenerStop() {
+        if (eventListeners.isEmpty()) {
+            return true;
+        }
+
+        Iterator<EventListener> iter = eventListeners.iterator();
+        while (iter.hasNext()) {
+            EventListener listener = iter.next();
+            // ServletContextListeners and HttpSessionListeners
+            // already had their PreDestroy called
+            if (listener instanceof ServletContextListener ||
+                    listener instanceof HttpSessionListener) {
+                continue;
+            }
+            fireContainerEvent(ContainerEvent.PRE_DESTROY, listener);
+        }
+
+        eventListeners.clear();
+
+        return true;
+    }
+
+    /**
+     * Merge the context initialization parameters specified in the application
+     * deployment descriptor with the application parameters described in the
+     * server configuration, respecting the <code>override</code> property of
+     * the application parameters appropriately.
+     */
+    private void mergeParameters() {
+        Map<String,String> mergedParams = new HashMap<>();
+
+        for (String name : findParameters()) {
+            mergedParams.put(name, findParameter(name));
+        }
+
+        for (ApplicationParameter param : findApplicationParameters()) {
+            if (param.getOverride()) {
+                if (mergedParams.get(param.getName()) == null)
+                    mergedParams.put(param.getName(), param.getValue());
+            } else {
+                mergedParams.put(param.getName(), param.getValue());
+            }
+        }
+
+        ServletContext sc = getServletContext();
+        for (Map.Entry<String,String> entry : mergedParams.entrySet()) {
+            sc.setInitParameter(entry.getKey(), entry.getValue());
+        }
+    }
+    
+    /**
+     * Allocate resources, including proxy.
+     * Return <code>true</code> if initialization was successfull,
+     * or <code>false</code> otherwise.
+     */
+    public boolean resourcesStart() {
+
+        boolean ok = true;
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        if(getParent() != null) {
+            env.put(ProxyDirContext.HOST, getParent().getName());
+        }
+        env.put(ProxyDirContext.CONTEXT, getName());
+        try {
+            ProxyDirContext proxyDirContext = new ProxyDirContext(env, webappResources);
+            if(webappResources instanceof BaseDirContext) {
+                ((BaseDirContext)webappResources).setDocBase(getBasePath(getDocBase()));
+                ((BaseDirContext)webappResources).allocate();
+            }
+            this.resources = proxyDirContext;
+        } catch(Throwable t) {
+            if(log.isLoggable(Level.FINE)) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.STARTING_RESOURCES_EXCEPTION), neutralizeForLog(getName()));
+                log.log(Level.SEVERE, msg, t);
+            } else {
+                log.log(Level.SEVERE, LogFacade.STARTING_RESOURCE_EXCEPTION_MESSAGE,
+                        new Object[] {neutralizeForLog(getName()), t.getMessage()});
+            }
+            ok = false;
+        }
+
+        return ok;
+    }
+
+    /**
+     * Starts this context's alternate doc base resources.
+     */
+    public void alternateResourcesStart() throws LifecycleException {
+
+        if (alternateDocBases == null || alternateDocBases.isEmpty()) {
+            return;
+        }
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        if (getParent() != null) {
+            env.put(ProxyDirContext.HOST, getParent().getName());
+        }
+        env.put(ProxyDirContext.CONTEXT, getName());
+        for(AlternateDocBase alternateDocBase : alternateDocBases) {
+            String basePath = alternateDocBase.getBasePath();
+            DirContext alternateWebappResources = ContextsAdapterUtility.unwrap(
+                    alternateDocBase.getWebappResources());
+            try {
+                ProxyDirContext proxyDirContext = new ProxyDirContext(env, alternateWebappResources);
+                if(alternateWebappResources instanceof BaseDirContext) {
+                    ((BaseDirContext)alternateWebappResources).setDocBase(basePath);
+                    ((BaseDirContext)alternateWebappResources).allocate();
+                }
+                alternateDocBase.setResources(ContextsAdapterUtility.wrap(proxyDirContext));
+            } catch(Throwable t) {
+                if(log.isLoggable(Level.FINE)) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.STARTING_RESOURCES_EXCEPTION), getName());
+                    throw new LifecycleException(msg, t);
+                } else {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.STARTING_RESOURCE_EXCEPTION_MESSAGE),
+                                                      new Object[] {getName(), t.getMessage()});
+                    throw new LifecycleException(msg);
+                }
+            }
+        }
+    }
+
+    /**
+     * Deallocate resources and destroy proxy.
+     */
+    public boolean resourcesStop() {
+
+        boolean ok = true;
+
+        try {
+            if (resources != null) {
+                if (resources instanceof Lifecycle) {
+                    ((Lifecycle) resources).stop();
+                }
+                if (webappResources instanceof BaseDirContext) {
+                    ((BaseDirContext) webappResources).release();
+                }
+            }
+        } catch (Throwable t) {
+            log.log(Level.SEVERE, LogFacade.STOPPING_RESOURCES_EXCEPTION, t);
+            ok = false;
+        }
+
+        this.resources = null;
+
+        return (ok);
+
+    }
+
+    /**
+     * Stops this context's alternate doc base resources.
+     */
+    public boolean alternateResourcesStop() {
+
+        boolean ok = true;
+
+        if (alternateDocBases == null || alternateDocBases.isEmpty()) {
+            return ok;
+        }
+        for(AlternateDocBase alternateDocBase : alternateDocBases) {
+            final DirContext alternateResources = ContextsAdapterUtility.unwrap(
+                    alternateDocBase.getResources());
+            if(alternateResources instanceof Lifecycle) {
+                try {
+                    ((Lifecycle)alternateResources).stop();
+                } catch(Throwable t) {
+                    log.log(Level.SEVERE, LogFacade.STOPPING_RESOURCES_EXCEPTION, t);
+                    ok = false;
+                }
+            }
+            final DirContext alternateWebappResources = ContextsAdapterUtility.unwrap(
+                alternateDocBase.getWebappResources());
+            if(alternateWebappResources instanceof BaseDirContext) {
+                try {
+                    ((BaseDirContext)alternateWebappResources).release();
+                } catch(Throwable t) {
+                    log.log(Level.SEVERE, LogFacade.STOPPING_RESOURCES_EXCEPTION, t);
+                    ok = false;
+                }
+            }
+        }
+
+        this.alternateDocBases = null;
+
+        return (ok);
+    }
+
+    /**
+     * Load and initialize all servlets marked "load on startup" in the
+     * web application deployment descriptor.
+     *
+     * @param children Array of wrappers for all currently defined
+     *  servlets (including those not declared load on startup)
+     */
+    /* SJSAS 6377790
+    public void loadOnStartup(Container children[]){
+    */
+    // START SJSAS 6377790
+    public void loadOnStartup(Container children[]) throws LifecycleException {
+    // END SJSAS 6377790
+        // Collect "load on startup" servlets that need to be initialized
+        Map<Integer, List<Wrapper>> map =
+            new TreeMap<Integer, List<Wrapper>>();
+        for (Container aChildren : children) {
+            Wrapper wrapper = (Wrapper)aChildren;
+            int loadOnStartup = wrapper.getLoadOnStartup();
+            if(loadOnStartup < 0) {
+                continue;
+            }
+            Integer key = loadOnStartup;
+            List<Wrapper> list = map.get(key);
+            if(list == null) {
+                list = new ArrayList<Wrapper>();
+                map.put(key, list);
+            }
+            list.add(wrapper);
+        }
+
+        // Load the collected "load on startup" servlets
+        for (List<Wrapper> list : map.values()) {
+            for(Wrapper wrapper : list) {
+                try {
+                    wrapper.load();
+                } catch(ServletException e) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_LOAD_EXCEPTION), neutralizeForLog(getName()));
+                    getServletContext().log(msg, StandardWrapper.getRootCause(e));
+                    // NOTE: load errors (including a servlet that throws
+                    // UnavailableException from the init() method) are NOT
+                    // fatal to application startup
+                    // START SJSAS 6377790
+                    throw new LifecycleException(
+                        StandardWrapper.getRootCause(e));
+                    // END SJSAS 6377790
+                }
+            }
+        }
+    }
+
+    /**
+     * Starts the session manager of this Context.
+     */
+    protected void managerStart() throws LifecycleException {
+        if ((manager != null) && (manager instanceof Lifecycle)) {
+            ((Lifecycle) getManager()).start();
+        }
+    }
+
+
+    /**
+     * Stops the session manager of this Context.
+     */
+    protected void managerStop() throws LifecycleException {
+        if ((manager != null) && (manager instanceof Lifecycle)) {
+            ((Lifecycle) manager).stop();
+        }
+    }
+
+    /**
+     * Start this Context component.
+     *
+     * @exception LifecycleException if a startup error occurs
+     */
+    @Override
+    public synchronized void start() throws LifecycleException {
+
+        if (started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.CONTAINER_ALREADY_STARTED_EXCEPTION, neutralizeForLog(logName()));
+            }
+            return;
+        }
+
+        long startupTimeStart = System.currentTimeMillis();
+
+        if(!initialized) {
+            try {
+                init();
+            } catch( Exception ex ) {
+                throw new LifecycleException("Error initializaing ", ex);
+            }
+        }
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Starting " +
+                    ("".equals(getName()) ? "ROOT" : neutralizeForLog(getName())));
+        }
+
+        // Set JMX object name for proper pipeline registration
+        preRegisterJMX();
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        setAvailable(false);
+        setConfigured(false);
+
+        // Add missing components as necessary
+        if (webappResources == null) {   // (1) Required by Loader
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Configuring default Resources");
+            }
+            try {
+                if ((docBase != null) && (docBase.endsWith(".war")) &&
+                        (!(new File(docBase).isDirectory())))
+                    setResources(new WARDirContext());
+                else
+                    setResources(new WebDirContext());
+            } catch (IllegalArgumentException e) {
+                throw new LifecycleException(rb.getString(LogFacade.INIT_RESOURCES_EXCEPTION), e);
+            }
+        }
+
+        resourcesStart();
+
+        // Add alternate resources
+        if (alternateDocBases != null && !alternateDocBases.isEmpty()) {
+            for(AlternateDocBase alternateDocBase : alternateDocBases) {
+                String docBase = alternateDocBase.getDocBase();
+                if(log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, "Configuring alternate resources");
+                }
+                try {
+                    if(docBase != null && docBase.endsWith(".war") &&
+                        (!(new File(docBase).isDirectory()))) {
+                        setAlternateResources(alternateDocBase,
+                            new WARDirContext());
+                    } else {
+                        setAlternateResources(alternateDocBase,
+                            new FileDirContext());
+                    }
+                } catch(IllegalArgumentException e) {
+                    throw new LifecycleException(rb.getString(LogFacade.INIT_RESOURCES_EXCEPTION), e);
+                }
+            }
+
+            alternateResourcesStart();
+        }
+
+        if (getLoader() == null) {
+            createLoader();
+        }
+
+        // Initialize character set mapper
+        getCharsetMapper();
+
+        // Post work directory
+        postWorkDirectory();
+
+        // Validate required extensions
+        try {
+            ExtensionValidator.validateApplication(getResources(), this);
+        } catch (IOException ioe) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DEPENDENCY_CHECK_EXCEPTION), this);
+            throw new LifecycleException(msg, ioe);
+        }
+
+        // Reading the "catalina.useNaming" environment variable
+        String useNamingProperty = System.getProperty("catalina.useNaming");
+        if ((useNamingProperty != null) &&
+                ("false".equals(useNamingProperty))) {
+            useNaming = false;
+        }
+
+        if (isUseNaming()) {
+            if (namingContextListener == null) {
+                namingContextListener = new NamingContextListener();
+                namingContextListener.setDebug(getDebug());
+                namingContextListener.setName(getNamingContextName());
+                addLifecycleListener(namingContextListener);
+            }
+        }
+
+        // Binding thread
+        // START OF SJSAS 8.1 6174179
+        //ClassLoader oldCCL = bindThread();
+        ClassLoader oldCCL = null;
+        // END OF SJSAS 8.1 6174179
+
+        try {
+            started = true;
+
+            // Start our subordinate components, if any
+            if ((loader != null) && (loader instanceof Lifecycle))
+                ((Lifecycle) loader).start();
+            if ((logger != null) && (logger instanceof Lifecycle))
+                ((Lifecycle) logger).start();
+
+            // Unbinding thread
+            // START OF SJSAS 8.1 6174179
+            //unbindThread(oldCCL);
+            // END OF SJSAS 8.1 6174179
+
+            // Binding thread
+            oldCCL = bindThread();
+
+            if ((realm != null) && (realm instanceof Lifecycle))
+                ((Lifecycle) realm).start();
+            if ((resources != null) && (resources instanceof Lifecycle))
+                ((Lifecycle) resources).start();
+
+            // Start our child containers, if any
+            for (Container child : findChildren()) {
+                if(child instanceof Lifecycle) {
+                    ((Lifecycle)child).start();
+                }
+            }
+
+            // Start the Valves in our pipeline (including the basic),
+            // if any
+            if (pipeline instanceof Lifecycle)
+                ((Lifecycle) pipeline).start();
+
+            // START SJSAS 8.1 5049111
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(START_EVENT, null);
+            // END SJSAS 8.1 5049111
+        } catch (Throwable t) {
+            throw new LifecycleException(t);
+        } finally {
+            // Unbinding thread
+            unbindThread(oldCCL);
+        }
+
+        if (!getConfigured()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.STARTUP_CONTEXT_FAILED_EXCEPTION), getName());
+            throw new LifecycleException(msg);
+        }
+
+        // Store some required info as ServletContext attributes
+        postResources();
+        if (orderedLibs != null && !orderedLibs.isEmpty()) {
+            getServletContext().setAttribute(ServletContext.ORDERED_LIBS,
+                orderedLibs);
+            context.setAttributeReadOnly(ServletContext.ORDERED_LIBS);
+        }
+
+        // Initialize associated mapper
+        mapper.setContext(getPath(), welcomeFiles, ContextsAdapterUtility.wrap(resources));
+
+        // Binding thread
+        oldCCL = bindThread();
+
+        try {
+            // Set up the context init params
+            mergeParameters();
+
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+            // Support for pluggability : this has to be done before
+            // listener events are fired
+            callServletContainerInitializers();
+
+            // Configure and call application event listeners
+            contextListenerStart();
+
+            // Start manager
+            if ((manager != null) && (manager instanceof Lifecycle)) {
+                ((Lifecycle) getManager()).start();
+            }
+
+            // Start ContainerBackgroundProcessor thread
+            super.threadStart();
+
+            // Configure and call application filters
+            filterStart();
+
+            // Load and initialize all "load on startup" servlets
+            loadOnStartup(findChildren());
+        } catch (Throwable t) {
+            log.log(Level.SEVERE, LogFacade.STARTUP_CONTEXT_FAILED_EXCEPTION, getName());
+            try {
+                stop();
+            } catch (Throwable tt) {
+                log.log(Level.SEVERE, LogFacade.CLEANUP_FAILED_EXCEPTION, tt);
+            }
+            throw new LifecycleException(t);
+        } finally {
+            // Unbinding thread
+            unbindThread(oldCCL);
+        }
+
+        // Set available status depending upon startup success
+        if (log.isLoggable(Level.FINEST)) {
+            log.log(Level.FINEST, "Startup successfully completed");
+        }
+
+        setAvailable(true);
+
+        // JMX registration
+        registerJMX();
+
+        startTimeMillis = System.currentTimeMillis();
+        startupTime = startTimeMillis - startupTimeStart;
+
+        // Send j2ee.state.running notification
+        if (getObjectName() != null) {
+            Notification notification =
+                new Notification("j2ee.state.running", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+
+        // Close all JARs right away to avoid always opening a peak number
+        // of files on startup
+        if (getLoader() instanceof WebappLoader) {
+            ((WebappLoader) getLoader()).closeJARs(true);
+        }
+    }
+
+    protected Types getTypes() {
+        return null;
+    }
+
+    protected void callServletContainerInitializers()
+            throws LifecycleException {
+
+        // Get the list of ServletContainerInitializers and the classes
+        // they are interested in
+        Map<Class<?>, List<Class<? extends ServletContainerInitializer>>> interestList =
+            ServletContainerInitializerUtil.getInterestList(
+                servletContainerInitializers);
+        Map<Class<? extends ServletContainerInitializer>, Set<Class<?>>> initializerList =
+            ServletContainerInitializerUtil.getInitializerList(
+                servletContainerInitializers, interestList,
+                getTypes(),
+                getClassLoader());
+        if (initializerList == null) {
+            return;
+        }
+
+        // Allow programmatic registration of ServletContextListeners, but
+        // only within the scope of ServletContainerInitializer#onStartup
+        isProgrammaticServletContextListenerRegistrationAllowed = true;
+
+        // We have the list of initializers and the classes that satisfy
+        // the condition. Time to call the initializers
+        ServletContext ctxt = this.getServletContext();
+        try {
+            for (Map.Entry<Class<? extends ServletContainerInitializer>, Set<Class<?>>> e :
+                    initializerList.entrySet()) {
+                Class<? extends ServletContainerInitializer> initializer = e.getKey();
+                // See IT 11333
+                if (isUseMyFaces() &&
+                        Globals.FACES_INITIALIZER.equals(initializer.getName())) {
+                    continue;
+                }
+                try {
+                    if (log.isLoggable(Level.FINE)) {
+                        log.log(Level.FINE, "Calling ServletContainerInitializer [" + initializer
+                                + "] onStartup with classes " + e.getValue());
+                    }
+                    ServletContainerInitializer iniInstance =
+                        initializer.newInstance();
+                    fireContainerEvent(ContainerEvent.BEFORE_CONTEXT_INITIALIZER_ON_STARTUP,
+                        iniInstance);
+                    iniInstance.onStartup(
+                        initializerList.get(initializer), ctxt);
+                    fireContainerEvent(ContainerEvent.AFTER_CONTEXT_INITIALIZER_ON_STARTUP,
+                        iniInstance);
+                } catch (Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.INVOKING_SERVLET_CONTAINER_INIT_EXCEPTION),
+                                                      initializer.getCanonicalName());
+                    log.log(Level.SEVERE, msg, t);
+                    throw new LifecycleException(t);
+                }
+            }
+        } finally {
+            isProgrammaticServletContextListenerRegistrationAllowed = false;
+        }
+    }
+
+    public void setServletContainerInitializerInterestList(
+            Iterable<ServletContainerInitializer> initializers)
+ {
+        servletContainerInitializers = initializers;
+    }
+
+    /**
+     * Creates a classloader for this context.
+     */
+    public void createLoader() {
+        ClassLoader parent = null;
+        if (getPrivileged()) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Configuring privileged default Loader");
+            }
+            parent = this.getClass().getClassLoader();
+        } else {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Configuring non-privileged default Loader");
+            }
+            parent = getParentClassLoader();
+        }
+        WebappLoader webappLoader = new WebappLoader(parent);
+        webappLoader.setDelegate(getDelegate());
+        webappLoader.setUseMyFaces(useMyFaces);
+        setLoader(webappLoader);
+    }
+
+    /**
+     * Stop this Context component.
+     *
+     * @exception LifecycleException if a shutdown error occurs
+     */
+    @Override
+    public synchronized void stop() throws LifecycleException {
+        stop(false);
+    }
+
+    /**
+     * Stop this Context component.
+     *
+     * @param isShutdown true if this Context is being stopped as part
+     * of a domain shutdown (as opposed to an undeployment), and false otherwise
+     * @exception LifecycleException if a shutdown error occurs
+     */
+    public synchronized void stop(boolean isShutdown)
+            throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started) {
+            if(log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.CONTAINER_NOT_STARTED_EXCEPTION, logName());
+            }
+            return;
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        // Send j2ee.state.stopping notification
+        if (this.getObjectName() != null) {
+            Notification notification =
+                new Notification("j2ee.state.stopping", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+
+        // Mark this application as unavailable while we shut down
+        setAvailable(false);
+
+        // Binding thread
+        ClassLoader oldCCL = bindThread();
+
+        try {
+            // Stop our child containers, if any
+            for (Container child : findChildren()) {
+                if(child instanceof Lifecycle) {
+                    ((Lifecycle)child).stop();
+                }
+            }
+
+            // Stop our filters
+            filterStop();
+
+            // Stop ContainerBackgroundProcessor thread
+            super.threadStop();
+
+            if ((manager != null) && (manager instanceof Lifecycle)) {
+                if(manager instanceof StandardManager) {
+                    ((StandardManager)manager).stop(isShutdown);
+                } else {
+                    ((Lifecycle)manager).stop();
+                }
+            }
+
+            /*
+             * Stop all ServletContextListeners. It is important that they
+             * are passed a ServletContext to their contextDestroyed() method
+             * that still has all its attributes set. In other words, it is
+             * important that we invoke these listeners before calling
+             * context.clearAttributes()
+             */
+            contextListenerStop();
+
+            sessionListenerStop();
+
+            // Clear all application-originated servlet context attributes
+            if (context != null) {
+                context.clearAttributes();
+            }
+
+            /*
+             * Stop all event listeners, including those of type
+             * ServletContextAttributeListener. For the latter, it is
+             * important that we invoke them after calling
+             * context.clearAttributes, so that they receive the corresponding
+             * attribute removal events
+             */
+            eventListenerStop();
+
+            // Notify our interested LifecycleListeners
+            lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+            started = false;
+
+            // Stop the Valves in our pipeline (including the basic), if any
+            if (pipeline instanceof Lifecycle) {
+                ((Lifecycle) pipeline).stop();
+            }
+
+            // Finalize our character set mapper
+            setCharsetMapper(null);
+
+            // Stop resources
+            resourcesStop();
+            alternateResourcesStop();
+
+            if ((realm != null) && (realm instanceof Lifecycle)) {
+                ((Lifecycle) realm).stop();
+            }
+            if ((logger != null) && (logger instanceof Lifecycle)) {
+                ((Lifecycle) logger).stop();
+            }
+            /* SJSAS 6347606
+            if ((loader != null) && (loader instanceof Lifecycle)) {
+                ((Lifecycle) loader).stop();
+            }
+            */
+        } catch(Throwable t) {
+            // started was "true" when it first enters the try block.
+            // Note that it is set to false after STOP_EVENT is fired.
+            // One need to fire STOP_EVENT to clean up naming information
+            // if START_EVENT is processed successfully.
+            if (started) {
+                lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+            }
+
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException)t;
+            } else if (t instanceof LifecycleException) {
+                throw (LifecycleException)t;
+            } else {
+                throw new LifecycleException(t);
+            }
+        } finally {
+
+            // Unbinding thread
+            unbindThread(oldCCL);
+
+            // START SJSAS 6347606
+            /*
+             * Delay the stopping of the webapp classloader until this point,
+             * because unbindThread() calls the security-checked
+             * Thread.setContextClassLoader(), which may ask the current thread
+             * context classloader (i.e., the webapp classloader) to load
+             * Principal classes specified in the security policy file
+             */
+            if ((loader != null) && (loader instanceof Lifecycle)) {
+                ((Lifecycle) loader).stop();
+            }
+            // END SJSAS 6347606
+        }
+
+        // Send j2ee.state.stopped notification
+        if (this.getObjectName() != null) {
+            Notification notification =
+                new Notification("j2ee.state.stopped", this ,sequenceNumber++);
+            sendNotification(notification);
+        }
+
+        // Reset application context
+        context = null;
+
+        // This object will no longer be visible or used.
+        try {
+            resetContext();
+        } catch( Exception ex ) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.RESETTING_CONTEXT_EXCEPTION), this);
+            log.log(Level.SEVERE, msg, ex);
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Stopping complete");
+
+        if(oname != null) {
+            // Send j2ee.object.deleted notification
+            Notification notification =
+                    new Notification("j2ee.object.deleted", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+
+    }
+
+    /**
+     * Destroys this context by cleaning it up completely.
+     *
+     * The problem is that undoing all the config in start() and restoring
+     * a 'fresh' state is impossible. After stop()/destroy()/init()/start()
+     * we should have the same state as if a fresh start was done - i.e
+     * read modified web.xml, etc. This can only be done by completely
+     * removing the context object and remapping a new one, or by cleaning
+     * up everything.
+     *
+     * XXX  Should this be done in stop() ?
+     */
+    @Override
+    public void destroy() throws Exception {
+        super.destroy();
+
+        // START SJASAS 6359401
+        // super.destroy() will stop session manager and cause it to unload
+        // all its active sessions into a file. Delete this file, because this
+        // context is being destroyed and must not leave any traces.
+        if (getManager() instanceof ManagerBase) {
+            ((ManagerBase)getManager()).release();
+        }
+        // END SJSAS 6359401
+
+        instanceListeners.clear();
+        instanceListenerInstances.clear();
+    }
+
+    private void resetContext() throws Exception, MBeanRegistrationException {
+        // Restore the original state ( pre reading web.xml in start )
+        // If you extend this - override this method and make sure to clean up
+        children=new HashMap<String, Container>();
+        startupTime = 0;
+        startTimeMillis = 0;
+        tldScanTime = 0;
+
+        // Bugzilla 32867
+        distributable = false;
+
+        eventListeners.clear();
+        contextListeners.clear();
+        sessionListeners.clear();
+
+        requestCharacterEncoding = null;
+        responseCharacterEncoding = DEFAULT_RESPONSE_CHARACTER_ENCODING;
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "resetContext " + oname);
+        }
+    }
+
+    /**
+     * Return a String representation of this component.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (getParent() != null) {
+            sb.append(getParent().toString());
+            sb.append(".");
+        }
+        sb.append("StandardContext[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    @Override
+    public void backgroundProcess() {
+
+        if (!started)
+            return;
+
+        count = (count + 1) % managerChecksFrequency;
+
+        if ((getManager() != null) && (count == 0)) {
+            if (getManager() instanceof StandardManager) {
+                ((StandardManager) getManager()).processExpires();
+            } else if (getManager() instanceof PersistentManagerBase) {
+                PersistentManagerBase pManager =
+                    (PersistentManagerBase) getManager();
+                pManager.backgroundProcess();
+            }
+        }
+
+        // START S1AS8PE 4965017
+        if (isReload()) {
+            if (getLoader() != null) {
+                if (reloadable && (getLoader().modified())) {
+                    try {
+                        Thread.currentThread().setContextClassLoader
+                            (standardContextClassLoader);
+                        reload();
+                    } finally {
+                        if (getLoader() != null) {
+                            Thread.currentThread().setContextClassLoader
+                                (getClassLoader());
+                        }
+                    }
+                }
+                if (getLoader() instanceof WebappLoader) {
+                    ((WebappLoader) getLoader()).closeJARs(false);
+                }
+            }
+        }
+        // END S1AS8PE 4965017
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * Adjust the URL pattern to begin with a leading slash, if appropriate
+     * (i.e. we are running a servlet 2.2 application).  Otherwise, return
+     * the specified URL pattern unchanged.
+     *
+     * @param urlPattern The URL pattern to be adjusted (if needed)
+     *  and returned
+     */
+    protected String adjustURLPattern(String urlPattern) {
+
+        if (urlPattern == null)
+            return (urlPattern);
+        if (urlPattern.startsWith("/") || urlPattern.startsWith("*."))
+            return (urlPattern);
+        if (!isServlet22())
+            return (urlPattern);
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, LogFacade.URL_PATTERN_WARNING, urlPattern);
+        }
+        return ("/" + urlPattern);
+
+    }
+
+    /**
+     * Are we processing a version 2.2 deployment descriptor?
+     */
+    protected boolean isServlet22() {
+        return publicId != null && publicId.equals(
+            org.apache.catalina.startup.Constants.WebDtdPublicId_22);
+    }
+
+    /**
+     * Return a File object representing the base directory for the
+     * entire servlet container (i.e. the Engine container if present).
+     */
+    protected File engineBase() {
+        String base=System.getProperty("catalina.base");
+        if( base == null ) {
+            StandardEngine eng=(StandardEngine)this.getParent().getParent();
+            base=eng.getBaseDir();
+        }
+        return (new File(base));
+    }
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Bind current thread, both for CL purposes and for JNDI ENC support
+     * during : startup, shutdown and realoading of the context.
+     *
+     * @return the previous context class loader
+     */
+    private ClassLoader bindThread() {
+        ClassLoader oldContextClassLoader =
+            Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(getClassLoader());
+        if (isUseNaming()) {
+            try {
+                ContextBindings.bindThread(this, this);
+            } catch (Throwable e) {
+                log.log(Level.WARNING, LogFacade.BIND_THREAD_EXCEPTION, e);
+            }
+        }
+
+        return oldContextClassLoader;
+
+    }
+
+    /**
+     * Unbind thread.
+     */
+    private void unbindThread(ClassLoader oldContextClassLoader) {
+        Thread.currentThread().setContextClassLoader(oldContextClassLoader);
+        if (isUseNaming()) {
+            ContextBindings.unbindThread(this, this);
+        }
+    }
+
+    /**
+     * Get base path.
+     */
+    private String getBasePath(String docBase) {
+        String basePath = null;
+        Container container = this;
+        while (container != null) {
+            if (container instanceof Host)
+                break;
+            container = container.getParent();
+        }
+        File file = new File(docBase);
+        if (!file.isAbsolute()) {
+            if (container == null) {
+                basePath = (new File(engineBase(), docBase)).getPath();
+            } else {
+                // Use the "appBase" property of this container
+                String appBase = ((Host) container).getAppBase();
+                file = new File(appBase);
+                if (!file.isAbsolute())
+                    file = new File(engineBase(), appBase);
+                basePath = (new File(file, docBase)).getPath();
+            }
+        } else {
+            basePath = file.getPath();
+        }
+        return basePath;
+    }
+
+    /**
+     * Get app base.
+     *
+    private String getAppBase() {
+        String appBase = null;
+        Container container = this;
+        while (container != null) {
+            if (container instanceof Host)
+                break;
+            container = container.getParent();
+        }
+        if (container != null) {
+            appBase = ((Host) container).getAppBase();
+        }
+        return appBase;
+    }
+
+    /**
+     * Get config base.
+     *
+    private File getConfigBase() {
+        File configBase =
+            new File(System.getProperty("catalina.base"), "conf");
+        if (!configBase.exists()) {
+            return null;
+        }
+        Container container = this;
+        Container host = null;
+        Container engine = null;
+        while (container != null) {
+            if (container instanceof Host)
+                host = container;
+            if (container instanceof Engine)
+                engine = container;
+            container = container.getParent();
+        }
+        if (engine != null) {
+            configBase = new File(configBase, engine.getName());
+        }
+        if (host != null) {
+            configBase = new File(configBase, host.getName());
+        }
+        configBase.mkdirs();
+        return configBase;
+    }
+    */
+
+    /**
+     * Given a context path, get the config file name.
+     */
+    protected String getDefaultConfigFile() {
+        String basename = null;
+        String path = getPath();
+        if ("".equals(path)) {
+            basename = "ROOT";
+        } else {
+            basename = path.substring(1).replace('/', '_');
+        }
+        return (basename + ".xml");
+    }
+
+    /**
+     * Copy a file.
+     *
+    private boolean copy(File src, File dest) {
+        FileInputStream is = null;
+        FileOutputStream os = null;
+        try {
+            is = new FileInputStream(src);
+            os = new FileOutputStream(dest);
+            byte[] buf = new byte[4096];
+            while (true) {
+                int len = is.read(buf);
+                if (len < 0)
+                    break;
+                os.write(buf, 0, len);
+            }
+            is.close();
+            os.close();
+        } catch (IOException e) {
+            return false;
+        } finally {
+            try {
+                if (is != null) {
+                    is.close();
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+            try {
+                if (os != null) {
+                    os.close();
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+        return true;
+    }
+    */
+
+
+    /**
+     * Get naming context full name.
+     */
+    public String getNamingContextName() {
+        if (namingContextName == null) {
+            Container parent = getParent();
+            if (parent == null) {
+                namingContextName = getName();
+            } else {
+                Stack<String> stk = new Stack<String>();
+                StringBuilder buff = new StringBuilder();
+                while (parent != null) {
+                    stk.push(parent.getName());
+                    parent = parent.getParent();
+                }
+                while (!stk.empty()) {
+                    buff.append("/").append(stk.pop());
+                }
+                buff.append(getName());
+                namingContextName = buff.toString();
+            }
+            // START RIMOD 4868393
+            // append an id to make the name unique to the instance.
+            namingContextName += instanceIDCounter.getAndIncrement();
+            // END RIMOD 4868393
+        }
+        return namingContextName;
+    }
+
+    /**
+     * @return the request processing paused flag for this Context
+     */
+    public boolean getPaused() {
+        return paused;
+    }
+
+    /**
+     * Stores the resources of this application as ServletContext
+     * attributes.
+     */
+    private void postResources() {
+        getServletContext().setAttribute(
+            Globals.RESOURCES_ATTR, getResources());
+        context.setAttributeReadOnly(Globals.RESOURCES_ATTR);
+        getServletContext().setAttribute(
+            Globals.ALTERNATE_RESOURCES_ATTR, getAlternateDocBases());
+        context.setAttributeReadOnly(Globals.ALTERNATE_RESOURCES_ATTR);
+    }
+
+    public String getHostname() {
+        Container parentHost = getParent();
+        if (parentHost != null) {
+            hostName = parentHost.getName();
+        }
+        if ((hostName == null) || (hostName.length() < 1))
+            hostName = "_";
+        return hostName;
+    }
+
+    /**
+     * Set the appropriate context attribute for our work directory.
+     */
+    private void postWorkDirectory() {
+
+        // Acquire (or calculate) the work directory path
+        String workDir = getWorkDir();
+        if (workDir == null || workDir.length() == 0) {
+
+            // Retrieve our parent (normally a host) name
+            String hostName = null;
+            String engineName = null;
+            String hostWorkDir = null;
+            Container parentHost = getParent();
+            if (parentHost != null) {
+                hostName = parentHost.getName();
+                if (parentHost instanceof StandardHost) {
+                    hostWorkDir = ((StandardHost)parentHost).getWorkDir();
+                }
+                Container parentEngine = parentHost.getParent();
+                if (parentEngine != null) {
+                   engineName = parentEngine.getName();
+                }
+            }
+            if ((hostName == null) || (hostName.length() < 1))
+                hostName = "_";
+            if ((engineName == null) || (engineName.length() < 1))
+                engineName = "_";
+
+            String temp = getPath();
+            if (temp.startsWith("/"))
+                temp = temp.substring(1);
+            temp = temp.replace('/', '_');
+            temp = temp.replace('\\', '_');
+            if (temp.length() < 1)
+                temp = "_";
+            if (hostWorkDir != null ) {
+                workDir = hostWorkDir + File.separator + temp;
+            } else {
+                workDir = "work" + File.separator + engineName +
+                    File.separator + hostName + File.separator + temp;
+            }
+            setWorkDir(workDir);
+        }
+
+        // Create this directory if necessary
+        File dir = new File(workDir);
+        if (!dir.isAbsolute()) {
+            File catalinaHome = engineBase();
+            String catalinaHomePath = null;
+            try {
+                catalinaHomePath = catalinaHome.getCanonicalPath();
+                dir = new File(catalinaHomePath, workDir);
+            } catch (IOException e) {
+            }
+        }
+        if (!dir.mkdirs() && !dir.isDirectory()) {
+            log.log(Level.SEVERE, LogFacade.CREATE_WORK_DIR_EXCEPTION, dir.getAbsolutePath());
+        }
+
+        // Set the appropriate servlet context attribute
+        getServletContext().setAttribute(ServletContext.TEMPDIR, dir);
+        context.setAttributeReadOnly(ServletContext.TEMPDIR);
+
+    }
+
+    /**
+     * Set the request processing paused flag for this Context.
+     *
+     * @param paused The new request processing paused flag
+     */
+    private void setPaused(boolean paused) {
+        this.paused = paused;
+    }
+
+    /**
+     * Validate the syntax of a proposed <code>&lt;url-pattern&gt;</code>
+     * for conformance with specification requirements.
+     *
+     * @param urlPattern URL pattern to be validated
+     */
+    protected boolean validateURLPattern(String urlPattern) {
+        if (urlPattern == null) {
+            return false;
+        }
+        if (urlPattern.isEmpty()) {
+            return true;
+        }
+        if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) {
+            log.log(Level.WARNING, LogFacade.URL_PATTERN_CANNOT_BE_MATCHED_EXCEPTION, urlPattern);
+            return false;
+        }
+        if (urlPattern.startsWith("*.")) {
+            if (urlPattern.indexOf('/') < 0) {
+                checkUnusualURLPattern(urlPattern);
+                return true;
+            } else
+                return false;
+        }
+        if ( (urlPattern.startsWith("/")) &&
+                (!urlPattern.contains("*."))) {
+            checkUnusualURLPattern(urlPattern);
+            return true;
+        } else
+            return false;
+
+    }
+
+
+    /**
+     * Check for unusual but valid <code>&lt;url-pattern&gt;</code>s.
+     * See Bugzilla 34805, 43079 &amp; 43080
+     */
+    private void checkUnusualURLPattern(String urlPattern) {
+        if (log.isLoggable(Level.INFO)) {
+            if(urlPattern.endsWith("*") && (urlPattern.length() < 2 ||
+                    urlPattern.charAt(urlPattern.length()-2) != '/')) {
+                String msg = "Suspicious url pattern: \"" + urlPattern + "\"" +
+                             " in context [" + getName() + "] - see" +
+                             " section SRV.11.2 of the Servlet specification";
+                log.log(Level.INFO, msg);
+            }
+        }
+    }
+
+
+    // -------------------- JMX methods  --------------------
+
+    /**
+     * Return the MBean Names of the set of defined environment entries for
+     * this web application
+     */
+    public String[] getEnvironments() {
+        ContextEnvironment[] envs = getNamingResources().findEnvironments();
+        List<String> results = new ArrayList<String>();
+        for(ContextEnvironment env : envs) {
+            try {
+                ObjectName oname = createObjectName(env);
+                results.add(oname.toString());
+            } catch(MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for environment " + env);
+                iae.initCause(e);
+                throw iae;
+            }
+        }
+        return results.toArray(new String[results.size()]);
+
+    }
+
+
+    /**
+     * Return the MBean Names of all the defined resource references for this
+     * application.
+     */
+    public String[] getResourceNames() {
+
+        ContextResource[] resources = getNamingResources().findResources();
+        List<String> results = new ArrayList<String>();
+        for(ContextResource resource : resources) {
+            try {
+                ObjectName oname = createObjectName(resource);
+                results.add(oname.toString());
+            } catch(MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for resource " + resource);
+                iae.initCause(e);
+                throw iae;
+            }
+        }
+        return results.toArray(new String[results.size()]);
+
+    }
+
+    /**
+     * Return the MBean Names of all the defined resource links for this
+     * application
+     */
+    public String[] getResourceLinks() {
+
+        ContextResourceLink[] links = getNamingResources().findResourceLinks();
+        List<String> results = new ArrayList<String>();
+        for(ContextResourceLink link : links) {
+            try {
+                ObjectName oname = createObjectName(link);
+                results.add(oname.toString());
+            } catch(MalformedObjectNameException e) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("Cannot create object name for resource " + link);
+                iae.initCause(e);
+                throw iae;
+            }
+        }
+        return results.toArray(new String[results.size()]);
+
+    }
+
+    // ------------------------------------------------------------- Operations
+
+
+    /**
+     * Add an environment entry for this web application.
+     *
+     * @param envName New environment entry name
+     */
+    public String addEnvironment(String envName, String type)
+        throws MalformedObjectNameException {
+
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextEnvironment env = nresources.findEnvironment(envName);
+        if (env != null) {
+            throw new IllegalArgumentException
+                ("Invalid environment name - already exists '" + envName + "'");
+        }
+        env = new ContextEnvironment();
+        env.setName(envName);
+        env.setType(type);
+        nresources.addEnvironment(env);
+
+        // Return the corresponding MBean name
+        return createObjectName(env).toString();
+
+    }
+
+
+    /**
+     * Add a resource reference for this web application.
+     *
+     * @param resourceName New resource reference name
+     */
+    public String addResource(String resourceName, String type)
+        throws MalformedObjectNameException {
+
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextResource resource = nresources.findResource(resourceName);
+        if (resource != null) {
+            throw new IllegalArgumentException
+                ("Invalid resource name - already exists'" + resourceName + "'");
+        }
+        resource = new ContextResource();
+        resource.setName(resourceName);
+        resource.setType(type);
+        nresources.addResource(resource);
+
+        // Return the corresponding MBean name
+        return createObjectName(resource).toString();
+    }
+
+    /**
+     * Add a resource link for this web application.
+     *
+     * @param resourceLinkName New resource link name
+     */
+    public String addResourceLink(String resourceLinkName, String global,
+                String name, String type) throws MalformedObjectNameException {
+
+        NamingResources nresources = getNamingResources();
+        if (nresources == null) {
+            return null;
+        }
+        ContextResourceLink resourceLink =
+                                nresources.findResourceLink(resourceLinkName);
+        if (resourceLink != null) {
+            throw new IllegalArgumentException
+                ("Invalid resource link name - already exists'" +
+                                                        resourceLinkName + "'");
+        }
+        resourceLink = new ContextResourceLink();
+        resourceLink.setGlobal(global);
+        resourceLink.setName(resourceLinkName);
+        resourceLink.setType(type);
+        nresources.addResourceLink(resourceLink);
+
+        // Return the corresponding MBean name
+        return createObjectName(resourceLink).toString();
+    }
+
+    @Override
+    public ObjectName createObjectName(String hostDomain, ObjectName parentName)
+            throws MalformedObjectNameException
+    {
+        String onameStr;
+        StandardHost hst=(StandardHost)getParent();
+
+        String hostName=getParent().getName();
+        String name= "//" + ((hostName==null)? "DEFAULT" : hostName) +
+                (("".equals(encodedPath)) ? "/" : encodedPath);
+
+        String suffix=",J2EEApplication=" +
+                getJ2EEApplication() + ",J2EEServer=" +
+                getJ2EEServer();
+
+        onameStr="j2eeType=WebModule,name=" + name + suffix;
+        if( log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Registering " + onameStr + " for " + oname);
+
+        // default case - no domain explictely set.
+        if( getDomain() == null ) domain=hst.getDomain();
+        return new ObjectName(getDomain() + ":" + onameStr);
+    }
+
+
+
+    private void preRegisterJMX() {
+        try {
+            StandardHost host = (StandardHost) getParent();
+              if ((oname == null)
+                      || (oname.getKeyProperty("j2eeType") == null)) {
+                  oname = createObjectName(host.getDomain(), host.getJmxName());
+                  controller = oname;
+              }
+        } catch(Exception ex) {
+            if (log.isLoggable(Level.INFO)) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_UPDATING_CTX_INFO),
+                        new Object[] {this, oname, ex.toString()});
+                log.log(Level.INFO, msg, ex);
+            }
+        }
+    }
+
+    private void registerJMX() {
+        try {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Checking for " + oname);
+            }
+            controller = oname;
+             // Send j2ee.object.created notification
+             if (this.getObjectName() != null) {
+                 Notification notification = new Notification(
+                         "j2ee.object.created", this, sequenceNumber++);
+                 sendNotification(notification);
+            }
+            for (Container child : findChildren()) {
+                ((StandardWrapper)child).registerJMX( this );
+            }
+        } catch (Exception ex) {
+
+            String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_REGISTERING_WRAPPER_INFO),
+                                              new Object[] {this, oname, ex.toString()});
+            log.log(Level.INFO, msg, ex);
+        }
+    }
+
+
+    public void sendNotification(Notification notification) {
+
+        if (broadcaster == null) {
+            broadcaster = ((StandardEngine)getParent().getParent()).getService().getBroadcaster();
+        }
+        if (broadcaster != null) {
+            broadcaster.sendNotification(notification);
+        }
+        return;
+    }
+
+
+    @Override
+    public void init() throws Exception {
+
+        if( this.getParent() == null ) {
+
+            ContextConfig config = new ContextConfig();
+            this.addLifecycleListener(config);
+        }
+
+        // It's possible that addChild may have started us
+        if( initialized ) {
+            return;
+        }
+
+        super.init();
+
+        // START GlassFish 2439
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(INIT_EVENT, null);
+        // END GlassFish 2439
+
+        // Send j2ee.state.starting notification
+        if (this.getObjectName() != null) {
+            Notification notification = new Notification("j2ee.state.starting", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+
+    }
+
+    @Override
+    public ObjectName getParentName() throws MalformedObjectNameException {
+        // "Life" update
+        String path=oname.getKeyProperty("name");
+        if( path == null ) {
+            log.log(Level.SEVERE, LogFacade.MISSING_ATTRIBUTE, getName());
+            return null;
+        }
+        if( ! path.startsWith( "//")) {
+            log.log(Level.SEVERE, LogFacade.MALFORMED_NAME, getName());
+        }
+        path=path.substring(2);
+        int delim=path.indexOf( "/" );
+        hostName="localhost"; // Should be default...
+        if( delim > 0 ) {
+            hostName=path.substring(0, delim);
+            path = path.substring(delim);
+            if ("/".equals(path)) {
+                this.setName("");
+            } else {
+                this.setName(path);
+            }
+        } else {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Setting path " + path);
+            }
+            this.setName( path );
+        }
+        // XXX  The service and domain should be the same.
+        String parentDomain=getEngineName();
+        if( parentDomain == null ) parentDomain=domain;
+        return new ObjectName( parentDomain + ":" +
+                "type=Host,host=" + hostName);
+    }
+
+    public void create() throws Exception{
+        init();
+    }
+
+
+
+    /**
+     * Create an <code>ObjectName</code> for <code>ContextEnvironment</code> object.
+     *
+     * @param environment The ContextEnvironment to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    public ObjectName createObjectName(ContextEnvironment environment)
+            throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        Object container =
+                environment.getNamingResources().getContainer();
+        if (container instanceof Server) {
+            name = new ObjectName(domain + ":type=Environment" +
+                    ",resourcetype=Global,name=" + environment.getName());
+        } else if (container instanceof Context) {
+            String path = ((Context)container).getPath();
+            if (path.length() < 1)
+                path = "/";
+            Host host = (Host) ((Context)container).getParent();
+            name = new ObjectName(domain + ":type=Environment" +
+                    ",resourcetype=Context,path=" + path +
+                    ",host=" + host.getName() +
+                    ",name=" + environment.getName());
+        }
+
+        return (name);
+
+    }
+
+    /**
+     * Create an <code>ObjectName</code> for <code>ContextResource</code> object.
+     *
+     * @param resource The ContextResource to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    public ObjectName createObjectName(ContextResource resource)
+            throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        String encodedResourceName = urlEncoder.encode(resource.getName());
+        Object container =
+                resource.getNamingResources().getContainer();
+        if (container instanceof Server) {
+            name = new ObjectName(domain + ":type=Resource" +
+                    ",resourcetype=Global,class=" + resource.getType() +
+                    ",name=" + encodedResourceName);
+        } else if (container instanceof Context) {
+            String path = ((Context)container).getPath();
+            if (path.length() < 1)
+                path = "/";
+            Host host = (Host) ((Context)container).getParent();
+            name = new ObjectName(domain + ":type=Resource" +
+                    ",resourcetype=Context,path=" + path +
+                    ",host=" + host.getName() +
+                    ",class=" + resource.getType() +
+                    ",name=" + encodedResourceName);
+        }
+
+        return (name);
+
+    }
+
+
+    /**
+     * Create an <code>ObjectName</code> for <code>ContextResourceLink</code> object.
+     *
+     * @param resourceLink The ContextResourceLink to be named
+     *
+     * @exception MalformedObjectNameException if a name cannot be created
+     */
+    public ObjectName createObjectName(ContextResourceLink resourceLink)
+            throws MalformedObjectNameException {
+
+        ObjectName name = null;
+        String encodedResourceLinkName = urlEncoder.encode(resourceLink.getName());
+        Object container =
+                resourceLink.getNamingResources().getContainer();
+        if (container instanceof Server) {
+            name = new ObjectName(domain + ":type=ResourceLink" +
+                    ",resourcetype=Global" +
+                    ",name=" + encodedResourceLinkName);
+        } else if (container instanceof Context) {
+            String path = ((Context)container).getPath();
+            if (path.length() < 1)
+                path = "/";
+            Host host = (Host) ((Context)container).getParent();
+            name = new ObjectName(domain + ":type=ResourceLink" +
+                    ",resourcetype=Context,path=" + path +
+                    ",host=" + host.getName() +
+                    ",name=" + encodedResourceLinkName);
+        }
+
+        return (name);
+
+    }
+
+    // ------------------------------------------------- ServletContext Methods
+
+    /**
+     * Return the value of the specified context attribute, if any;
+     * otherwise return <code>null</code>.
+     */
+    @Override
+    public Object getAttribute(String name) {
+        return context.getAttribute(name);
+    }
+
+    /**
+     * Return an enumeration of the names of the context attributes
+     * associated with this context.
+     */@Override
+    public Enumeration<String> getAttributeNames() {
+        return context.getAttributeNames();
+    }
+
+    /**
+     * Returns the context path of the web application.
+     */
+    public String getContextPath() {
+        return getPath();
+    }
+
+    /**
+     * Return a <code>ServletContext</code> object that corresponds to a
+     * specified URI on the server.
+     */
+    public ServletContext getContext(String uri) {
+
+        // Validate the format of the specified argument
+        if ((uri == null) || (!uri.startsWith("/"))) {
+            return (null);
+        }
+
+        Context child = null;
+        try {
+            Host host = (Host) getParent();
+            String mapuri = uri;
+            while (true) {
+                child = (Context) host.findChild(mapuri);
+                if (child != null)
+                    break;
+                int slash = mapuri.lastIndexOf('/');
+                if (slash < 0)
+                    break;
+                mapuri = mapuri.substring(0, slash);
+            }
+        } catch (Throwable t) {
+            return (null);
+        }
+
+        if (child == null) {
+            return (null);
+        }
+
+        if (getCrossContext()) {
+            // If crossContext is enabled, can always return the context
+            return child.getServletContext();
+        } else if (child == this) {
+            // Can still return the current context
+            return getServletContext();
+        } else {
+            // Nothing to return
+            return (null);
+        }
+    }
+
+    /**
+     * Return the value of the specified initialization parameter, or
+     * <code>null</code> if this parameter does not exist.
+     */
+    public String getInitParameter(final String name) {
+        return context.getInitParameter(name);
+    }
+
+    /**
+     * Return the names of the context's initialization parameters, or an
+     * empty enumeration if the context has no initialization parameters.
+     */
+    public Enumeration<String> getInitParameterNames() {
+        return context.getInitParameterNames();
+    }
+
+    /**
+     * @return true if the context initialization parameter with the given
+     * name and value was set successfully on this ServletContext, and false
+     * if it was not set because this ServletContext already contains a
+     * context initialization parameter with a matching name
+     */
+    public boolean setInitParameter(String name, String value) {
+        if (isContextInitializedCalled) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_CONTEXT_ALREADY_INIT_EXCEPTION),
+                                              new Object[] {"setInitParameter", getName()});
+            throw new IllegalStateException(msg);
+        }
+        return context.setInitParameter(name, value);
+    }
+
+    /**
+     * Return the major version of the Java Servlet API that we implement.
+     */
+    public int getMajorVersion() {
+        return context.getMajorVersion();
+    }
+
+    /**
+     * Return the minor version of the Java Servlet API that we implement.
+     */
+    public int getMinorVersion() {
+        return context.getMinorVersion();
+    }
+
+    /**
+     * Return the MIME type of the specified file, or <code>null</code> if
+     * the MIME type cannot be determined.
+     */
+    public String getMimeType(String file) {
+
+        if (file == null)
+            return (null);
+        int period = file.lastIndexOf(".");
+        if (period < 0)
+            return (null);
+        String extension = file.substring(period + 1);
+        if (extension.length() < 1)
+            return (null);
+        return (findMimeMapping(extension));
+
+    }
+
+    /**
+     * Return a <code>RequestDispatcher</code> object that acts as a
+     * wrapper for the named servlet.
+     */
+    public RequestDispatcher getNamedDispatcher(String name) {
+
+        // Validate the name argument
+        if (name == null)
+            return (null);
+
+        // Create and return a corresponding request dispatcher
+        Wrapper wrapper = (Wrapper) findChild(name);
+        if (wrapper == null)
+            return (null);
+
+        return new ApplicationDispatcher(wrapper, null, null, null, null, null, name);
+
+    }
+
+    /**
+     * Return the display name of this web application.
+     */
+    public String getServletContextName() {
+        return getDisplayName();
+    }
+
+    /**
+     * Remove the context attribute with the specified name, if any.
+     */
+    public void removeAttribute(String name) {
+        context.removeAttribute(name);
+    }
+
+    /**
+     * Bind the specified value with the specified context attribute name,
+     * replacing any existing value for that name.
+     */
+    public void setAttribute(String name, Object value) {
+        context.setAttribute(name, value);
+    }
+
+    /**
+     * Return the name and version of the servlet container.
+     */
+    public String getServerInfo() {
+        return context.getServerInfo();
+    }
+
+    /**
+     * Return the real path corresponding to the given virtual path, or
+     * <code>null</code> if the container was unable to perform the
+     * translation
+     */
+    public String getRealPath(String path) {
+        if (!(showArchivedRealPathEnabled || directoryDeployed)) {
+            return null;
+        }
+
+        if (!isFilesystemBased())
+            return null;
+
+        if (path == null) {
+            return null;
+        }
+
+        File file = null;
+        if (alternateDocBases == null
+                || alternateDocBases.size() == 0) {
+            file = new File(getBasePath(getDocBase()), path);
+        } else {
+            AlternateDocBase match = AlternateDocBase.findMatch(
+                                                path, alternateDocBases);
+            if (match != null) {
+                file = new File(match.getBasePath(), path);
+            } else {
+                // None of the url patterns for alternate doc bases matched
+                file = new File(getBasePath(getDocBase()), path);
+            }
+        }
+
+        if (!file.exists()) {
+            try {
+                // Try looking up resource in
+                // WEB-INF/lib/[*.jar]/META-INF/resources
+                File f = getExtractedMetaInfResourcePath(path);
+                if (f != null && f.exists()) {
+                    file = f;
+                }
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+
+        if (!file.exists()) {
+            return null;
+        } else {
+            return file.getAbsolutePath();
+        }
+    }
+
+    /**
+     * Writes the specified message to a servlet log file.
+     */
+    public void log(String message) {
+        message= neutralizeForLog(message);
+        org.apache.catalina.Logger logger = getLogger();
+        if (logger != null) {
+            /* PWC 6403328
+            logger.log(context.logName() + message, Logger.INFORMATION);
+            */
+            //START PWC 6403328
+            logger.log(logName() + " ServletContext.log():" + message, org.apache.catalina.Logger.INFORMATION);
+            //END PWC 6403328
+        }
+    }
+
+    /**
+     * Writes the specified exception and message to a servlet log file.
+     */
+    public void log(Exception exception, String message) {
+        message= neutralizeForLog(message);
+        org.apache.catalina.Logger logger = getLogger();
+        if (logger != null)
+            logger.log(exception, logName() + message);
+    }
+
+    /**
+     * Writes the specified message and exception to a servlet log file.
+     */
+    public void log(String message, Throwable throwable) {
+        message= neutralizeForLog(message);
+        org.apache.catalina.Logger logger = getLogger();
+        if (logger != null)
+            logger.log(logName() + message, throwable);
+    }
+
+    public Servlet getServlet(String name) {
+        return context.getServlet(name);
+    }
+
+    public Enumeration<String> getServletNames() {
+        return context.getServletNames();
+    }
+
+    public Enumeration<Servlet> getServlets() {
+        return context.getServlets();
+    }
+
+    /**
+     * Return the requested resource as an <code>InputStream</code>.  The
+     * path must be specified according to the rules described under
+     * <code>getResource</code>.  If no such resource can be identified,
+     * return <code>null</code>.
+     */
+    public InputStream getResourceAsStream(String path) {
+
+        if (path == null || !path.startsWith("/"))
+            return (null);
+
+        path = RequestUtil.normalize(path);
+        if (path == null)
+            return (null);
+
+        DirContext resources = null;
+
+        if (alternateDocBases == null
+                || alternateDocBases.size() == 0) {
+            resources = getResources();
+        } else {
+            AlternateDocBase match = AlternateDocBase.findMatch(
+                                path, alternateDocBases);
+            if (match != null) {
+                resources = ContextsAdapterUtility.unwrap(match.getResources());
+            } else {
+                // None of the url patterns for alternate doc bases matched
+                resources = getResources();
+            }
+        }
+
+        if (resources != null) {
+            try {
+                Object resource = resources.lookup(path);
+                if (resource instanceof Resource)
+                    return (((Resource) resource).streamContent());
+            } catch (Exception e) {
+                // do nothing
+            }
+        }
+        return (null);
+    }
+
+    /**
+     * Return the URL to the resource that is mapped to a specified path.
+     * The path must begin with a "/" and is interpreted as relative to the
+     * current context root.
+     */
+    public java.net.URL getResource(String path)
+        throws MalformedURLException {
+
+        if (path == null || !path.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INCORRECT_PATH), path);
+            throw new MalformedURLException(msg);
+        }
+
+        path = RequestUtil.normalize(path);
+        if (path == null)
+            return (null);
+
+        String libPath = "/WEB-INF/lib/";
+        if ((path.startsWith(libPath)) && (path.endsWith(".jar"))) {
+            File jarFile = null;
+            if (isFilesystemBased()) {
+                jarFile = new File(getBasePath(docBase), path);
+            } else {
+                jarFile = new File(getWorkPath(), path);
+            }
+            if (jarFile.exists()) {
+                return jarFile.toURL();
+            } else {
+                return null;
+            }
+
+        } else {
+
+            DirContext resources = null;
+            if (alternateDocBases == null
+                    || alternateDocBases.size() == 0) {
+                resources = context.getResources();
+            } else {
+                AlternateDocBase match = AlternateDocBase.findMatch(
+                                                path, alternateDocBases);
+                if (match != null) {
+                    resources = ContextsAdapterUtility.unwrap(match.getResources());
+                } else {
+                    // None of the url patterns for alternate doc bases matched
+                    resources = getResources();
+                }
+            }
+
+            if (resources != null) {
+                String fullPath = getName() + path;
+                String hostName = getParent().getName();
+                try {
+                    resources.lookup(path);
+                    return new java.net.URL
+                        /* SJSAS 6318494
+                        ("jndi", null, 0, getJNDIUri(hostName, fullPath),
+                         */
+                        // START SJAS 6318494
+                        ("jndi", "", 0, getJNDIUri(hostName, fullPath),
+                        // END SJSAS 6318494
+		         new DirContextURLStreamHandler(resources));
+                } catch (Exception e) {
+                    // do nothing
+                }
+            }
+        }
+
+        return (null);
+    }
+
+    /**
+     * Return a Set containing the resource paths of resources member of the
+     * specified collection. Each path will be a String starting with
+     * a "/" character. The returned set is immutable.
+     */
+    public Set<String> getResourcePaths(String path) {
+        // Validate the path argument
+        if (path == null) {
+            return null;
+        }
+        if (!path.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INCORRECT_PATH), path);
+            throw new IllegalArgumentException(msg);
+        }
+
+        path = RequestUtil.normalize(path);
+        if (path == null)
+            return (null);
+
+        DirContext resources = null;
+
+        if (alternateDocBases == null
+                || alternateDocBases.size() == 0) {
+            resources = getResources();
+        } else {
+            AlternateDocBase match = AlternateDocBase.findMatch(
+                                path, alternateDocBases);
+            if (match != null) {
+                resources = ContextsAdapterUtility.unwrap(match.getResources());
+            } else {
+                // None of the url patterns for alternate doc bases matched
+                resources = getResources();
+            }
+        }
+
+        if (resources != null) {
+            return (getResourcePathsInternal(resources, path));
+        }
+
+        return (null);
+    }
+
+    /**
+     * Internal implementation of getResourcesPath() logic.
+     *
+     * @param resources Directory context to search
+     * @param path Collection path
+     */
+    private Set<String> getResourcePathsInternal(DirContext resources,
+                                                 String path) {
+        HashSet<String> set = new HashSet<String>();
+        try {
+            listCollectionPaths(set, resources, path);
+        } catch (NamingException e) {
+            // Ignore, need to check for resource paths underneath
+            // WEB-INF/lib/[*.jar]/META-INF/resources, see next
+        }
+        try {
+            // Trigger expansion of bundled JAR files
+            File file = getExtractedMetaInfResourcePath(path);
+            if (file != null) {
+                File[] children = file.listFiles();
+                StringBuilder sb = null;
+                for (File child : children) {
+                    sb = new StringBuilder(path);
+                    if (!path.endsWith("/")) {
+                        sb.append("/");
+                    }
+                    sb.append(child.getName());
+                    if (child.isDirectory()) {
+                        sb.append("/");
+                    }
+                    set.add(sb.toString());
+                }
+            }
+        } catch (Exception e) {
+            // ignore
+        }
+        return Collections.unmodifiableSet(set);
+    }
+
+    /**
+     * Return a <code>RequestDispatcher</code> instance that acts as a
+     * wrapper for the resource at the given path.  The path must begin
+     * with a "/" and is interpreted as relative to the current context root.
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        // Validate the path argument
+        if (path == null) {
+            return null;
+        }
+
+        if (!path.startsWith("/") && !path.isEmpty()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INCORRECT_OR_NOT_EMPTY_PATH), path);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Get query string
+        String queryString = null;
+        int pos = path.indexOf('?');
+        if (pos >= 0) {
+            queryString = path.substring(pos + 1);
+            path = path.substring(0, pos);
+        }
+
+        path = RequestUtil.normalize(path);
+        if (path == null)
+            return (null);
+
+        pos = path.length();
+
+        // Use the thread local URI and mapping data
+        DispatchData dd = dispatchData.get();
+        if (dd == null) {
+            dd = new DispatchData();
+            dispatchData.set(dd);
+        }
+
+        MessageBytes uriMB = dd.uriMB;
+        uriMB.recycle();
+
+        // Retrieve the thread local mapping data
+        MappingData mappingData = dd.mappingData;
+
+        // Map the URI
+        CharChunk uriCC = uriMB.getCharChunk();
+        try {
+            uriCC.append(getPath(), 0, getPath().length());
+            /*
+             * Ignore any trailing path params (separated by ';') for mapping
+             * purposes
+             */
+            int semicolon = path.indexOf(';');
+            if (pos >= 0 && semicolon > pos) {
+                semicolon = -1;
+            }
+            uriCC.append(path, 0, semicolon > 0 ? semicolon : pos);
+            getMapper().map(uriMB, mappingData);
+            if (mappingData.wrapper == null) {
+                return (null);
+            }
+            /*
+             * Append any trailing path params (separated by ';') that were
+             * ignored for mapping purposes, so that they're reflected in the
+             * RequestDispatcher's requestURI
+             */
+            if (semicolon > 0) {
+                uriCC.append(path, semicolon, pos - semicolon);
+            }
+        } catch (Exception e) {
+            // Should never happen
+            log.log(Level.WARNING, LogFacade.MAPPING_ERROR_EXCEPTION, e);
+            return (null);
+        }
+
+        Wrapper wrapper = (Wrapper) mappingData.wrapper;
+        String wrapperPath = mappingData.wrapperPath.toString();
+        String pathInfo = mappingData.pathInfo.toString();
+        HttpServletMapping mappingForDispatch = new MappingImpl(mappingData);
+
+        mappingData.recycle();
+
+        // Construct a RequestDispatcher to process this request
+        return new ApplicationDispatcher
+            (wrapper, mappingForDispatch, uriCC.toString(), wrapperPath, pathInfo,
+             queryString, null);
+    }
+
+
+    // ------------------------------------------------------------- Attributes
+
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public DirContext getStaticResources() {
+        return getResources();
+    }
+
+    /**
+     * Return the naming resources associated with this web application.
+     * FIXME: Fooling introspection ...
+     */
+    public DirContext findStaticResources() {
+        return getResources();
+    }
+
+    /**
+     * Return the naming resources associated with this web application.
+     */
+    public String[] getWelcomeFiles() {
+        return findWelcomeFiles();
+    }
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param webXmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean webXmlValidation){
+        this.webXmlValidation = webXmlValidation;
+    }
+
+    /**
+     * Get the server.xml <context> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation(){
+        return webXmlValidation;
+    }
+
+    /**
+     * Get the server.xml <context> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     */
+    public boolean getXmlNamespaceAware(){
+        return webXmlNamespaceAware;
+    }
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param webXmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean webXmlNamespaceAware){
+        this.webXmlNamespaceAware= webXmlNamespaceAware;
+    }
+
+    /**
+     * Set the validation feature of the XML parser used when
+     * parsing tlds files.
+     * @param tldValidation true to enable xml instance validation
+     */
+    public void setTldValidation(boolean tldValidation){
+        this.tldValidation = tldValidation;
+    }
+
+    /**
+     * Get the server.xml <context> attribute's webXmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getTldValidation(){
+        return tldValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     */
+    public boolean getTldNamespaceAware(){
+        return tldNamespaceAware;
+    }
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param tldNamespaceAware true to enable namespace awareness
+     */
+    public void setTldNamespaceAware(boolean tldNamespaceAware){
+        this.tldNamespaceAware= tldNamespaceAware;
+    }
+
+    /**
+     * Sets the list of ordered libs, which will be used as the value of the
+     * ServletContext attribute with name javax.servlet.context.orderedLibs
+     */
+    public void setOrderedLibs(List<String> orderedLibs) {
+        this.orderedLibs = orderedLibs;
+    }
+
+    public void startRecursive() throws LifecycleException {
+        // nothing to start recursively, the servlets will be started by
+        // load-on-startup
+        start();
+    }
+
+    public int getState() {
+        if( started ) {
+            return 1; // RUNNING
+        }
+        if( initialized ) {
+            return 0; // starting ?
+        }
+        if( ! available ) {
+            return 4; //FAILED
+        }
+        // 2 - STOPPING
+        return 3; // STOPPED
+    }
+
+    boolean isContextInitializedCalled() {
+        return isContextInitializedCalled;
+    }
+
+    /**
+     * Creates an ObjectInputStream that provides special deserialization
+     * logic for classes that are normally not serializable (such as
+     * javax.naming.Context).
+     */
+    public ObjectInputStream createObjectInputStream(InputStream is)
+            throws IOException {
+
+        ObjectInputStream ois = null;
+
+        Loader loader = getLoader();
+        if (loader != null) {
+            ClassLoader classLoader = loader.getClassLoader();
+            if (classLoader != null) {
+                try {
+                    ois = new CustomObjectInputStream(is, classLoader);
+                } catch (IOException ioe) {
+                    log.log(Level.SEVERE, LogFacade.CANNOT_CREATE_OBJECT_INPUT_STREAM, ioe);
+                }
+            }
+        }
+
+        if (ois == null) {
+            ois = new ObjectInputStream(is);
+        }
+
+        return ois;
+    }
+
+    /**
+     * Creates an ObjectOutputStream that provides special serialization
+     * logic for classes that are normally not serializable (such as
+     * javax.naming.Context).
+     */
+    public ObjectOutputStream createObjectOutputStream(OutputStream os)
+            throws IOException {
+        return new ObjectOutputStream(os);
+    }
+
+    /**
+     * Gets the time this context was started.
+     *
+     * @return Time (in milliseconds since January 1, 1970, 00:00:00) when this
+     * context was started
+     */
+    public long getStartTimeMillis() {
+        return startTimeMillis;
+    }
+
+    public boolean isEventProvider() {
+        return false;
+    }
+
+    public boolean isStatisticsProvider() {
+        return false;
+    }
+
+    /*
+     * HTTP session related monitoring events
+     */
+    public void sessionCreatedEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public void sessionDestroyedEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public void sessionRejectedEvent(int maxSessions) {
+        // Deliberate noop
+    }
+
+    public void sessionExpiredEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public void sessionPersistedStartEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public void sessionPersistedEndEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public void sessionActivatedStartEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public void sessionActivatedEndEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public void sessionPassivatedStartEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public void sessionPassivatedEndEvent(HttpSession session) {
+        // Deliberate noop
+    }
+
+    public static class RestrictedServletContextListener
+            implements ServletContextListener {
+
+        /*
+         * The ServletContextListener to which to delegate
+         */
+        private ServletContextListener delegate;
+
+        /**
+         * Constructor
+         */
+        public RestrictedServletContextListener(
+                ServletContextListener delegate) {
+            this.delegate = delegate;
+        }
+
+        public void contextInitialized(ServletContextEvent sce) {
+            delegate.contextInitialized(sce);
+        }
+
+        public void contextDestroyed(ServletContextEvent sce) {
+            delegate.contextDestroyed(sce);
+        }
+
+        public ServletContextListener getNestedListener() {
+            return delegate;
+        }
+    }
+
+    /**
+     * Instantiates the given Servlet class.
+     *
+     * @return the new Servlet instance
+     */
+    protected <T extends Servlet> T createServletInstance(Class<T> clazz)
+            throws Exception{
+        return clazz.newInstance();
+    }
+
+    /**
+     * Instantiates the given Filter class.
+     *
+     * @return the new Filter instance
+     */
+    protected <T extends Filter> T createFilterInstance(Class<T> clazz)
+            throws Exception{
+        return clazz.newInstance();
+    }
+
+    /**
+     * Instantiates the given EventListener class.
+     *
+     * @return the new EventListener instance
+     */
+    public <T extends EventListener> T createListenerInstance(
+                Class<T> clazz) throws Exception{
+        return clazz.newInstance();
+    }
+
+    /**
+     * Instantiates the given HttpUpgradeHandler class.
+     *
+     * @param clazz
+     * @param <T>
+     * @return a new T instance
+     * @throws Exception
+     */
+    public <T extends HttpUpgradeHandler> T createHttpUpgradeHandlerInstance(Class<T> clazz)
+            throws Exception {
+        return clazz.newInstance();
+    }
+
+    /**
+     * Custom security manager responsible for enforcing permission
+     * check on ServletContext#getClassLoader if necessary.
+     */
+    private static class MySecurityManager extends SecurityManager {
+
+        /*
+         * @return true if the specified class loader <code>cl</code>
+         * can be found in the class loader delegation chain of the
+         * <code>start</code> class loader, false otherwise
+         */
+        boolean isAncestor(ClassLoader start, ClassLoader cl) {
+            ClassLoader acl = start;
+            do {
+	        acl = acl.getParent();
+                if (cl == acl) {
+                    return true;
+                }
+            } while (acl != null);
+            return false;
+        }
+
+        /*
+         * Checks whether access to the webapp class loader associated
+         * with this Context should be granted to the caller of
+         * ServletContext#getClassLoader.
+         *
+         * If no security manager exists, this method returns immediately.
+         *
+         * Otherwise, it calls the security manager's checkPermission
+         * method with the getClassLoader permission if the class loader
+         * of the caller of ServletContext#getClassLoader is not the same as,
+         * or an ancestor of the webapp class loader associated with this
+         * Context.
+         */
+        void checkGetClassLoaderPermission(ClassLoader webappLoader) {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm == null) {
+                return;
+            }
+
+            // Get the current execution stack as an array of classes
+            Class[] classContext = getClassContext();
+
+            /*
+             * Determine the caller of ServletContext#getClassLoader:
+             *
+             * classContext[0]:
+             *   org.apache.catalina.core.StandardContext$MySecurityManager
+             * classContext[1]:
+             *   org.apache.catalina.core.StandardContext
+             * classContext[2]:
+             *   org.apache.catalina.core.StandardContext
+             * classContext[3]:
+             *   org.apache.catalina.core.ApplicationContext
+             * classContext[4]:
+             *  org.apache.catalina.core.ApplicationContextFacade
+             * classContext[5]:
+             *  Caller whose classloader to check
+             *
+             * NOTE: INDEX MUST BE ADJUSTED WHENEVER EXECUTION STACK
+             * CHANGES, E.G., DUE TO CODE BEING REORGANIZED
+             */
+            ClassLoader ccl = classContext[5].getClassLoader();
+            if (ccl != null && ccl != webappLoader &&
+                    !isAncestor(webappLoader, ccl)) {
+                sm.checkPermission(GET_CLASSLOADER_PERMISSION);
+            }
+        }
+    }
+
+    private static class PrivilegedCreateSecurityManager
+            implements PrivilegedAction<MySecurityManager> {
+
+        public MySecurityManager run() {
+            return new MySecurityManager();
+        }
+    }
+
+    /**
+     * List resource paths (recursively), and store all of them in the given
+     * Set.
+     */
+    private static void listCollectionPaths
+        (Set<String> set, DirContext resources, String path)
+        throws NamingException {
+
+        Enumeration<Binding> childPaths = resources.listBindings(path);
+        while (childPaths.hasMoreElements()) {
+            Binding binding = childPaths.nextElement();
+            String name = binding.getName();
+            StringBuilder childPath = new StringBuilder(path);
+            if (!"/".equals(path) && !path.endsWith("/"))
+                childPath.append("/");
+            childPath.append(name);
+            Object object = binding.getObject();
+            if (object instanceof DirContext &&
+                    childPath.charAt(childPath.length() -1) != '/') {
+                childPath.append("/");
+            }
+            set.add(childPath.toString());
+        }
+    }
+
+    /**
+     * Get full path, based on the host name and the context path.
+     */
+    private static String getJNDIUri(String hostName, String path) {
+        if (!path.startsWith("/"))
+            return "/" + hostName + "/" + path;
+        else
+            return "/" + hostName + path;
+    }
+
+    /**
+     * Internal class used as thread-local storage when doing path
+     * mapping during dispatch.
+     */
+    private static final class DispatchData {
+        public MessageBytes uriMB;
+        public MappingData mappingData;
+
+        public DispatchData() {
+            uriMB = MessageBytes.newInstance();
+            CharChunk uriCC = uriMB.getCharChunk();
+            uriCC.setLimit(-1);
+            mappingData = new MappingData();
+        }
+    }
+
+    /**
+     * Get resource from META-INF/resources/ in jars.
+     */
+    private File getExtractedMetaInfResourcePath(String path) {
+        path = Globals.META_INF_RESOURCES + path;
+
+        ClassLoader cl = getLoader().getClassLoader();
+        if (cl instanceof WebappClassLoader) {
+            return ((WebappClassLoader)cl).getExtractedResourcePath(path);
+        }
+        return null;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardContextValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardContextValve.java
new file mode 100644
index 0000000..ef856be
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardContextValve.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.valves.ValveBase;
+import org.glassfish.web.valve.GlassFishValve;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import org.glassfish.grizzly.http.util.DataChunk;
+// END GlassFish 1343
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardContext</code> container implementation.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  This implementation is likely to be useful only
+ * when processing HTTP requests.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.19 $ $Date: 2007/05/05 05:31:54 $
+ */
+
+final class StandardContextValve
+    extends ValveBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardContextValve/1.0";
+
+
+    private StandardContext context = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Cast to a StandardContext right away, as it will be needed later.
+     * 
+     * @see org.apache.catalina.Contained#setContainer(org.apache.catalina.Container)
+     */
+    public void setContainer(Container container) {
+        super.setContainer(container);
+        if (container instanceof StandardContext) {
+            context = (StandardContext) container;
+        }
+    }
+
+
+    /**
+     * Select the appropriate child Wrapper to process this request,
+     * based on the specified request URI.  If no matching Wrapper can
+     * be found, return an appropriate HTTP error.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     * @param valveContext Valve context used to forward to the next Valve
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    @Override
+    public int invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        Wrapper wrapper = preInvoke(request, response);
+        if (wrapper == null) {
+            return END_PIPELINE;
+        }
+
+        /* GlassFish 1343
+        wrapper.getPipeline().invoke(request, response);
+        */
+        // START GlassFish 1343
+        if (wrapper.getPipeline().hasNonBasicValves() ||
+                wrapper.hasCustomPipeline()) {
+            wrapper.getPipeline().invoke(request, response);
+        } else {
+            GlassFishValve basic = wrapper.getPipeline().getBasic();
+            if (basic != null) {
+                basic.invoke(request, response);
+                basic.postInvoke(request, response);
+            }
+        }
+        // END GlassFish 1343
+
+        return END_PIPELINE;
+    } 
+
+
+    /**
+     * Tomcat style invocation.
+     */
+    @Override
+    public void invoke(org.apache.catalina.connector.Request request,
+                       org.apache.catalina.connector.Response response)
+            throws IOException, ServletException {
+
+        Wrapper wrapper = preInvoke(request, response);
+        if (wrapper == null) {
+            return;
+        }
+
+        /* GlassFish 1343
+        wrapper.getPipeline().invoke(request, response);
+        */
+        // START GlassFish 1343
+        if (wrapper.getPipeline().hasNonBasicValves() ||
+                wrapper.hasCustomPipeline()) {
+            wrapper.getPipeline().invoke(request, response);
+        } else {
+            GlassFishValve basic = wrapper.getPipeline().getBasic();
+            if (basic != null) {
+                basic.invoke(request, response);
+                basic.postInvoke(request, response);
+            }
+        }
+        // END GlassFish 1343
+
+        postInvoke(request, response);
+    }
+
+
+    @Override
+    public void postInvoke(Request request, Response response)
+            throws IOException, ServletException {
+    }
+
+
+    /**
+     * Report a "not found" error for the specified resource.  FIXME:  We
+     * should really be using the error reporting settings for this web
+     * application, but currently that code runs at the wrapper level rather
+     * than the context level.
+     *
+     * @param response The response we are creating
+     */
+    private void notFound(HttpServletResponse response) {
+
+        try {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+        } catch (IllegalStateException e) {
+            // Ignore
+        } catch (IOException e) {
+            // Ignore
+        }
+
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     *
+    private void log(String message) {
+        org.apache.catalina.Logger logger = null;
+        String containerName = null;
+        if (container != null) {
+            logger = container.getLogger();
+            containerName = container.getName();
+        }
+        if (logger != null) {
+            logger.log("StandardContextValve[" + container.getName() + "]: " +
+                       message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.info("StandardContextValve[" + containerName + "]: " +
+                         message);
+            }
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     *
+    private void log(String message, Throwable t) {
+        org.apache.catalina.Logger logger = null;
+        String containerName = null;
+        if (container != null) {
+            logger = container.getLogger();
+            containerName = container.getName();
+        }
+        if (logger != null) {
+            logger.log("StandardContextValve[" + container.getName() + "]: " +
+                message, t, org.apache.catalina.Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, "StandardContextValve[" + containerName +
+                "]: " + message, t);
+        }
+    }
+    */
+
+
+    private Wrapper preInvoke(Request request, Response response) {
+
+        // Disallow any direct access to resources under WEB-INF or META-INF
+        HttpRequest hreq = (HttpRequest) request;
+        // START CR 6415120
+        if (request.getCheckRestrictedResources()) {
+        // END CR 6415120
+            DataChunk requestPathDC = hreq.getRequestPathMB();
+            if ((requestPathDC.startsWithIgnoreCase("/META-INF/", 0))
+                    || (requestPathDC.equalsIgnoreCase("/META-INF"))
+                    || (requestPathDC.startsWithIgnoreCase("/WEB-INF/", 0))
+                    || (requestPathDC.equalsIgnoreCase("/WEB-INF"))) {
+                notFound((HttpServletResponse) response.getResponse());
+                return null;
+            }
+        // START CR 6415120
+        }
+        // END CR 6415120
+
+        // Wait if we are reloading
+        boolean reloaded = false;
+        while (((StandardContext) container).getPaused()) {
+            reloaded = true;
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+        }
+
+        // Reloading will have stopped the old webappclassloader and
+        // created a new one
+        if (reloaded &&
+                context.getLoader() != null &&
+                context.getLoader().getClassLoader() != null) {
+            Thread.currentThread().setContextClassLoader(
+                    context.getLoader().getClassLoader());
+        }
+
+        // Select the Wrapper to be used for this Request
+        Wrapper wrapper = request.getWrapper();
+        if (wrapper == null) {
+            notFound((HttpServletResponse) response.getResponse());
+            return null;
+        } else if (wrapper.isUnavailable()) {
+            // May be as a result of a reload, try and find the new wrapper
+            wrapper = (Wrapper) container.findChild(wrapper.getName());
+            if (wrapper == null) {
+                notFound((HttpServletResponse) response.getResponse());
+                return null;
+            }
+        }
+
+        return wrapper;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngine.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngine.java
new file mode 100644
index 0000000..6db970e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngine.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.*;
+import org.apache.catalina.realm.JAASRealm;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import java.util.logging.Level;
+
+/**
+ * Standard implementation of the <b>Engine</b> interface.  Each
+ * child container must be a Host implementation to process the specific
+ * fully qualified host name of that virtual host.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.5 $ $Date: 2006/03/12 01:27:01 $
+ */
+
+public class StandardEngine
+    extends ContainerBase
+    implements Engine {
+
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new StandardEngine component with the default basic Valve.
+     */
+    public StandardEngine() {
+
+        super();
+        pipeline.setBasic(new StandardEngineValve());
+        /* Set the jmvRoute using the system property jvmRoute */
+        try {
+            setJvmRoute(System.getProperty("jvmRoute"));
+        } catch(Exception ex) {
+        }
+        // By default, the engine will hold the reloading thread
+        backgroundProcessorDelay = 10;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Host name to use when no server host, or an unknown host,
+     * is specified in the request.
+     */
+    private String defaultHost = null;
+
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardEngine/1.0";
+
+
+    /**
+     * The <code>Service</code> that owns this Engine, if any.
+     */
+    private Service service = null;
+
+    /** Allow the base dir to be specified explicitly for
+     * each engine. In time we should stop using catalina.base property -
+     * otherwise we loose some flexibility.
+     */
+    private String baseDir = null;
+
+    /**
+     * The JVM Route ID for this Tomcat instance. All Route ID's must be unique
+     * across the cluster.
+     */
+    private String jvmRouteId;
+
+
+    // ------------------------------------------------------------- Properties
+
+    /** Provide a default in case no explicit configuration is set
+     *
+     * @return configured realm, or a JAAS realm by default
+     */
+    public Realm getRealm() {
+        Realm configured=super.getRealm();
+        // If no set realm has been called - default to JAAS
+        // This can be overridden at engine, context and host level  
+        if( configured==null ) {
+            configured=new JAASRealm();
+            this.setRealm( configured );
+        }
+        return configured;
+    }
+
+
+    /**
+     * Return the default host.
+     */
+    public String getDefaultHost() {
+
+        return (defaultHost);
+
+    }
+
+
+    /**
+     * Set the default host.
+     *
+     * @param host The new default host
+     */
+    public void setDefaultHost(String host) {
+
+        String oldDefaultHost = this.defaultHost;
+        if (host == null) {
+            this.defaultHost = null;
+        } else {
+            // START OF PE 4989789
+            //this.defaultHost = host.toLowerCase();
+            this.defaultHost = host;
+            // END OF PE 4989789
+        }
+        support.firePropertyChange("defaultHost", oldDefaultHost,
+                                   this.defaultHost);
+
+    }
+    
+    public void setName(String name ) {
+        if( domain != null ) {
+            // keep name==domain, ignore override
+            // we are already registered
+            super.setName( domain );
+            return;
+        }
+        // The engine name is used as domain
+        domain=name; // XXX should we set it in init() ? It shouldn't matter
+        super.setName( name );
+    }
+
+
+    /**
+     * Set the cluster-wide unique identifier for this Engine.
+     * This value is only useful in a load-balancing scenario.
+     * <p>
+     * This property should not be changed once it is set.
+     */
+    public void setJvmRoute(String routeId) {
+        jvmRouteId = routeId;
+    }
+
+
+    /**
+     * Retrieve the cluster-wide unique identifier for this Engine.
+     * This value is only useful in a load-balancing scenario.
+     */
+    public String getJvmRoute() {
+        return jvmRouteId;
+    }
+
+
+    /**
+     * 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;
+    }
+
+    public String getBaseDir() {
+        if( baseDir==null ) {
+            baseDir=System.getProperty("catalina.base");
+        }
+        if( baseDir==null ) {
+            baseDir=System.getProperty("catalina.home");
+        }
+        return baseDir;
+    }
+
+    public void setBaseDir(String baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a child Container, only if the proposed child is an implementation
+     * of Host.
+     *
+     * @param child Child container to be added
+     */
+    public void addChild(Container child) {
+
+        if (!(child instanceof Host))
+            throw new IllegalArgumentException(rb.getString(LogFacade.CHILD_OF_ENGINE_MUST_BE_HOST_EXCEPTION));
+        super.addChild(child);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * Disallow any attempt to set a parent for this Container, since an
+     * Engine is supposed to be at the top of the Container hierarchy.
+     *
+     * @param container Proposed parent Container
+     */
+    public void setParent(Container container) {
+
+        throw new IllegalArgumentException
+                (rb.getString(LogFacade.CANNOT_HAVE_PARENT_CONTAINER_EXCEPTION));
+
+    }
+
+
+    /* CR 6368085
+    private boolean initialized=false;
+    */
+    
+    public void init() {
+        if( initialized ) return;
+        /* CR 6368085
+        initialized=true;
+        */
+
+        if( oname==null ) {
+            // not registered in JMX yet - standalone mode
+            try {
+                if (domain==null) {
+                    domain=getName();
+                }
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, "Register " + neutralizeForLog(domain));
+                }
+                oname=new ObjectName(domain + ":type=Engine");
+                controller=oname;
+            } catch (Throwable t) {
+                log.log(Level.INFO, LogFacade.ERROR_REGISTERING_EXCEPTION, t);
+            }
+        }
+        
+        if( service==null ) {
+            // for consistency...: we are probably in embedded mode
+            try {
+                service=new StandardService();
+                service.setContainer( this );
+                service.initialize();
+                // Use same name for Service
+                service.setName(getName());
+            } catch( Throwable t ) {
+                log.log(Level.SEVERE, t.toString());
+            }
+        }
+        // START CR 6368085
+        initialized = true;
+        // END CR 6368085
+        
+    }
+
+    /* CR 6368085    
+    public void destroy() throws LifecycleException {
+    */
+    // START CR 6368085
+    public void destroy() throws Exception {
+    // END CR 6368085
+        if( ! initialized ) return;
+        /* CR 6368085
+        initialized=false;
+        */
+        // START CR 6368085
+        super.destroy();
+        // END CR 6368085
+
+        // if we created it, make sure it's also destroyed
+        ((StandardService)service).destroy();
+    }
+    
+    /**
+     * Start this Engine component.
+     *
+     * @exception LifecycleException if a startup error occurs
+     */
+    public void start() throws LifecycleException {
+        if( started ) {
+            return;
+        }
+        if( !initialized ) {
+            init();
+        }
+
+        /* PWC 6296256
+        // Log our server identification information
+        log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
+        */
+        // START PWC 6296256
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Starting Servlet Engine");
+        }
+        // END PWC 6296256
+
+        // Standard container startup
+        super.start();
+
+    }
+    
+    public void stop() throws LifecycleException {
+        super.stop();
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("StandardEngine[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    // FIXME Remove -- not used 
+    public ObjectName getParentName() throws MalformedObjectNameException {
+        if (getService()==null) {
+            return null;
+        }
+        String name = getService().getName();
+        ObjectName serviceName=new ObjectName(domain +
+                        ":type=Service,serviceName="+name);
+        return serviceName;                
+    }
+    
+    public ObjectName createObjectName(String domain, ObjectName parent)
+        throws Exception
+    {
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Create ObjectName " + domain + " " + parent);
+        return new ObjectName( domain + ":type=Engine");
+    }
+    
+    public String getDomain() {
+        if (domain!=null) {
+            return domain;
+        } else { 
+            return getName();
+        }
+    }
+    
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngineValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngineValve.java
new file mode 100644
index 0000000..ebc57b7
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngineValve.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.Host;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.valves.ValveBase;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardEngine</code> container implementation.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  This implementation is likely to be useful only
+ * when processing HTTP requests.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:36 $
+ */
+
+final class StandardEngineValve
+    extends ValveBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardEngineValve/1.0";
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Select the appropriate child Host to process this request,
+     * based on the requested server name.  If no matching Host can
+     * be found, return an appropriate HTTP error.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     * @param valveContext Valve context used to forward to the next Valve
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    @Override
+    public int invoke(Request request, Response response)
+            throws IOException, ServletException {
+
+        Host host = preInvoke(request, response);
+        if (host == null) {
+            return END_PIPELINE;
+        }
+
+        if (host.getPipeline().hasNonBasicValves() ||
+                host.hasCustomPipeline()) {
+            // Invoke pipeline
+            host.getPipeline().invoke(request, response);
+        } else {
+            // Invoke basic valve only
+            host.getPipeline().getBasic().invoke(request, response);
+        }
+
+        return END_PIPELINE;
+    }
+
+
+    /**
+     * Tomcat style invocation.
+     */
+    @Override
+    public void invoke(org.apache.catalina.connector.Request request,
+                       org.apache.catalina.connector.Response response)
+            throws IOException, ServletException {
+
+        Host host = preInvoke(request, response);
+        if (host == null) {
+            return;
+        }
+
+        if (host.getPipeline().hasNonBasicValves() ||
+                host.hasCustomPipeline()) {
+            // Invoke pipeline
+            host.getPipeline().invoke(request, response);
+        } else {
+            // Invoke basic valve only
+            host.getPipeline().getBasic().invoke(request, response);
+        }
+    }
+
+
+    private Host preInvoke(Request request, Response response)
+            throws IOException, ServletException {
+
+        // Select the Host to be used for this Request
+        Host host = request.getHost();
+        if (host == null) {
+
+            // BEGIN S1AS 4878272
+            ((HttpServletResponse) response.getResponse()).sendError
+                (HttpServletResponse.SC_BAD_REQUEST);
+            String msg = MessageFormat.format(rb.getString(LogFacade.NO_HOST_MATCH), request.getRequest().getServerName());
+            response.setDetailMessage(msg);
+            // END S1AS 4878272
+            return null;
+        }
+
+        return host;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHost.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHost.java
new file mode 100644
index 0000000..eb8997d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHost.java
@@ -0,0 +1,1266 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.authenticator.SingleSignOn;
+import org.apache.catalina.deploy.ErrorPage;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.valves.ValveBase;
+import org.glassfish.web.valve.GlassFishValve;
+
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.ObjectName;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+
+/**
+ * Standard implementation of the <b>Host</b> interface.  Each
+ * child container must be a Context implementation to process the
+ * requests directed to a particular web application.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.13 $ $Date: 2007/04/17 21:33:22 $
+ */
+
+public class StandardHost
+    extends ContainerBase
+    implements Deployer, Host  
+ {
+    /* Why do we implement deployer and delegate to deployer ??? */
+
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new StandardHost component with the default basic Valve.
+     */
+    public StandardHost() {
+        pipeline.setBasic(new StandardHostValve());
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of aliases for this Host.
+     */
+    protected String[] aliases = new String[0];
+
+
+    /**
+     * The application root for this Host.
+     */
+    private String appBase = ".";
+
+
+    /**
+     * The auto deploy flag for this Host.
+     */
+    private boolean autoDeploy = true;
+
+
+     /**
+      * The broadcaster that sends j2ee notifications.
+      */
+     private NotificationBroadcasterSupport broadcaster = null;
+
+
+    /**
+     * The Java class name of the default context configuration class
+     * for deployed web applications.
+     */
+    private String configClass =
+        "org.apache.catalina.startup.ContextConfig";
+
+
+    /**
+     * The Java class name of the default Context implementation class for
+     * deployed web applications.
+     */
+    private String contextClass =
+        "org.apache.catalina.core.StandardContext";
+
+    /**
+     * The default context.xml location
+     */
+    private String defaultContextXmlLocation;
+
+    /**
+     * The default web.xml location
+     */
+    private String defaultWebXmlLocation;
+
+    /**
+     * The <code>Deployer</code> to whom we delegate application
+     * deployment requests.
+     */
+    private Deployer deployer = null;
+
+
+    /**
+     * The deploy on startup flag for this Host.
+     */
+    private boolean deployOnStartup = true;
+
+
+    /**
+     * deploy Context XML config files property.
+     */
+    private boolean deployXML = true;
+
+
+    /**
+     * The Java class name of the default error reporter implementation class 
+     * for deployed web applications.
+     */
+    private String errorReportValveClass =
+        "org.apache.catalina.valves.ErrorReportValve";
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardHost/1.0";
+
+
+    /**
+     * Unpack WARs property.
+     */
+    private boolean unpackWARs = true;
+
+
+    /**
+     * Work Directory base for applications.
+     */
+    private String workDir = null;
+
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+     private boolean xmlValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off XML namespace awareness.
+     */
+     private boolean xmlNamespaceAware = false;
+
+
+    // START SJSAS 6324911
+    /**
+     * The status code error pages for this StandardHost, keyed by HTTP status
+     * code.
+     */
+    private HashMap<Integer, ErrorPage> statusPages =
+        new HashMap<Integer, ErrorPage>();
+    // END SJSAS 6324911
+
+
+    // BEGIN S1AS 5000999
+    /**
+     * The network listener names with which this StandardHost is associated
+     */
+    private String[] networkListenerNames = new String[0];
+    // END S1AS 5000999
+
+
+    /**
+     * With proxy caching disabled, setting this flag to true adds 
+     * Pragma and Cache-Control headers with "No-cache" as value. 
+     * Setting this flag to false does not add any Pragma header,
+     * but sets the Cache-Control header to "private".
+     */
+    private boolean securePagesWithPragma = true;
+    
+
+    private SingleSignOn sso;
+
+
+     /**
+      * The notification sequence number.
+      */
+     private long sequenceNumber = 0;
+
+    
+    // ------------------------------------------------------------- Properties
+
+
+    // START SJSAS 6331392
+    public void setPipeline(Pipeline pl) {
+        StandardHostValve shValve = new StandardHostValve();
+        configureStandardHostValve(shValve);
+        pl.setBasic(shValve);
+        pipeline = pl;
+        hasCustomPipeline = true;
+    }    
+    // END SJSAS 6331392
+
+
+    /**
+     * Return the application root for this Host.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     */
+    public String getAppBase() {
+
+        return (this.appBase);
+
+    }
+
+
+    /**
+     * Set the application root for this Host.  This can be an absolute
+     * pathname, a relative pathname, or a URL.
+     *
+     * @param appBase The new application root
+     */
+    public void setAppBase(String appBase) {
+
+        String oldAppBase = this.appBase;
+        this.appBase = appBase;
+        support.firePropertyChange("appBase", oldAppBase, this.appBase);
+
+    }
+
+
+    /**
+     * Return the value of the auto deploy flag.  If true, it indicates that 
+     * this host's child webapps will be dynamically deployed.
+     */
+    public boolean getAutoDeploy() {
+
+        return (this.autoDeploy);
+
+    }
+
+
+    /**
+     * Set the auto deploy flag value for this host.
+     * 
+     * @param autoDeploy The new auto deploy flag
+     */
+    public void setAutoDeploy(boolean autoDeploy) {
+
+        boolean oldAutoDeploy = this.autoDeploy;
+        this.autoDeploy = autoDeploy;
+        support.firePropertyChange("autoDeploy", oldAutoDeploy, 
+                                   this.autoDeploy);
+
+    }
+
+
+    /**
+     * Return the Java class name of the context configuration class
+     * for new web applications.
+     */
+    public String getConfigClass() {
+
+        return (this.configClass);
+
+    }
+
+
+    /**
+     * Set the Java class name of the context configuration class
+     * for new web applications.
+     *
+     * @param configClass The new context configuration class
+     */
+    public void setConfigClass(String configClass) {
+
+        String oldConfigClass = this.configClass;
+        this.configClass = configClass;
+        support.firePropertyChange("configClass",
+                                   oldConfigClass, this.configClass);
+
+    }
+
+
+    /**
+     * Return the Java class name of the Context implementation class
+     * for new web applications.
+     */
+    public String getContextClass() {
+
+        return (this.contextClass);
+
+    }
+
+
+    /**
+     * Set the Java class name of the Context implementation class
+     * for new web applications.
+     *
+     * @param contextClass The new context implementation class
+     */
+    public void setContextClass(String contextClass) {
+
+        String oldContextClass = this.contextClass;
+        this.contextClass = contextClass;
+        support.firePropertyChange("contextClass",
+                                   oldContextClass, this.contextClass);
+
+    }
+
+
+    /**
+     * Return the value of the deploy on startup flag.  If true, it indicates 
+     * that this host's child webapps should be discovered and automatically 
+     * deployed at startup time.
+     */
+    public boolean getDeployOnStartup() {
+
+        return (this.deployOnStartup);
+
+    }
+
+
+    /**
+     * Set the deploy on startup flag value for this host.
+     * 
+     * @param deployOnStartup The new deploy on startup flag
+     */
+    public void setDeployOnStartup(boolean deployOnStartup) {
+
+        boolean oldDeployOnStartup = this.deployOnStartup;
+        this.deployOnStartup = deployOnStartup;
+        support.firePropertyChange("deployOnStartup", oldDeployOnStartup, 
+                                   this.deployOnStartup);
+
+    }
+
+
+    /**
+     * Deploy XML Context config files flag accessor.
+     */
+    public boolean isDeployXML() {
+
+        return (deployXML);
+
+    }
+
+
+    /**
+     * Deploy XML Context config files flag mutator.
+     */
+    public void setDeployXML(boolean deployXML) {
+
+        this.deployXML = deployXML;
+
+    }
+
+
+    /**
+     * Return the Java class name of the error report valve class
+     * for new web applications.
+     */
+    public String getErrorReportValveClass() {
+
+        return (this.errorReportValveClass);
+
+    }
+
+
+    /**
+     * Set the Java class name of the error report valve class
+     * for new web applications.
+     *
+     * @param errorReportValveClass The new error report valve class
+     */
+    public void setErrorReportValveClass(String errorReportValveClass) {
+
+        String oldErrorReportValveClassClass = this.errorReportValveClass;
+        this.errorReportValveClass = errorReportValveClass;
+        support.firePropertyChange("errorReportValveClass",
+                                   oldErrorReportValveClassClass, 
+                                   this.errorReportValveClass);
+    }
+
+
+    /**
+     * Return the canonical, fully qualified, name of the virtual host
+     * this Container represents.
+     */
+    @Override
+    public String getName() {
+        return (name);
+    }
+
+
+    /**
+     * Set the canonical, fully qualified, name of the virtual host
+     * this Container represents.
+     *
+     * @param name Virtual host name
+     *
+     * @exception IllegalArgumentException if name is null
+     */
+    @Override
+    public void setName(String name) {
+
+        if (name == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.HOST_NAME_REQUIRED_EXCEPTION));
+
+        // START OF PE 4989789
+        // name = name.toLowerCase();      // Internally all names are lower case
+        // END OF PE 4989789
+
+        String oldName = this.name;
+        this.name = name;
+        support.firePropertyChange("name", oldName, this.name);
+    }
+
+
+    /**
+     * Unpack WARs flag accessor.
+     */
+    public boolean isUnpackWARs() {
+        return (unpackWARs);
+    }
+
+
+    /**
+     * Unpack WARs flag mutator.
+     */
+    public void setUnpackWARs(boolean unpackWARs) {
+
+        this.unpackWARs = unpackWARs;
+
+    }
+
+     /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean xmlValidation){
+        this.xmlValidation = xmlValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation(){
+        return xmlValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awareness is enabled.
+     *
+     */
+    public boolean getXmlNamespaceAware(){
+        return xmlNamespaceAware;
+    }
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean xmlNamespaceAware){
+        this.xmlNamespaceAware=xmlNamespaceAware;
+    }    
+    
+    /**
+     * Host work directory base.
+     */
+    public String getWorkDir() {
+        return (workDir);
+    }
+
+
+    /**
+     * Host work directory base.
+     */
+    public void setWorkDir(String workDir) {
+        this.workDir = workDir;
+    }
+
+
+    // BEGIN S1AS 5000999
+    /**
+     * Associates this StandardHost with the given network listener names.
+     *
+     * @param networkListenerNames The network listener names with which to associate this StandardHost
+     */
+    public void setNetworkListenerNames(String[] networkListenerNames) {
+        String[] oldListenerNames = this.networkListenerNames;
+        this.networkListenerNames = networkListenerNames.clone();
+        support.firePropertyChange("ports", oldListenerNames, this.networkListenerNames);
+    }
+
+
+    /**
+     * Gets the network listener names with which this StandardHost is associated.
+     *
+     * @return The network listener names with which this StandardHost is associated,
+     * or null if this StandardHost has not been associated with any ports
+     */
+    public String[] getNetworkListenerNames() {
+        return this.networkListenerNames.clone();
+    }
+    // END S1AS 5000999
+
+
+    public String getNetworkListeners() {
+        List<String> list = Arrays.asList(networkListenerNames);
+        String listeners = null;
+        if (list.size() > 0) {
+            listeners = list.get(0);
+            for (int i = 1; i < list.size(); i++) {
+                listeners = list.get(i) + "," + listeners ;
+            }
+        }
+        return listeners;
+    }
+
+     /**
+     * Gets the default-context.xml location of web modules deployed on this
+     * virtual server.
+     *
+     * @return default-context.xml location of web modules deployed on this
+     * virtual server
+     */
+    public String getDefaultContextXmlLocation() {
+        return defaultContextXmlLocation;
+    }
+
+    /**
+     * Sets the default-context.xml location for web modules deployed on this
+     * virtual server.
+     *
+     * @param defaultContextXmlLocation default-context.xml location for web modules
+     * deployed on this virtual server
+     */
+    public void setDefaultContextXmlLocation(String defaultContextXmlLocation) {
+        this.defaultContextXmlLocation = defaultContextXmlLocation;
+    }
+
+    /**
+     * Gets the default-web.xml location of web modules deployed on this
+     * virtual server.
+     *
+     * @return default-web.xml location of web modules deployed on this
+     * virtual server
+     */
+    public String getDefaultWebXmlLocation() {
+        return defaultWebXmlLocation;
+    }
+
+    /**
+     * Sets the default-web.xml location for web modules deployed on this
+     * virtual server.
+     *
+     * @param defaultWebXmlLocation default-web.xml location for web modules
+     * deployed on this virtual server
+     */
+    public void setDefaultWebXmlLocation(String defaultWebXmlLocation) {
+        this.defaultWebXmlLocation = defaultWebXmlLocation;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an alias name that should be mapped to this same Host.
+     *
+     * @param alias The alias to be added
+     */
+    public void addAlias(String alias) {
+
+        // START OF PE 4989789
+        //alias = alias.toLowerCase();
+        // START OF PE 4989789
+        
+        // Skip duplicate aliases
+        for(String name : aliases) {
+            if(name.equals(alias)) {
+                return;
+            }
+        }
+
+        // Add this alias to the list
+        String newAliases[] = new String[aliases.length + 1];
+        for (int i = 0; i < aliases.length; i++)
+            newAliases[i] = aliases[i];
+        newAliases[aliases.length] = alias;
+
+        aliases = newAliases;
+
+        // Inform interested listeners
+        fireContainerEvent(ADD_ALIAS_EVENT, alias);
+
+    }
+
+
+    /**
+     * Add a child Container, only if the proposed child is an implementation
+     * of Context.
+     *
+     * @param child Child container to be added
+     */
+    @Override
+    public void addChild(Container child) {
+
+        if (!(child instanceof Context))
+            throw new IllegalArgumentException
+                       (rb.getString(LogFacade.CHILD_MUST_BE_CONTEXT_EXCEPTION));
+        super.addChild(child);
+
+    }
+
+
+    // START GlassFish 862
+    /**
+     * Returns the context deployed at the given context root.
+     *
+     * @param contextRoot The context root whose associated context to return
+     *
+     * @return The context deployed at the given context root, or null
+     */
+    @Override
+    public Container findChild(String contextRoot) {
+        return super.findChild(RequestUtil.urlDecode(contextRoot, "UTF-8"));
+    }
+    // END GlassFish 862
+
+
+    /**
+     * Return the set of alias names for this Host.  If none are defined,
+     * a zero length array is returned.
+     */
+    public String[] findAliases() {
+
+        return (this.aliases);
+
+    }
+
+
+    public Host findMappingObject() {
+        return (Host) getMappingObject();
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    @Override
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the Context that would be used to process the specified
+     * host-relative request URI, if any; otherwise return <code>null</code>.
+     *
+     * @param uri Request URI to be mapped
+     */
+    public Context map(String uri) {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Mapping request URI '" + neutralizeForLog(uri) + "'");
+        if (uri == null)
+            return (null);
+
+        // Match on the longest possible context path prefix
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Trying the longest context path prefix");
+        Context context = null;
+        String mapuri = uri;
+        while (true) {
+            context = (Context) findChild(mapuri);
+            if (context != null)
+                break;
+            int slash = mapuri.lastIndexOf('/');
+            if (slash < 0)
+                break;
+            mapuri = mapuri.substring(0, slash);
+        }
+
+        // If no Context matches, select the default Context
+        if (context == null) {
+            if (log.isLoggable(Level.FINEST))
+                log.log(Level.FINEST, "Trying the default context");
+            context = (Context) findChild("");
+        }
+
+        // Complain if no Context has been selected
+        if (context == null) {
+            log.log(Level.SEVERE, LogFacade.MAPPING_CONF_REQUEST_URI_EXCEPTION, uri);
+            return (null);
+        }
+
+        // Return the mapped Context (if any)
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, " Mapped to context '" + neutralizeForLog(context.getPath()) + "'");
+        return (context);
+
+    }
+
+
+    /**
+     * Remove the specified alias name from the aliases for this Host.
+     *
+     * @param alias Alias name to be removed
+     */
+    public void removeAlias(String alias) {
+
+        // START OF PE 4989789
+        //alias = alias.toLowerCase();
+        // START OF PE 4989789
+        
+        // Make sure this alias is currently present
+        int n = -1;
+        for (int i = 0; i < aliases.length; i++) {
+            if (aliases[i].equals(alias)) {
+                n = i;
+                break;
+            }
+        }
+        if (n < 0) {
+            return;
+        }
+
+        // Remove the specified alias
+        int j = 0;
+        String results[] = new String[aliases.length - 1];
+        for (int i = 0; i < aliases.length; i++) {
+            if (i != n) {
+                results[j++] = aliases[i];
+            }
+        }
+        aliases = results;
+
+        // Inform interested listeners
+        fireContainerEvent(REMOVE_ALIAS_EVENT, alias);
+
+    }
+
+
+    // START SJSAS 6324911
+    /**
+     * Adds the given error page to this StandardHost.
+     *
+     * @param errorPage The error page definition to be added
+     */
+    public void addErrorPage(ErrorPage errorPage) {
+        // Validate the input parameters
+        if (errorPage == null) {
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.ERROR_PAGE_CANNOT_BE_NULL_EXCEPTION));
+        }
+
+        // Add the specified error page to our internal collections
+        synchronized (statusPages) {
+            statusPages.put(errorPage.getErrorCode(), errorPage);
+        }
+        fireContainerEvent("addErrorPage", errorPage);
+
+    }
+
+    /**
+     * Gets the error page for the specified HTTP error code.
+     *
+     * @param errorCode Error code to look up
+     *
+     * @return The error page that is mapped to the specified HTTP error
+     * code, or null if no error page exists for that HTTP error code
+     */
+    public ErrorPage findErrorPage(int errorCode) {
+        return statusPages.get(Integer.valueOf(errorCode));
+    }
+    // END SJSAS 6324911
+
+
+    /**
+     * Configures the Secure attribute of the given SSO cookie.
+     *
+     * @param cookie the SSO cookie to be configured
+     * @param hreq the HttpServletRequest that has initiated the SSO session
+     */
+    public void configureSingleSignOnCookieSecure(Cookie cookie,
+                                                  HttpServletRequest hreq) {
+        cookie.setSecure(hreq.isSecure());
+    }
+
+
+    /**
+     * Configures the HttpOnly attribute of the given SSO cookie.
+     *
+     * @param cookie the SSO ccokie to be configured
+     */
+    public void configureSingleSignOnCookieHttpOnly(Cookie cookie) {
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    @Override
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder();
+        if (getParent() != null) {
+            sb.append(getParent().toString());
+            sb.append(".");
+        }
+        sb.append("StandardHost[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    /**
+     * Start this host.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    @Override
+    public synchronized void start() throws LifecycleException {
+        if( started ) {
+            return;
+        }
+        if( ! initialized )
+            init();
+
+        // Set error report valve
+        configureStandardHostValve((StandardHostValve) pipeline.getBasic());
+
+        // START SJSAS_PE 8.1 5034793
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, LogFacade.XML_VALIDATION_ENABLED);
+        }
+        // END SJSAS_PE 8.1 5034793 
+
+        super.start();
+
+    }
+
+     @Override
+     public synchronized void stop() throws LifecycleException {
+         super.stop();
+
+         if(oname != null) {
+             // Send j2ee.object.deleted notification
+             Notification notification =
+                     new Notification("j2ee.object.deleted", this, sequenceNumber++);
+             sendNotification(notification);
+         }
+     }
+
+     public void sendNotification(Notification notification) {
+
+         if (broadcaster == null) {
+             broadcaster = ((StandardEngine)getParent()).getService().getBroadcaster();
+         }
+         if (broadcaster != null) {
+             broadcaster.sendNotification(notification);
+         }
+         return;
+     }
+
+
+    // ------------------------------------------------------- Deployer Methods
+
+
+    /**
+     * Install a new web application, whose web application archive is at the
+     * specified URL, into this container with the specified context path.
+     * A context path of "" (the empty string) should be used for the root
+     * application for this container.  Otherwise, the context path must
+     * start with a slash.
+     * <p>
+     * If this application is successfully installed, a ContainerEvent of type
+     * <code>INSTALL_EVENT</code> will be sent to all registered listeners,
+     * with the newly created <code>Context</code> as an argument.
+     *
+     * @param contextPath The context path to which this application should
+     *  be installed (must be unique)
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalStateException if the specified context path
+     *  is already attached to an existing web application
+     * @exception IOException if an input/output error was encountered
+     *  during install
+     */
+    public void install(String contextPath, URL war) throws IOException {
+        getDeployer().install(contextPath, war);
+
+    }
+
+
+    /**
+     * <p>Install a new web application, whose context configuration file
+     * (consisting of a <code>&lt;Context&gt;</code> element) and web
+     * application archive are at the specified URLs.</p>
+     *
+     * <p>If this application is successfully installed, a ContainerEvent
+     * of type <code>INSTALL_EVENT</code> will be sent to all registered
+     * listeners, with the newly created <code>Context</code> as an argument.
+     * </p>
+     *
+     * @param config A URL that points to the context configuration file to
+     *  be used for configuring the new Context
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed
+     *
+     * @exception IllegalArgumentException if one of the specified URLs is
+     *  null
+     * @exception IllegalStateException if the context path specified in the
+     *  context configuration file is already attached to an existing web
+     *  application
+     * @exception IOException if an input/output error was encountered
+     *  during installation
+     */
+    public synchronized void install(URL config, URL war) throws IOException {
+
+        getDeployer().install(config, war);
+
+    }
+
+
+    /**
+     * Return the Context for the deployed application that is associated
+     * with the specified context path (if any); otherwise return
+     * <code>null</code>.
+     *
+     * @param contextPath The context path of the requested web application
+     */
+    public Context findDeployedApp(String contextPath) {
+
+        return (getDeployer().findDeployedApp(contextPath));
+
+    }
+
+
+    /**
+     * Return the context paths of all deployed web applications in this
+     * Container.  If there are no deployed applications, a zero-length
+     * array is returned.
+     */
+    public String[] findDeployedApps() {
+
+        return (getDeployer().findDeployedApps());
+
+    }
+
+
+    /**
+     * Remove an existing web application, attached to the specified context
+     * path.  If this application is successfully removed, a
+     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+     * registered listeners, with the removed <code>Context</code> as
+     * an argument.
+     *
+     * @param contextPath The context path of the application to be removed
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  removal
+     */
+    public void remove(String contextPath) throws IOException {
+
+        getDeployer().remove(contextPath);
+
+    }
+
+
+    /**
+     * Remove an existing web application, attached to the specified context
+     * path.  If this application is successfully removed, a
+     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+     * registered listeners, with the removed <code>Context</code> as
+     * an argument. Deletes the web application war file and/or directory
+     * if they exist in the Host's appBase.
+     *
+     * @param contextPath The context path of the application to be removed
+     * @param undeploy boolean flag to remove web application from server
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  removal
+     */
+    public void remove(String contextPath, boolean undeploy) throws IOException {
+
+        getDeployer().remove(contextPath,undeploy);
+
+    }
+
+
+    /**
+     * Start an existing web application, attached to the specified context
+     * path.  Only starts a web application if it is not running.
+     *
+     * @param contextPath The context path of the application to be started
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  startup
+     */
+    public void start(String contextPath) throws IOException {
+
+        getDeployer().start(contextPath);
+
+    }
+
+
+    /**
+     * Stop an existing web application, attached to the specified context
+     * path.  Only stops a web application if it is running.
+     *
+     * @param contextPath The context path of the application to be stopped
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs while stopping
+     *  the web application
+     */
+    public void stop(String contextPath) throws IOException {
+
+        getDeployer().stop(contextPath);
+
+    }
+
+    
+    /**
+     * Returns the value of the securePagesWithPragma property.
+     */
+    public boolean isSecurePagesWithPragma() {
+
+        return (this.securePagesWithPragma);
+    }
+
+
+    /**
+     * Sets the securePagesWithPragma property of this Context.
+     *
+     * Setting this property to true will result in Pragma and Cache-Control
+     * headers with a value of "No-cache" if proxy caching has been disabled.
+     *
+     * Setting this property to false will not add any Pragma header,
+     * but will set the Cache-Control header to "private".
+     *
+     * @param securePagesWithPragma true if Pragma and Cache-Control headers
+     * are to be set to "No-cache" if proxy caching has been disabled, false
+     * otherwise
+     */
+    public void setSecurePagesWithPragma(boolean securePagesWithPragma) {
+
+        boolean oldSecurePagesWithPragma = this.securePagesWithPragma;
+        this.securePagesWithPragma = securePagesWithPragma;
+        support.firePropertyChange("securePagesWithPragma",
+                                   Boolean.valueOf(oldSecurePagesWithPragma),
+                                   Boolean.valueOf(this.securePagesWithPragma));
+    }
+
+
+    @Override
+    public void addValve(GlassFishValve valve) {
+        super.addValve(valve);
+        if (valve instanceof SingleSignOn) {
+            sso = (SingleSignOn) valve;
+        }
+    }
+
+
+    @Override
+    public void removeValve(GlassFishValve valve) {
+        super.removeValve(valve);
+        if (valve instanceof SingleSignOn) {
+            sso = null;
+        }
+    }
+
+
+    public SingleSignOn getSingleSignOn() {
+        return sso;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    static String STANDARD_HOST_DEPLOYER="org.apache.catalina.core.StandardHostDeployer";
+    
+    public Deployer getDeployer() {
+        if( deployer!= null )
+            return deployer;
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.CREATE_HOST_DEPLOYER_INFO);
+        }
+        try {
+            Class<?> c=Class.forName( STANDARD_HOST_DEPLOYER );
+            deployer=(Deployer)c.newInstance();
+            Method m=c.getMethod("setHost", new Class[] {Host.class} );
+            m.invoke( deployer,  new Object[] { this } );
+        } catch( Throwable t ) {
+            log.log(Level.SEVERE, LogFacade.ERROR_CREATING_DEPLOYER_EXCEPTION, t);
+        }
+        return deployer;
+    }
+    
+    public void setDeployer(Deployer d) {
+        this.deployer=d;
+    }
+
+    // -------------------- JMX  --------------------
+    /**
+      * Return the MBean Names of the Valves associated with this Host
+      *
+      * @exception Exception if an MBean cannot be created or registered
+      */
+     public String [] getValveNames()
+         throws Exception
+    {
+         GlassFishValve [] valves = this.getValves();
+         String [] mbeanNames = new String[valves.length];
+         for (int i = 0; i < valves.length; i++) {
+             if( valves[i] == null ) continue;
+             if( ((ValveBase)valves[i]).getObjectName() == null ) continue;
+             mbeanNames[i] = ((ValveBase)valves[i]).getObjectName().toString();
+         }
+
+         return mbeanNames;
+
+     }
+
+    public String[] getAliases() {
+        return aliases;
+    }
+
+    /* CR 6368085
+    private boolean initialized=false;
+    */    
+
+    @Override
+    public void init() {
+        if( initialized ) return;
+        /* CR 6368085
+        initialized=true;
+        */
+        
+        if( oname==null ) {
+            // not registered in JMX yet - standalone mode
+            try {
+                StandardEngine engine=(StandardEngine)parent;
+                domain=engine.getName();
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, neutralizeForLog("Registering host " + getName()
+                            + " with domain " + domain));
+                }
+                oname=new ObjectName(domain + ":type=Host,host=" +
+                        this.getName());
+                // START CR 6368091
+                controller = oname;
+                // END CR 6368091
+                Notification notification =
+                        new Notification("j2ee.object.created", this, sequenceNumber++);
+                sendNotification(notification);
+            } catch(Throwable t) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_REGISTERING_HOST_EXCEPTION), getName());
+                log.log(Level.SEVERE, msg, t);
+            }
+        }
+        // START CR 6368085
+        initialized = true;
+        // END CR 6368085
+    }
+    
+    @Override
+    public ObjectName createObjectName(String domain, ObjectName parent)
+        throws Exception
+    {
+        if( log.isLoggable(Level.FINE))
+            log.log(Level.FINE, neutralizeForLog("Create ObjectName " + domain + " " + parent));
+        return new ObjectName( domain + ":type=Host,host=" + getName());
+    }
+
+    protected Object loadInstance(String className) throws Exception {
+        return Class.forName(className).newInstance();
+    }
+
+
+    // ------------------------------------------------------ Private Methods
+
+
+    private void configureStandardHostValve(StandardHostValve host) {
+        // Set error report valve
+        if ((errorReportValveClass != null)
+            && !"".equals(errorReportValveClass)) {
+            try {
+                GlassFishValve valve = (GlassFishValve)
+                    loadInstance(errorReportValveClass);
+                /* START SJSAS 6374691
+                addValve(valve);
+                */
+                // START SJSAS 6374691
+                host.setErrorReportValve(valve);
+                // END SJSAS 6374691
+            } catch (Throwable t) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.LOAD_SPEC_ERROR_REPORT_EXCEPTION),
+                                                  errorReportValveClass);
+                log.log(Level.SEVERE, msg, t);
+            }
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHostDeployer.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHostDeployer.java
new file mode 100644
index 0000000..71f58a9
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHostDeployer.java
@@ -0,0 +1,886 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.startup.ContextRuleSet;
+import org.apache.catalina.startup.ExpandWar;
+import org.apache.catalina.startup.NamingRuleSet;
+import org.apache.tomcat.util.digester.Digester;
+
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * <p>Implementation of <b>Deployer</b> that is delegated to by the
+ * <code>StandardHost</code> implementation class.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.4 $ $Date: 2006/03/12 01:27:01 $
+ */
+
+public class StandardHostDeployer implements Deployer {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------------- Constructors
+
+    public StandardHostDeployer() {
+    }
+
+    /**
+     * Create a new StandardHostDeployer associated with the specified
+     * StandardHost.
+     *
+     * @param host The StandardHost we are associated with
+     */
+    public StandardHostDeployer(StandardHost host) {
+
+        super();
+        this.host = host;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>ContextRuleSet</code> associated with our
+     * <code>digester</code> instance.
+     */
+    private ContextRuleSet contextRuleSet = null;
+
+
+     /**
+     * The <code>Digester</code> instance to use for deploying web applications
+     * to this <code>Host</code>.  <strong>WARNING</strong> - Usage of this
+     * instance must be appropriately synchronized to prevent simultaneous
+     * access by multiple threads.
+     */
+    private Digester digester = null;
+
+
+    /**
+     * The <code>StandardHost</code> instance we are associated with.
+     */
+    protected StandardHost host = null;
+
+
+    /**
+     * The <code>NamingRuleSet</code> associated with our
+     * <code>digester</code> instance.
+     */
+    private NamingRuleSet namingRuleSet = null;
+
+
+    /**
+     * The document base which should replace the value specified in the
+     * <code>Context</code> being added in the <code>addChild()</code> method,
+     * or <code>null</code> if the original value should remain untouched.
+     */
+    private String overrideDocBase = null;
+
+
+    /**
+     * The config file which should replace the value set for the config file
+     * of the <code>Context</code>being added in the <code>addChild()</code> 
+     * method, or <code>null</code> if the original value should remain 
+     * untouched.
+     */
+    private String overrideConfigFile = null;
+
+
+    // -------------------------------------------------------- Depoyer Methods
+
+    public Host getHost() {
+        return host;
+    }
+
+    public void setHost(Host host) {
+        this.host = (StandardHost)host;
+    }
+
+    /**
+     * Return the name of the Container with which this Deployer is associated.
+     */
+    public String getName() {
+
+        return (host.getName());
+
+    }
+
+
+    /**
+     * Install a new web application, whose web application archive is at the
+     * specified URL, into this container with the specified context path.
+     * A context path of "" (the empty string) should be used for the root
+     * application for this container.  Otherwise, the context path must
+     * start with a slash.
+     * <p>
+     * If this application is successfully installed, a ContainerEvent of type
+     * <code>PRE_INSTALL_EVENT</code> will be sent to registered listeners
+     * before the associated Context is started, and a ContainerEvent of type
+     * <code>INSTALL_EVENT</code> will be sent to all registered listeners
+     * after the associated Context is started, with the newly created
+     * <code>Context</code> as an argument.
+     *
+     * @param contextPath The context path to which this application should
+     *  be installed (must be unique)
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalStateException if the specified context path
+     *  is already attached to an existing web application
+     * @exception IOException if an input/output error was encountered
+     *  during installation
+     */
+    public synchronized void install(String contextPath, URL war)
+        throws IOException {
+
+        // Validate the format and state of our arguments
+        if (contextPath == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.CONTEXT_PATH_REQUIRED_EXCEPTION));
+        if (!contextPath.equals("") && !contextPath.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_CONTEXT_PATH_EXCEPTION), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+        if (findDeployedApp(contextPath) != null)
+        {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CONTEXT_PATH_ALREADY_USED_EXCEPTION), contextPath);
+            throw new IllegalStateException(msg);
+        }
+        if (war == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.URL_WEB_APP_ARCHIVE_REQUIRED_EXCEPTION));
+
+        // Calculate the document base for the new web application
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.INSTALLING_WEB_APP_INFO, new Object[] {contextPath, war.toString()});
+        }
+        String url = war.toString();
+        String docBase = null;
+        boolean isWAR = false;
+        if (url.startsWith("jar:")) {
+            url = url.substring(4, url.length() - 2);
+            if (!url.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_URL_WEB_APP_EXCEPTION), url);
+                throw new IllegalArgumentException(msg);
+            }
+            isWAR = true;
+        }
+        if (url.startsWith("file://"))
+            docBase = url.substring(7);
+        else if (url.startsWith("file:"))
+            docBase = url.substring(5);
+        else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_URL_WEB_APP_EXCEPTION), url);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Determine if directory/war to install is in the host appBase
+        boolean isAppBase = false;
+        File appBase = new File(host.getAppBase());
+        if (!appBase.isAbsolute())
+            appBase = new File(System.getProperty("catalina.base"),
+                            host.getAppBase());
+        File contextFile = new File(docBase);
+        File baseDir = contextFile.getParentFile();
+        if (appBase.getCanonicalPath().equals(baseDir.getCanonicalPath())) {
+            isAppBase = true;
+        }
+
+        // For security, if deployXML is false only allow directories
+        // and war files from the hosts appBase
+        if (!host.isDeployXML() && !isAppBase) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.HOST_WEB_APP_DIR_CAN_BE_INSTALLED_EXCEPTION), url);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Make sure contextPath and directory/war names match when
+        // installing from the host appBase
+        if (isAppBase && host.getAutoDeploy()) {
+            String filename = contextFile.getName();
+            if (isWAR) {
+                filename = filename.substring(0,filename.length()-4);
+            }
+            if (contextPath.length() == 0) {
+                if (!filename.equals("ROOT")) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.CONSTEXT_PATH_MATCH_DIR_WAR_NAME_EXCEPTION),
+                                                      new Object[] {"/", "ROOT"});
+                    throw new IllegalArgumentException(msg);
+                }
+            } else if (!filename.equals(contextPath.substring(1))) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CONSTEXT_PATH_MATCH_DIR_WAR_NAME_EXCEPTION),
+                                                  new Object[] {contextPath, filename});
+                throw new IllegalArgumentException(msg);
+            }
+        }
+
+        // Expand war file if host wants wars unpacked
+        if (isWAR && host.isUnpackWARs()) {
+            docBase = ExpandWar.expand(host, war, contextPath);
+        }
+
+        // Install the new web application
+        try {
+            Class clazz = Class.forName(host.getContextClass());
+            Context context = (Context) clazz.newInstance();
+            context.setPath(contextPath);
+            context.setDocBase(docBase);
+            if (context instanceof Lifecycle) {
+                clazz = Class.forName(host.getConfigClass());
+                LifecycleListener listener =
+                    (LifecycleListener) clazz.newInstance();
+                ((Lifecycle) context).addLifecycleListener(listener);
+            }
+            host.fireContainerEvent(PRE_INSTALL_EVENT, context);
+            host.addChild(context);
+            host.fireContainerEvent(INSTALL_EVENT, context);
+        } catch (ClassNotFoundException e) {
+            log.log(Level.INFO, "", e);
+        } catch (Exception e) {
+            log.log(Level.INFO, LogFacade.ERROR_INSTALLING_EXCEPTION, e);
+            throw new IOException(e.toString());
+        }
+
+    }
+
+
+    /**
+     * Install a new web application, whose web application archive is at the
+     * specified URL, into this container with the specified context path.
+     * A context path of "" (the empty string) should be used for the root
+     * application for this container.  Otherwise, the context path must
+     * start with a slash.
+     * <p>
+     * If this application is successfully installed, a ContainerEvent of type
+     * <code>PRE_INSTALL_EVENT</code> will be sent to registered listeners
+     * before the associated Context is started, and a ContainerEvent of type
+     * <code>INSTALL_EVENT</code> will be sent to all registered listeners
+     * after the associated Context is started, with the newly created
+     * <code>Context</code> as an argument.
+     *
+     * @param contextPath The context path to which this application should
+     *  be installed (must be unique)
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed
+     * @param configFile The path to a file to save the Context information.
+     *  If configFile is null, the Context information is saved in server.xml;
+     *  if it is NOT null, the Context information is saved in configFile.
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalStateException if the specified context path
+     *  is already attached to an existing web application
+     * @exception IOException if an input/output error was encountered
+     *  during installation
+     */
+    public synchronized void install(String contextPath, URL war,
+        String configFile) throws IOException {
+
+        // Validate the format and state of our arguments
+        if (contextPath == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.CONTEXT_PATH_REQUIRED_EXCEPTION));
+        if (!contextPath.equals("") && !contextPath.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_CONTEXT_PATH_EXCEPTION), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+        if (findDeployedApp(contextPath) != null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CONTEXT_PATH_ALREADY_USED_EXCEPTION), contextPath);
+            throw new IllegalStateException(msg);
+        }
+        if (war == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.URL_WEB_APP_ARCHIVE_REQUIRED_EXCEPTION));
+
+        // Calculate the document base for the new web application
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.INSTALLING_WEB_APP_INFO, new Object[] {contextPath, war.toString()});
+        }
+        String url = war.toString();
+        String docBase = null;
+        boolean isWAR = false;
+        if (url.startsWith("jar:")) {
+            url = url.substring(4, url.length() - 2);
+            if (!url.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_URL_WEB_APP_EXCEPTION), url);
+                throw new IllegalArgumentException(msg);
+            }
+            isWAR = true;
+        }
+        if (url.startsWith("file://"))
+            docBase = url.substring(7);
+        else if (url.startsWith("file:"))
+            docBase = url.substring(5);
+        else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_URL_WEB_APP_EXCEPTION), url);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Expand war file if host wants wars unpacked
+        if (isWAR && host.isUnpackWARs()) {
+            docBase = ExpandWar.expand(host, war, contextPath);
+        }
+
+        // Install the new web application
+        try {
+            Class clazz = Class.forName(host.getContextClass());
+            Context context = (Context) clazz.newInstance();
+            context.setPath(contextPath);
+            context.setDocBase(docBase);
+            context.setConfigFile(configFile);
+            if (context instanceof Lifecycle) {
+                clazz = Class.forName(host.getConfigClass());
+                LifecycleListener listener =
+                    (LifecycleListener) clazz.newInstance();
+                ((Lifecycle) context).addLifecycleListener(listener);
+            }
+            host.fireContainerEvent(PRE_INSTALL_EVENT, context);
+            host.addChild(context);
+            host.fireContainerEvent(INSTALL_EVENT, context);
+
+            // save context info into configFile
+            //Engine engine = (Engine)host.getParent();
+            //StandardServer server = (StandardServer) engine.getService().getServer();
+            //server.storeContext(context);
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_DEPLOYING_APP_CONTEXT_PATH_EXCEPTION), contextPath);
+            log.log(Level.SEVERE, msg, e);
+            throw new IOException(e.toString());
+        }
+
+    }
+
+
+    /**
+     * <p>Install a new web application, whose context configuration file
+     * (consisting of a <code>&lt;Context&gt;</code> element) and (optional)
+     * web application archive are at the specified URLs.</p>
+     *
+     * If this application is successfully installed, a ContainerEvent of type
+     * <code>PRE_INSTALL_EVENT</code> will be sent to registered listeners
+     * before the associated Context is started, and a ContainerEvent of type
+     * <code>INSTALL_EVENT</code> will be sent to all registered listeners
+     * after the associated Context is started, with the newly created
+     * <code>Context</code> as an argument.
+     *
+     * @param config A URL that points to the context configuration descriptor
+     *  to be used for configuring the new Context
+     * @param war A URL of type "jar:" that points to a WAR file, or type
+     *  "file:" that points to an unpacked directory structure containing
+     *  the web application to be installed, or <code>null</code> to use
+     *  the <code>docBase</code> attribute from the configuration descriptor
+     *
+     * @exception IllegalArgumentException if one of the specified URLs is
+     *  null
+     * @exception IllegalStateException if the context path specified in the
+     *  context configuration file is already attached to an existing web
+     *  application
+     * @exception IOException if an input/output error was encountered
+     *  during installation
+     */
+    public synchronized void install(URL config, URL war) throws IOException {
+
+        // Validate the format and state of our arguments
+        if (config == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.URL_CONFIG_FILE_REQUIRED_EXCEPTION));
+
+        if (!host.isDeployXML())
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.USE_CONFIG_FILE_NOT_ALLOWED));
+
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.PROCESSING_CONTEXT_CONFIG_INFO, config);
+        }
+
+        // Calculate the document base for the new web application (if needed)
+        String docBase = null; // Optional override for value in config file
+        boolean isWAR = false;
+        if (war != null) {
+            String url = war.toString();
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.INSTALLING_WEB_APP_FROM_URL_INFO, url);
+            }
+            // Calculate the WAR file absolute pathname
+            if (url.startsWith("jar:")) {
+                url = url.substring(4, url.length() - 2);
+                isWAR = true;
+            }
+            if (url.startsWith("file://"))
+                docBase = url.substring(7);
+            else if (url.startsWith("file:"))
+                docBase = url.substring(5);
+            else
+                throw new IllegalArgumentException
+                        (rb.getString(LogFacade.INVALID_URL_WEB_APP_EXCEPTION));
+
+        }
+
+        // Expand war file if host wants wars unpacked
+        if (isWAR && host.isUnpackWARs()) {
+            docBase = ExpandWar.expand(host, war);
+        }
+
+        // Install the new web application
+        this.overrideDocBase = docBase;
+        if (config.toString().startsWith("file:")) {
+            this.overrideConfigFile = config.getFile();
+        }
+
+        InputStream stream = null;
+        try {
+            stream = config.openStream();
+            Digester digester = createDigester();
+            digester.setDebug(host.getDebug());
+            digester.setClassLoader(this.getClass().getClassLoader());
+            digester.clear();
+            digester.push(this);
+            digester.parse(stream);
+            stream.close();
+            stream = null;
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_DEPLOYING_APP_CONTEXT_PATH_EXCEPTION), docBase);
+            host.log(msg, e);
+            throw new IOException(e.toString());
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (Throwable t) {
+                    ;
+                }
+            }
+            this.overrideDocBase = null;
+            this.overrideConfigFile = null;
+        }
+
+    }
+
+
+    /**
+     * Return the Context for the deployed application that is associated
+     * with the specified context path (if any); otherwise return
+     * <code>null</code>.
+     *
+     * @param contextPath The context path of the requested web application
+     */
+    public Context findDeployedApp(String contextPath) {
+
+        return ((Context) host.findChild(contextPath));
+
+    }
+
+
+    /**
+     * Return the context paths of all deployed web applications in this
+     * Container.  If there are no deployed applications, a zero-length
+     * array is returned.
+     */
+    public String[] findDeployedApps() {
+
+        Container children[] = host.findChildren();
+        String results[] = new String[children.length];
+        for (int i = 0; i < children.length; i++)
+            results[i] = children[i].getName();
+        return (results);
+
+    }
+
+
+    /**
+     * Remove an existing web application, attached to the specified context
+     * path.  If this application is successfully removed, a
+     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+     * registered listeners, with the removed <code>Context</code> as
+     * an argument.
+     *
+     * @param contextPath The context path of the application to be removed
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  removal
+     */
+    public void remove(String contextPath) throws IOException {
+
+        // Validate the format and state of our arguments
+        if (contextPath == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.CONTEXT_PATH_REQUIRED_EXCEPTION));
+        if (!contextPath.equals("") && !contextPath.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_CONTEXT_PATH_EXCEPTION), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Locate the context and associated work directory
+        Context context = findDeployedApp(contextPath);
+        if (context == null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CONTEXT_PATH_NOT_IN_USE), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Remove this web application
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.REMOVING_WEB_APP_INFO, contextPath);
+        }
+        try {
+            host.removeChild(context);
+            host.fireContainerEvent(REMOVE_EVENT, context);
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_REMOVING_APP_EXCEPTION), contextPath);
+            log.log(Level.SEVERE, msg, e);
+            throw new IOException(e.toString());
+        }
+
+    }
+
+
+    /**
+     * Remove an existing web application, attached to the specified context
+     * path.  If this application is successfully removed, a
+     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+     * registered listeners, with the removed <code>Context</code> as
+     * an argument. Deletes the web application war file and/or directory
+     * if they exist in the Host's appBase.
+     *
+     * @param contextPath The context path of the application to be removed
+     * @param undeploy boolean flag to remove web application from server
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  removal
+     */
+    public void remove(String contextPath, boolean undeploy)
+        throws IOException {
+
+        // Validate the format and state of our arguments
+        if (contextPath == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.CONTEXT_PATH_REQUIRED_EXCEPTION));
+        if (!contextPath.equals("") && !contextPath.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_CONTEXT_PATH_EXCEPTION), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Locate the context and associated work directory
+        Context context = findDeployedApp(contextPath);
+        if (context == null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CONTEXT_PATH_NOT_IN_USE), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+
+        // Remove this web application
+        String msgInfo = MessageFormat.format(rb.getString(LogFacade.REMOVING_WEB_APP_INFO), contextPath);
+        host.log(msgInfo);
+        try {
+            // Get the work directory for the Context
+            File workDir = 
+                (File) context.getServletContext().getAttribute
+                (ServletContext.TEMPDIR);
+            String configFile = context.getConfigFile();
+            host.removeChild(context);
+
+            if (undeploy) {
+                // Remove the web application directory and/or war file if it
+                // exists in the Host's appBase directory.
+
+                // Determine if directory/war to remove is in the host appBase
+                boolean isAppBase = false;
+                File appBase = new File(host.getAppBase());
+                if (!appBase.isAbsolute())
+                    appBase = new File(System.getProperty("catalina.base"),
+                                       host.getAppBase());
+                File contextFile = new File(context.getDocBase());
+                File baseDir = contextFile.getParentFile();
+                if ((baseDir == null) 
+                    || (appBase.getCanonicalPath().equals
+                        (baseDir.getCanonicalPath()))) {
+                    isAppBase = true;
+                }
+
+                boolean isWAR = false;
+                if (contextFile.getName().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
+                    isWAR = true;
+                }
+                // Only remove directory and/or war if they are located in the
+                // Host's appBase autoDeploy is true
+                if (isAppBase && host.getAutoDeploy()) {
+                    String filename = contextFile.getName();
+                    if (isWAR) {
+                        filename = filename.substring(0,filename.length()-4);
+                    }
+                    if (contextPath.length() == 0 && filename.equals("ROOT") ||
+                        filename.equals(contextPath.substring(1))) {
+                        if (!isWAR) {
+                            long contextLastModified = 
+                                contextFile.lastModified();
+                            if (contextFile.isDirectory()) {
+                                deleteDir(contextFile);
+                            }
+                            if (host.isUnpackWARs()) {
+                                File contextWAR = 
+                                    new File(context.getDocBase() + ".war");
+                                if (contextWAR.exists()) {
+                                    if (contextLastModified 
+                                        > contextWAR.lastModified()) {
+                                        deleteFile(contextWAR);
+                                    }
+                                }
+                            }
+                        } else {
+                            deleteFile(contextFile);
+                        }
+                    }
+                    if (host.isDeployXML() && (configFile != null)) {
+                        File docBaseXml = new File(configFile);
+                        deleteFile(docBaseXml);
+                    }
+                }
+
+                // Remove the work directory for the Context
+                if (workDir == null &&
+                    context instanceof StandardContext &&
+                    ((StandardContext)context).getWorkDir() != null) {
+                    workDir = new File(((StandardContext)context).getWorkPath());
+                }
+                if (workDir != null && workDir.exists()) {
+                    deleteDir(workDir);
+                }
+            }
+
+            host.fireContainerEvent(REMOVE_EVENT, context);
+        } catch (Exception e) {
+            String msgException = MessageFormat.format(rb.getString(LogFacade.ERROR_REMOVING_APP_EXCEPTION), contextPath);
+            host.log(msgException, e);
+            throw new IOException(e.toString());
+        }
+
+    }
+
+
+    /**
+     * Start an existing web application, attached to the specified context
+     * path.  Only starts a web application if it is not running.
+     *
+     * @param contextPath The context path of the application to be started
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs during
+     *  startup
+     */
+    public void start(String contextPath) throws IOException {
+
+        // Validate the format and state of our arguments
+        if (contextPath == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.CONTEXT_PATH_REQUIRED_EXCEPTION));
+        if (!contextPath.equals("") && !contextPath.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_CONTEXT_PATH_EXCEPTION), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+        Context context = findDeployedApp(contextPath);
+        if (context == null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CONTEXT_PATH_NOT_IN_USE), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.STARTING_WEB_APP_INFO, contextPath);
+        }
+        try {
+            ((Lifecycle) context).start();
+        } catch (LifecycleException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.STARTING_WEB_APP_FAILED_EXCEPTION), contextPath);
+
+            log.log(Level.SEVERE, msg, e);
+            throw new IllegalStateException(msg, e);
+        }
+    }
+
+
+    /**
+     * Stop an existing web application, attached to the specified context
+     * path.  Only stops a web application if it is running.
+     *
+     * @param contextPath The context path of the application to be stopped
+     *
+     * @exception IllegalArgumentException if the specified context path
+     *  is malformed (it must be "" or start with a slash)
+     * @exception IllegalArgumentException if the specified context path does
+     *  not identify a currently installed web application
+     * @exception IOException if an input/output error occurs while stopping
+     *  the web application
+     */
+    public void stop(String contextPath) throws IOException {
+
+        // Validate the format and state of our arguments
+        if (contextPath == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.CONTEXT_PATH_REQUIRED_EXCEPTION));
+        if (!contextPath.equals("") && !contextPath.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_CONTEXT_PATH_EXCEPTION), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+        Context context = findDeployedApp(contextPath);
+        if (context == null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CONTEXT_PATH_NOT_IN_USE), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.STOPPING_WEB_APP_INFO, contextPath);
+
+        }
+        try {
+            ((Lifecycle) context).stop();
+        } catch (LifecycleException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.STOPPING_WEB_APP_FAILED_EXCEPTION), contextPath);
+            log.log(Level.SEVERE, msg, e);
+            throw new IllegalStateException(msg, e);
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Delegated Methods
+
+
+    /**
+     * Delegate a request to add a child Context to our associated Host.
+     *
+     * @param child The child Context to be added
+     */
+    public void addChild(Container child) {
+
+        Context context = null;
+        String contextPath = null;
+        if (child instanceof Context) {
+            context = (Context) child;
+            contextPath = context.getPath();
+        }
+        if (contextPath == null)
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.CONTEXT_PATH_REQUIRED_EXCEPTION));
+        else if (!contextPath.equals("") && !contextPath.startsWith("/")) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_CONTEXT_PATH_EXCEPTION), contextPath);
+            throw new IllegalArgumentException(msg);
+        }
+        if (host.findChild(contextPath) != null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CONTEXT_PATH_ALREADY_USED_EXCEPTION), contextPath);
+            throw new IllegalStateException(msg);
+        }
+        if (this.overrideDocBase != null)
+            context.setDocBase(this.overrideDocBase);
+        if (this.overrideConfigFile != null)
+            context.setConfigFile(this.overrideConfigFile);
+        host.fireContainerEvent(PRE_INSTALL_EVENT, context);
+        host.addChild(child);
+        host.fireContainerEvent(INSTALL_EVENT, context);
+
+    }
+
+
+    /**
+     * Delegate a request for the parent class loader to our associated Host.
+     */
+    public ClassLoader getParentClassLoader() {
+
+        return (host.getParentClassLoader());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Create (if necessary) and return a Digester configured to process the
+     * context configuration descriptor for an application.
+     */
+    protected Digester createDigester() {
+        if (digester == null) {
+            digester = new Digester();
+            if (host.getDebug() > 0)
+                digester.setDebug(3);
+            digester.setValidating(false);
+            contextRuleSet = new ContextRuleSet("");
+            digester.addRuleSet(contextRuleSet);
+            namingRuleSet = new NamingRuleSet("Context/");
+            digester.addRuleSet(namingRuleSet);
+        }
+        return (digester);
+
+    }
+
+
+    /**
+     * Delete the specified directory, including all of its contents and
+     * subdirectories recursively.
+     *
+     * @param dir File object representing the directory to be deleted
+     */
+    protected void deleteDir(File dir) {
+
+        String files[] = dir.list();
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; i < files.length; i++) {
+            File file = new File(dir, files[i]);
+            if (file.isDirectory()) {
+                deleteDir(file);
+            } else {
+                deleteFile(file);
+            }
+        }
+        deleteFile(dir);
+
+    }
+
+    protected void deleteFile(File dir) {
+        if (!dir.delete()) {
+            log.log(Level.WARNING, LogFacade.FAILED_REMOVE_FILE, dir.getAbsolutePath());
+        }
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHostValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHostValve.java
new file mode 100644
index 0000000..198ca37
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardHostValve.java
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import org.apache.catalina.*;
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.catalina.deploy.ErrorPage;
+import org.apache.catalina.util.ResponseUtil;
+import org.apache.catalina.valves.ValveBase;
+import org.glassfish.web.valve.GlassFishValve;
+
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.ResourceBundle;
+// END SJSAS 6374691
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardHost</code> container implementation.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>:  This implementation is likely to be useful only
+ * when processing HTTP requests.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.21 $ $Date: 2007/04/10 17:12:22 $
+ */
+
+final class StandardHostValve
+    extends ValveBase {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    private static final ClassLoader standardHostValveClassLoader =
+        StandardHostValve.class.getClassLoader();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardHostValve/1.0";
+
+
+    // START SJSAS 6374691
+    private GlassFishValve errorReportValve;
+    // END SJSAS 6374691
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Select the appropriate child Context to process this request,
+     * based on the specified request URI.  If no matching Context can
+     * be found, return an appropriate HTTP error.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    @Override
+    public int invoke(Request request, Response response)
+            throws IOException, ServletException {
+
+        Context context = preInvoke(request, response);
+        if (context == null) {
+            return END_PIPELINE;
+        }
+
+        // Ask this Context to process this request
+        if (context.getPipeline().hasNonBasicValves() ||
+                context.hasCustomPipeline()) {
+            context.getPipeline().invoke(request, response);
+        } else {
+            context.getPipeline().getBasic().invoke(request, response);
+        }
+
+        return END_PIPELINE;
+    }
+
+
+    /**
+     * Tomcat style invocation.
+     */
+    @Override
+    public void invoke(org.apache.catalina.connector.Request request,
+                       org.apache.catalina.connector.Response response)
+            throws IOException, ServletException {
+
+        Context context = preInvoke(request, response);
+        if (context == null) {
+            return;
+        }
+
+        // Ask this Context to process this request
+        if (context.getPipeline().hasNonBasicValves() ||
+                context.hasCustomPipeline()) {
+            context.getPipeline().invoke(request, response);
+        } else {
+            context.getPipeline().getBasic().invoke(request, response);
+        }
+
+        postInvoke(request, response);
+    }
+
+
+    @Override
+    public void postInvoke(Request request, Response response)
+        // START SJSAS 6374691
+        throws IOException, ServletException
+        // END SJSAS 6374691
+    {
+        try {
+            /*
+            // START SJSAS 6374990
+            if (((ServletResponse) response).isCommitted()) {
+                return;
+            }
+            // END SJSAS 6374990
+            */
+
+            HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+
+            // Error page processing
+            response.setSuspended(false);
+
+            Throwable t = (Throwable) hreq.getAttribute(
+                RequestDispatcher.ERROR_EXCEPTION);
+
+            if (t != null) {
+                throwable(request, response, t);
+            } else {
+                status(request, response);
+            }
+
+            // See IT 11423
+            boolean isDefaultErrorPageEnabled = true;
+            Wrapper wrapper = request.getWrapper();
+            if (wrapper != null) {
+                String initParam = wrapper.findInitParameter(Constants.IS_DEFAULT_ERROR_PAGE_ENABLED_INIT_PARAM);
+                if (initParam != null) {
+                    isDefaultErrorPageEnabled = Boolean.parseBoolean(initParam);
+                }
+            }
+
+            // START SJSAS 6374691
+            if (errorReportValve != null && response.isError() && isDefaultErrorPageEnabled) {
+                errorReportValve.postInvoke(request, response);
+            }
+            // END SJSAS 6374691
+
+            Context context = request.getContext();
+            if (context != null) {
+                context.fireRequestDestroyedEvent(hreq);
+            }
+        } finally {
+            Thread.currentThread().setContextClassLoader
+                (standardHostValveClassLoader);
+        }
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Handle the specified Throwable encountered while processing
+     * the specified Request to produce the specified Response.  Any
+     * exceptions that occur during generation of the exception report are
+     * logged and swallowed.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     * @param throwable The throwable that occurred (which possibly wraps
+     *  a root cause exception
+     */
+    protected void throwable(Request request, Response response,
+                             Throwable throwable) {
+        Context context = request.getContext();
+        if (context == null)
+            return;
+        
+        Throwable realError = throwable;
+        if (realError instanceof ServletException) {
+            realError = ((ServletException) realError).getRootCause();
+            if (realError == null) {
+                realError = throwable;
+            }
+        } 
+
+        // If this is an aborted request from a client just log it and return
+        if (realError instanceof ClientAbortException ) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, LogFacade.REMOTE_CLIENT_ABORTED_EXCEPTION, realError.getCause().getMessage());
+            }
+            return;
+        }
+
+        ErrorPage errorPage = findErrorPage(context, throwable);
+        if ((errorPage == null) && (realError != throwable)) {
+            errorPage = findErrorPage(context, realError);
+        }
+
+        if (errorPage != null) {
+            dispatchToErrorPage(request, response, errorPage, throwable,
+                                realError, 0);
+        } else if (context.getDefaultErrorPage() != null) {
+            dispatchToErrorPage(request, response,
+                context.getDefaultErrorPage(), throwable, realError, 0);  
+        } else {
+            // A custom error-page has not been defined for the exception
+            // that was thrown during request processing. Check if an
+            // error-page for error code 500 was specified and if so, 
+            // send that page back as the response.
+            ServletResponse sresp = (ServletResponse) response;
+
+            /* GlassFish 6386229
+            if (sresp instanceof HttpServletResponse) {
+                ((HttpServletResponse) sresp).setStatus(
+                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                // The response is an error
+                response.setError();
+
+                status(request, response);
+            }
+            */
+            // START GlassFish 6386229
+            ((HttpServletResponse) sresp).setStatus(
+                HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            // The response is an error
+            response.setError();
+
+            status(request, response);
+            // END GlassFish 6386229
+        }
+            
+
+    }
+
+
+    /**
+     * Handle the HTTP status code (and corresponding message) generated
+     * while processing the specified Request to produce the specified
+     * Response.  Any exceptions that occur during generation of the error
+     * report are logged and swallowed.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     */
+    protected void status(Request request, Response response) {
+
+        // Handle a custom error page for this status code
+        Context context = request.getContext();
+        if (context == null)
+            return;
+
+        /*
+         * Only look for error pages when isError() is set.
+         * isError() is set when response.sendError() is invoked.
+         */
+        if (!response.isError()) {
+            return;
+        }
+
+        int statusCode = ((HttpResponse) response).getStatus();
+        ErrorPage errorPage = context.findErrorPage(statusCode);
+        if (errorPage != null) {
+            if (errorPage.getLocation() != null) {
+                File file = new File(context.getDocBase(), errorPage.getLocation());
+                if (!file.exists()) {
+                    File file2 = new File(errorPage.getLocation());
+                    if (!file2.exists()) {
+                        log.log(Level.WARNING, LogFacade.ERROR_PAGE_NOT_EXIST,
+                                new Object[]{file.getAbsolutePath(), file2.getAbsolutePath()});
+                    }
+                }
+            }
+            setErrorPageContentType(response, errorPage.getLocation(), context);
+            dispatchToErrorPage(request, response, errorPage, null, null, statusCode);
+        } else if (statusCode >= 400 && statusCode < 600 &&
+                context.getDefaultErrorPage() != null) {
+            dispatchToErrorPage(request, response,
+                context.getDefaultErrorPage(), null, null, statusCode);
+        }
+        // START SJSAS 6324911
+        else {
+            errorPage = ((StandardHost) getContainer()).findErrorPage(
+                                                        statusCode);
+            if (errorPage != null) {
+                if (errorPage.getLocation() != null) {
+                    File file = new File(context.getDocBase(), errorPage.getLocation());
+                    if (!file.exists()) { 
+                        File file2 = new File(errorPage.getLocation());
+                        if (!file2.exists()) {
+                            log.log(Level.WARNING, LogFacade.ERROR_PAGE_NOT_EXIST,
+                                    new Object[]{file.getAbsolutePath(), file2.getAbsolutePath()});
+                        }
+                    }
+                }
+                try {
+                    setErrorPageContentType(response, errorPage.getLocation(), context);
+                    handleHostErrorPage(response, errorPage, statusCode);
+                } catch (Exception e) {
+                    log("Exception processing " + errorPage, e);
+                }
+            }
+        }
+        // END SJSAS 6324911
+    }
+
+
+    /**
+     * Find and return the ErrorPage instance for the specified exception's
+     * class, or an ErrorPage instance for the closest superclass for which
+     * there is such a definition.  If no associated ErrorPage instance is
+     * found, return <code>null</code>.
+     *
+     * @param context The Context in which to search
+     * @param exception The exception for which to find an ErrorPage
+     */
+    protected static ErrorPage findErrorPage
+        (Context context, Throwable exception) {
+
+        if (exception == null)
+            return (null);
+        Class<?> clazz = exception.getClass();
+        String name = clazz.getName();
+        while (!Object.class.equals(clazz)) {
+            ErrorPage errorPage = context.findErrorPage(name);
+            if (errorPage != null)
+                return (errorPage);
+            clazz = clazz.getSuperclass();
+            if (clazz == null)
+                break;
+            name = clazz.getName();
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Handle an HTTP status code or Java exception by forwarding control
+     * to the location included in the specified errorPage object.  It is
+     * assumed that the caller has already recorded any request attributes
+     * that are to be forwarded to this page.  Return <code>true</code> if
+     * we successfully utilized the specified error page location, or
+     * <code>false</code> if the default error report should be rendered.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     * @param errorPage The errorPage directive we are obeying
+     */
+    protected boolean custom(Request request, Response response,
+                             ErrorPage errorPage) {
+
+        if (debug >= 1)
+            log("Processing " + errorPage);
+
+        // Validate our current environment
+        /* GlassFish 6386229
+        if (!(request instanceof HttpRequest)) {
+            if (debug >= 1)
+                log(" Not processing an HTTP request --> default handling");
+            return (false);     // NOTE - Nothing we can do generically
+        }
+        */
+        HttpServletRequest hreq =
+            (HttpServletRequest) request.getRequest();
+        /* GlassFish 6386229
+        if (!(response instanceof HttpResponse)) {
+            if (debug >= 1)
+                log("Not processing an HTTP response --> default handling");
+            return (false);     // NOTE - Nothing we can do generically
+        }
+        */
+        HttpServletResponse hres =
+            (HttpServletResponse) response.getResponse();
+
+        ((HttpRequest) request).setPathInfo(errorPage.getLocation());
+
+        try {
+            Integer statusCodeObj = (Integer) hreq.getAttribute(
+                RequestDispatcher.ERROR_STATUS_CODE);
+            int statusCode = statusCodeObj.intValue();
+            String message = (String) hreq.getAttribute(
+                RequestDispatcher.ERROR_MESSAGE);
+            hres.setStatus(statusCode, message);
+
+            // Forward control to the specified location
+            ServletContext servletContext =
+                request.getContext().getServletContext();
+            ApplicationDispatcher dispatcher = (ApplicationDispatcher)
+                servletContext.getRequestDispatcher(errorPage.getLocation());
+
+            if (hres.isCommitted()) {
+                // Response is committed - including the error page is the
+                // best we can do 
+                dispatcher.include(hreq, hres);
+            } else {
+                // Reset the response (keeping the real error code and message)
+                response.resetBuffer(true);
+
+                dispatcher.dispatch(hreq, hres, DispatcherType.ERROR);
+
+                // If we forward, the response is suspended again
+                response.setSuspended(false);
+            }
+
+            // Indicate that we have successfully processed this custom page
+            return (true);
+
+        } catch (Throwable t) {
+            // Report our failure to process this custom page
+            log("Exception Processing " + errorPage, t);
+            return (false);
+        }
+
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+        org.apache.catalina.Logger logger = container.getLogger();
+        if (logger != null) {
+            logger.log(this.toString() + ": " + message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, this.toString() + ": " + message);
+            }
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    protected void log(String message, Throwable t) {
+        org.apache.catalina.Logger logger = container.getLogger();
+        if (logger != null) {
+            logger.log(this.toString() + ": " + message, t,
+                org.apache.catalina.Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, this.toString() + ": " + message, t);
+        }
+    }
+
+
+    // START SJSAS 6324911
+    /**
+     * Copies the contents of the given error page to the response, and
+     * updates the status message with the reason string of the error page.
+     *
+     * @param response The response object
+     * @param errorPage The error page whose contents are to be copied
+     * @param statusCode The status code
+     */
+    private void handleHostErrorPage(Response response,
+                                     ErrorPage errorPage,
+                                     int statusCode)
+            throws Exception {
+
+        ServletOutputStream ostream = null;
+        PrintWriter writer = null;
+        FileReader reader = null;
+        BufferedInputStream istream = null;
+        IOException ioe = null;
+
+        if (!response.getResponse().isCommitted()) {
+            response.resetBuffer(true);
+        }
+        String message = errorPage.getReason();
+        if (message != null) {
+            ((HttpResponse) response).reset(statusCode, message);
+        }
+         
+        try {
+            ostream = response.getResponse().getOutputStream();
+        } catch (IllegalStateException e) {
+            // If it fails, we try to get a Writer instead if we're
+            // trying to serve a text file
+            writer = response.getResponse().getWriter();
+        }
+
+        if (writer != null) {
+            reader = new FileReader(errorPage.getLocation());
+            ioe = ResponseUtil.copy(reader, writer);
+            try {
+                reader.close();
+            } catch (Throwable t) {
+                ;
+            }
+        } else {
+            istream = new BufferedInputStream(
+                new FileInputStream(errorPage.getLocation()));
+            ioe = ResponseUtil.copy(istream, ostream);
+            try {
+                istream.close();
+            } catch (Throwable t) {
+                ;
+            }
+        }
+
+        // Rethrow any exception that may have occurred
+        if (ioe != null) {
+            throw ioe;
+        }
+    }
+    // END SJSAS 6324911
+
+
+    // START SJSAS 6374691
+    void setErrorReportValve(GlassFishValve errorReportValve) {
+        this.errorReportValve = errorReportValve;
+    }
+    // END SJSAS 6374691
+
+
+    private Context preInvoke(Request request, Response response)
+            throws IOException, ServletException {
+
+        // Select the Context to be used for this Request
+        Context context = request.getContext();
+        if (context == null) {
+            // BEGIN S1AS 4878272
+            ((HttpServletResponse) response.getResponse()).sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            response.setDetailMessage(rb.getString(LogFacade.NO_CONTEXT_TO_PROCESS));
+            // END S1AS 4878272
+            return null;
+        }
+
+        // Bind the context CL to the current thread
+        if( context.getLoader() != null ) {
+            // Not started - it should check for availability first
+            // This should eventually move to Engine, it's generic.
+            Thread.currentThread().setContextClassLoader
+                    (context.getLoader().getClassLoader());
+        }
+                 
+        // START GlassFish Issue 1057
+        // Update the session last access time for our session (if any)
+        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+        hreq.getSession(false);
+        // END GlassFish Issue 1057
+
+        context.fireRequestInitializedEvent(hreq);
+
+        return context;
+    }
+
+
+    private void dispatchToErrorPage(Request request, Response response,
+            ErrorPage errorPage, Throwable throwable, Throwable realError,
+            int statusCode) {
+
+        response.setAppCommitted(false);
+
+        ServletRequest sreq = request.getRequest();
+        ServletResponse sresp = response.getResponse();
+
+        sreq.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
+                          errorPage.getLocation());
+        sreq.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
+                          ((HttpServletRequest) sreq).getRequestURI());
+        Wrapper wrapper = request.getWrapper();
+        if (wrapper != null) {
+            sreq.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
+                              wrapper.getName());
+        }
+
+        if (throwable != null) {
+            sreq.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
+                Integer.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
+            sreq.setAttribute(RequestDispatcher.ERROR_MESSAGE,
+                              throwable.getMessage());
+            sreq.setAttribute(RequestDispatcher.ERROR_EXCEPTION,
+                              realError);
+            sreq.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,
+                              realError.getClass());
+        } else {
+            sreq.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
+                              Integer.valueOf(statusCode));
+            String message = ((HttpResponse) response).getMessage();
+            if (message == null) {
+                message = "";
+            }
+            sreq.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
+        }
+
+        if (custom(request, response, errorPage)) {
+            try {
+                sresp.flushBuffer();
+            } catch (IOException e) {
+                log("Exception processing " + errorPage, e);
+            }
+        }
+    }
+
+    private void setErrorPageContentType(Response response,
+                                         String location, Context context) {
+
+        if (response.getContentType() == null && location != null) {
+            String str = location.substring(location.lastIndexOf('.') + 1);
+            str = context.findMimeMapping(str.toLowerCase(Locale.ENGLISH));
+            if(str != null)
+                ((ServletResponse) response).setContentType(str);
+        }
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardPipeline.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardPipeline.java
new file mode 100644
index 0000000..1afcb4b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardPipeline.java
@@ -0,0 +1,839 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.connector.*;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+import org.glassfish.web.valve.GlassFishValve;
+import org.glassfish.web.valve.GlassFishValveAdapter;
+import org.glassfish.web.valve.TomcatValveAdapter;
+
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpUpgradeHandler;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/** CR 6411114 (Lifecycle implementation moved to ValveBase)
+import org.apache.tomcat.util.modeler.Registry;
+*/
+
+
+/**
+ * Standard implementation of a processing <b>Pipeline</b> that will invoke
+ * a series of Valves that have been configured to be called in order.  This
+ * implementation can be used for any type of Container.
+ *
+ * <b>IMPLEMENTATION WARNING</b> - This implementation assumes that no
+ * calls to <code>addValve()</code> or <code>removeValve</code> are allowed
+ * while a request is currently being processed.  Otherwise, the mechanism
+ * by which per-thread state is maintained will need to be modified.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public class StandardPipeline
+    implements Pipeline, Contained, Lifecycle {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new StandardPipeline instance with no associated Container.
+     */
+    public StandardPipeline() {
+
+        this(null);
+
+    }
+
+
+    /**
+     * Construct a new StandardPipeline instance that is associated with the
+     * specified Container.
+     *
+     * @param container The container we should be associated with
+     */
+    public StandardPipeline(Container container) {
+
+        super();
+        setContainer(container);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The basic Valve (if any) associated with this Pipeline.
+     */
+    protected GlassFishValve basic = null;
+
+
+    /**
+     * The Container with which this Pipeline is associated.
+     */
+    protected Container container = null;
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info = "org.apache.catalina.core.StandardPipeline/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+    // START OF IASRI# 4647091
+    /*
+     * The per-thread execution state for processing through this pipeline.
+     * The actual value is a java.lang.Integer object containing the subscript
+     * into the <code>values</code> array, or a subscript equal to
+     * <code>values.length</code> if the basic Valve is currently being
+     * processed.
+     */
+    // protected ThreadLocal state = new ThreadLocal();
+    // END OF IASRI# 4647091
+
+    /**
+     * The set of Valves (not including the Basic one, if any) associated with
+     * this Pipeline.
+     */
+    protected GlassFishValve valves[] = new GlassFishValve[0];
+
+
+    // The first Tomcat-style valve in the pipeline, if any
+    private Valve firstTcValve;
+
+    // The last Tomcat-style valve (immediately preceding the basic valve)
+    // in the pipeline, if any
+    private Valve lastTcValve;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return descriptive information about this implementation class.
+     */
+    public String getInfo() {
+
+        return info;
+
+    }
+
+
+    // ------------------------------------------------------ Contained Methods
+
+
+    /**
+     * Return the Container with which this Pipeline is associated.
+     */
+    public Container getContainer() {
+
+        return (this.container);
+
+    }
+
+
+    /**
+     * Set the Container with which this Pipeline is associated.
+     *
+     * @param container The new associated container
+     */
+    public void setContainer(Container container) {
+        this.container = container;
+    }
+
+
+    // --------------------------------------------------- Lifecycle Methods
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners
+     * associated with this Pipeline.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+
+    /**
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception IllegalStateException if this component has already been
+     *  started
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public synchronized void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                    (rb.getString(LogFacade.PIPLINE_STARTED));
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        started = true;
+
+        // Start the Valves in our pipeline (including the basic), if any
+        for (int i = 0; i < valves.length; i++) {
+            if (valves[i] instanceof Lifecycle)
+                ((Lifecycle) valves[i]).start();
+            /** CR 6411114 (MBean registration moved to ValveBase.start())
+            registerValve(valves[i]);
+            */
+        }
+        if ((basic != null) && (basic instanceof Lifecycle))
+            ((Lifecycle) basic).start();
+        
+        /** CR 6411114 (MBean registration moved to ValveBase.start())
+        if( basic!=null )
+            registerValve(basic);
+        */
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception IllegalStateException if this component has not been started
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public synchronized void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                    (rb.getString(LogFacade.PIPLINE_NOT_STARTED));
+
+        started = false;
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+
+        // Stop the Valves in our pipeline (including the basic), if any
+        if ((basic != null) && (basic instanceof Lifecycle)) 
+            ((Lifecycle) basic).stop();
+        /** CR 6411114 (MBean deregistration moved to ValveBase.stop())
+        if( basic!=null ) {
+            unregisterValve(basic);
+        }
+        */
+        for (int i = 0; i < valves.length; i++) {
+            if (valves[i] instanceof Lifecycle)
+                ((Lifecycle) valves[i]).stop();
+            /** CR 6411114 (MBean deregistration moved to ValveBase.stop())
+            unregisterValve(valves[i]);
+            */
+        
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+    }
+
+
+    // ------------------------------------------------------- Pipeline Methods
+
+
+    /**
+     * <p>Return the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).
+     */
+    public GlassFishValve getBasic() {
+
+        return (this.basic);
+
+    }
+
+
+    /**
+     * <p>Set the Valve instance that has been distinguished as the basic
+     * Valve for this Pipeline (if any).  Prior to setting the basic Valve,
+     * the Valve's <code>setContainer()</code> will be called, if it
+     * implements <code>Contained</code>, with the owning Container as an
+     * argument.  The method may throw an <code>IllegalArgumentException</code>
+     * if this Valve chooses not to be associated with this Container, or
+     * <code>IllegalStateException</code> if it is already associated with
+     * a different Container.</p>
+     *
+     * @param valve Valve to be distinguished as the basic Valve
+     */
+    public void setBasic(GlassFishValve valve) {
+
+        // Change components if necessary
+        GlassFishValve oldBasic = null;
+        synchronized (this) {
+            oldBasic = this.basic;
+        }
+        if (oldBasic == valve) {
+            return;
+        }
+
+        // Stop the old component if necessary
+        if (oldBasic != null) {
+            synchronized (this) {
+                if (started && (oldBasic instanceof Lifecycle)) {
+                    try {
+                        ((Lifecycle) oldBasic).stop();
+                    } catch (LifecycleException e) {
+                        log.log(Level.SEVERE, LogFacade.SET_BASIC_STOP_EXCEPTION, e);
+                    }
+                }
+            }
+            if (oldBasic instanceof Contained) {
+                try {
+                    ((Contained) oldBasic).setContainer(null);
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+        }
+
+        // Start the new component if necessary
+        if (valve == null) {
+            return;
+        }
+        if (valve instanceof Contained) {
+            ((Contained) valve).setContainer(this.container);
+        }
+        /** CR 6411114
+        if (valve instanceof Lifecycle) {
+        */
+        // START CR 6411114
+        // Start the valve if the pipeline has already been started
+        if (started && (valve instanceof Lifecycle)) {
+        // END CR 6411114
+            try {
+                ((Lifecycle) valve).start();
+            } catch (LifecycleException e) {
+                log.log(Level.SEVERE, LogFacade.SET_BASIC_START_EXCEPTION, e);
+                return;
+            }
+        }
+
+        synchronized (this) {
+            this.basic = valve;
+        }
+
+    }
+
+
+    /**
+     * <p>Add a new Valve to the end of the pipeline associated with this
+     * Container.  Prior to adding the Valve, the Valve's
+     * <code>setContainer()</code> method will be called, if it implements
+     * <code>Contained</code>, with the owning Container as an argument.
+     * The method may throw an
+     * <code>IllegalArgumentException</code> if this Valve chooses not to
+     * be associated with this Container, or <code>IllegalStateException</code>
+     * if it is already associated with a different Container.</p>
+     *
+     * @param valve Valve to be added
+     *
+     * @exception IllegalArgumentException if this Container refused to
+     *  accept the specified Valve
+     * @exception IllegalArgumentException if the specified Valve refuses to be
+     *  associated with this Container
+     * @exception IllegalStateException if the specified Valve is already
+     *  associated with a different Container
+     */
+    public void addValve(GlassFishValve valve) {
+    
+        if (firstTcValve != null) {
+            // Wrap GlassFish-style valve inside Tomcat-style valve
+            addValve(new TomcatValveAdapter(valve));
+            return;
+        }
+
+        // Validate that we can add this Valve
+        if (valve instanceof Contained)
+            ((Contained) valve).setContainer(this.container);
+
+        // Start the new component if necessary
+        if (started) {
+            if (valve instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) valve).start();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.ADD_VALVE_EXCEPTION, e);
+                }
+            }
+            /** CR 6411114 (MBean registration moved to ValveBase.start())
+            // Register the newly added valve
+            registerValve(valve);
+            */
+        }
+
+        // Add this Valve to the set associated with this Pipeline
+        GlassFishValve results[] = new GlassFishValve[valves.length +1];
+        System.arraycopy(valves, 0, results, 0, valves.length);
+        results[valves.length] = valve;
+        valves = results;
+    }
+
+
+    /**
+     * Add Tomcat-style valve.
+     */
+    public synchronized void addValve(Valve valve) {
+
+        /*
+         * Check if this is a GlassFish-style valve that was compiled
+         * against the old org.apache.catalina.Valve interface (from
+         * GlassFish releases prior to V3), which has since been renamed
+         * to org.glassfish.web.valve.GlassFishValve (in V3)
+         */
+        if (isGlassFishValve(valve)) {
+            try {
+                addValve(new GlassFishValveAdapter(valve));
+            } catch (Exception e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ADD_TOMCAT_STYLE_VALVE_EXCEPTION), valve);
+                log.log(Level.SEVERE, msg, e);
+            }
+            return;
+        }
+
+        if (valve instanceof Contained)
+            ((Contained) valve).setContainer(this.container);
+
+        // Start the new Valve if necessary
+        if (started) {
+            if (valve instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) valve).start();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE,
+                            LogFacade.ADD_VALVE_EXCEPTION, e);
+                }
+            }
+        }
+
+        if (firstTcValve == null) {
+            firstTcValve = lastTcValve = valve;
+        } else {
+            lastTcValve.setNext(valve);
+            lastTcValve = valve;
+        }
+
+        if (basic != null) {
+            valve.setNext((Valve) basic);
+        }
+    }
+
+
+    /**
+     * Return the set of Valves in the pipeline associated with this
+     * Container, including the basic Valve (if any).  If there are no
+     * such Valves, a zero-length array is returned.
+     */
+    public GlassFishValve[] getValves() {
+        if (basic == null) {
+            return (valves);
+        }
+        GlassFishValve results[] = new GlassFishValve[valves.length + 1];
+        System.arraycopy(valves, 0, results, 0, valves.length);
+        results[valves.length] = basic;
+        return (results);
+    }
+
+
+    /**
+     * @return true if this pipeline has any non basic valves, false
+     * otherwise
+     */
+    public boolean hasNonBasicValves() {
+        return ((valves != null && valves.length > 0) || firstTcValve != null);
+    }
+
+
+    public ObjectName[] getValveObjectNames() {
+        ObjectName oname[]=new ObjectName[valves.length + 1];
+        for( int i=0; i<valves.length; i++ ) {
+            if( valves[i] instanceof ValveBase )
+                oname[i]=((ValveBase)valves[i]).getObjectName();
+        }
+        if( basic instanceof ValveBase )
+            oname[valves.length]=((ValveBase)basic).getObjectName();
+        return oname;
+    }
+
+    /**
+     * Cause the specified request and response to be processed by the Valves
+     * associated with this pipeline, until one of these valves causes the
+     * response to be created and returned.  The implementation must ensure
+     * that multiple simultaneous requests (on different threads) can be
+     * processed through the same Pipeline without interfering with each
+     * other's control flow.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet exception is thrown
+     */
+    public void invoke(Request request, Response response)
+            throws IOException, ServletException {
+        doInvoke(request,response,false);
+    }
+
+    public void doChainInvoke(Request request, Response response)
+            throws IOException, ServletException {
+        doInvoke(request,response,true);
+    }
+
+    private void doInvoke(Request request, Response response, boolean chaining)
+            throws IOException, ServletException {
+
+        if ((valves.length > 0) || (basic != null)) {
+            // Set the status so that if there are no valves (other than the
+            // basic one), the basic valve's request processing logic will
+            // be invoked
+            int status = GlassFishValve.INVOKE_NEXT;
+
+            // Iterate over all the valves in the pipeline and invoke
+            // each valve's processing logic and then move onto to the
+            // next valve in the pipeline only if the previous valve indicated
+            // that the pipeline should proceed.
+            int i;
+            for (i = 0; i < valves.length; i++) {
+                Request req = request;
+                Response resp = response;
+                if (chaining) {
+                    req = getRequest(request);
+                    resp = getResponse(request, response);
+                }
+                status = valves[i].invoke(req, resp);
+                if (status != GlassFishValve.INVOKE_NEXT)
+                    break;
+            }
+
+            // Save a reference to the valve[], to ensure that postInvoke()
+            // is invoked on the original valve[], in case a valve gets added
+            // or removed during the invocation of the basic valve (e.g.,
+            // in case access logging is enabled or disabled by some kind of
+            // admin servlet), in which case the indices used for postInvoke
+            // invocations below would be off
+            GlassFishValve[] savedValves = valves;
+
+            // Invoke the basic valve's request processing and post-request
+            // logic only if the pipeline was not aborted (i.e. no valve
+            // returned END_PIPELINE).
+            // In addition, the basic valve needs to be invoked by the
+            // pipeline only if no Tomcat-style valves have been added.
+            // Otherwise, it will be invoked by the last Tomcat-style valve
+            // directly.
+            if (status == GlassFishValve.INVOKE_NEXT) {
+                if (firstTcValve != null) {
+                    firstTcValve.invoke(
+                        (org.apache.catalina.connector.Request) request,
+                        (org.apache.catalina.connector.Response) response);
+                } else if (basic != null) {
+                    Request req = request;
+                    Response resp = response;
+                    if (chaining) {
+                        req = getRequest(request);
+                        resp = getResponse(request, response);
+                    }
+                    basic.invoke(req, resp);
+                    basic.postInvoke(req, resp);
+                }
+            }
+
+            // Invoke the post-request processing logic only on those valves
+            // that returned a status of INVOKE_NEXT
+            for (int j = i - 1; j >= 0; j--) {
+                Request req = request;
+                Response resp = response;
+                if (chaining) {
+                    req = getRequest(request);
+                    resp = getResponse(request, response);
+                }
+
+                savedValves[j].postInvoke(req, resp);
+            }
+
+            savedValves = null;
+
+        } else {
+            throw new ServletException
+                    (rb.getString(LogFacade.NO_VALVES_IN_PIPELINE_EXCEPTION));
+        }
+
+        // Calls the protocol handler's init method if the request is marked to be upgraded
+        if (request instanceof org.apache.catalina.connector.Request) {
+            org.apache.catalina.connector.Request req = (org.apache.catalina.connector.Request) request;
+            if (req.isUpgrade()) {
+                HttpUpgradeHandler handler = req.getHttpUpgradeHandler();
+                if (handler != null) {
+                    WebConnectionImpl wc =
+                            new WebConnectionImpl(
+                                    req.getInputStream(),
+                                    ((org.apache.catalina.connector.Response)req.getResponse()).getOutputStream());
+                    wc.setRequest(req);
+                    req.setWebConnection(wc);
+                    if (response instanceof org.apache.catalina.connector.Response) {
+                        wc.setResponse((org.apache.catalina.connector.Response) response);
+                    }
+                    Context context = req.getContext();
+                    try {
+                        context.fireContainerEvent(ContainerEvent.BEFORE_UPGRADE_HANDLER_INITIALIZED, handler);
+                        handler.init(wc);
+                    } finally {
+                        context.fireContainerEvent(ContainerEvent.AFTER_UPGRADE_HANDLER_INITIALIZED, handler);
+                    }
+                } else {
+                    log.log(Level.SEVERE, LogFacade.PROTOCOL_HANDLER_REQUIRED_EXCEPTION);
+                }
+            }
+        }
+    }
+
+
+    private Request getRequest(Request request) {
+	Request r = (Request)
+	    request.getNote(Globals.WRAPPED_REQUEST);
+	if (r == null) {
+	    r = request;
+	}
+	return r;
+    }
+
+
+    private Response getResponse(Request request, Response response) {
+	Response r = (Response)
+	    request.getNote(Globals.WRAPPED_RESPONSE);
+	if (r == null) {
+	    r = response;
+	}
+	return r;
+    }
+
+
+    /**
+     * Remove the specified Valve from the pipeline associated with this
+     * Container, if it is found; otherwise, do nothing.  If the Valve is
+     * found and removed, the Valve's <code>setContainer(null)</code> method
+     * will be called if it implements <code>Contained</code>.
+     *
+     * @param valve Valve to be removed
+     */
+    public void removeValve(GlassFishValve valve) {
+
+        // Locate this Valve in our list
+        int j = -1;
+        for (int i = 0; i < valves.length; i++) {
+            if (valve == valves[i]) {
+                j = i;
+                break;
+            }
+        }
+        if (j < 0)
+            return;
+
+        // Remove this valve from our list
+        GlassFishValve results[] = new GlassFishValve[valves.length - 1];
+        int n = 0;
+        for (int i = 0; i < valves.length; i++) {
+            if (i == j)
+                continue;
+            results[n++] = valves[i];
+        }
+        valves = results;
+        try {
+            if (valve instanceof Contained)
+                ((Contained) valve).setContainer(null);
+        } catch (Throwable t) {
+            ;
+        }
+
+        // Stop this valve if necessary
+        if (started) {
+            if (valve instanceof ValveBase) {
+                if (((ValveBase)valve).isStarted()) {
+                    try {
+                        ((Lifecycle) valve).stop();
+                    } catch (LifecycleException e) {
+                        log.log(Level.SEVERE, LogFacade.REMOVE_VALVE_EXCEPTION, e);
+                    }
+                }
+            } else if (valve instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) valve).stop();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, LogFacade.REMOVE_VALVE_EXCEPTION, e);
+                }
+            }
+
+            /** CR 6411114 (MBean deregistration moved to ValveBase.stop())
+            // Unregister the removed valve
+            unregisterValve(valve);
+            */
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+        org.apache.catalina.Logger logger = null;
+        if (container != null) {
+            logger = container.getLogger();
+
+            String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_PIPELINE_INFO),
+                    new Object[] {container.getName(), message});
+
+            if (logger != null) {
+                logger.log(msg);
+            } else {
+                if (log.isLoggable(Level.INFO)) {
+                    log.log(Level.INFO, msg);
+                }
+            }
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_PIPELINE_NULL_INFO), message);
+                log.log(Level.INFO, msg);
+            }
+        }
+    }
+
+
+    /**
+     * Logs the given message to the Logger associated with the Container
+     * (if any) of this StandardPipeline.
+     *
+     * @param message the message
+     * @param t the Throwable
+     */
+    protected void log(String message, Throwable t) {
+        org.apache.catalina.Logger logger = null;
+        if (container != null) {
+            logger = container.getLogger();
+
+            String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_PIPELINE_INFO),
+                                              new Object[] {container.getName(), message});
+
+
+            if (logger != null) {
+                logger.log(msg, t, org.apache.catalina.Logger.WARNING);
+            } else {
+                log.log(Level.WARNING, msg, t);
+            }
+        } else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_PIPELINE_NULL_INFO), message);
+            log.log(Level.WARNING, msg, t);// INFO set to WARNING
+        }      
+    }                                                                     
+
+    // ------------------------------------------------------ Private Methods
+
+
+    /*
+     * Checks if the give valve is a GlassFish-style valve that was compiled
+     * against the old org.apache.catalina.Valve interface (from
+     * GlassFish releases prior to V3), which has since been renamed
+     * to org.glassfish.web.valve.GlassFishValve (in V3).
+     *
+     * The check is done by checking that it is not an abstract method with
+     * return type int. Note that invoke method in the original Tomcat-based
+     * Valve interface is declared to be void.
+     *
+     * @param valve the valve to check
+     *
+     * @return true if the given valve is a GlassFish-style valve, false
+     * otherwise
+     */
+    private boolean isGlassFishValve(Valve valve) {
+        try {
+            Method m = valve.getClass().getMethod(
+                        "invoke",
+                        org.apache.catalina.Request.class,
+                        org.apache.catalina.Response.class);
+            return (m != null && int.class.equals(m.getReturnType())
+                    && (!Modifier.isAbstract(m.getModifiers())));
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardServer.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardServer.java
new file mode 100644
index 0000000..29891ce
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardServer.java
@@ -0,0 +1,813 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.deploy.NamingResources;
+import org.apache.catalina.util.LifecycleSupport;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+
+import javax.management.ObjectName;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.AccessControlException;
+import java.security.SecureRandom;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Standard implementation of the <b>Server</b> interface, available for use
+ * (but not required) when deploying and starting Catalina.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.5 $ $Date: 2007/02/20 20:16:56 $
+ */
+
+public final class StandardServer
+    implements Lifecycle, Server
+ {
+     private static final Logger log = LogFacade.getLogger();
+     private static final ResourceBundle rb = log.getResourceBundle();
+
+     //--------------------------------------------------------------
+   
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * The set of class/property combinations that should <strong>NOT</strong>
+     * be persisted because they are automatically calculated.
+     */
+    private static String exceptions[][] = {
+        { "org.apache.catalina.core.StandardEngine", "domain" },
+        { "org.apache.catalina.core.StandardHost", "domain" },
+        { "org.apache.catalina.core.StandardContext", "available" },
+        { "org.apache.catalina.core.StandardContext", "configFile" },
+        { "org.apache.catalina.core.StandardContext", "configured" },
+        { "org.apache.catalina.core.StandardContext", "distributable" },
+        { "org.apache.catalina.core.StandardContext", "domain" },
+        { "org.apache.catalina.core.StandardContext", "engineName" },
+        { "org.apache.catalina.core.StandardContext", "name" },
+        { "org.apache.catalina.core.StandardContext", "override" },
+        { "org.apache.catalina.core.StandardContext", "publicId" },
+        { "org.apache.catalina.core.StandardContext", "replaceWelcomeFiles" },
+        { "org.apache.catalina.core.StandardContext", "sessionTimeout" },
+        { "org.apache.catalina.core.StandardContext", "startupTime" },
+        { "org.apache.catalina.core.StandardContext", "tldScanTime" },
+        { "org.apache.catalina.core.StandardContext", "workDir" },
+        { "org.apache.catalina.session.StandardManager", "distributable" },
+        { "org.apache.catalina.session.StandardManager", "entropy" },
+    };
+
+
+    /**
+     * The set of classes that represent persistable properties.
+     */
+    private static Class persistables[] = {
+        String.class,
+        Integer.class, Integer.TYPE,
+        Boolean.class, Boolean.TYPE,
+        Byte.class, Byte.TYPE,
+        Character.class, Character.TYPE,
+        Double.class, Double.TYPE,
+        Float.class, Float.TYPE,
+        Long.class, Long.TYPE,
+        Short.class, Short.TYPE,
+    };
+
+
+    /**
+     * The set of class names that should be skipped when persisting state,
+     * because the corresponding listeners, valves, etc. are configured
+     * automatically at startup time.
+     */
+    private static String skippables[] = {
+        "org.apache.catalina.authenticator.BasicAuthenticator",
+        "org.apache.catalina.authenticator.DigestAuthenticator",
+        "org.apache.catalina.authenticator.FormAuthenticator",
+        "org.apache.catalina.authenticator.NonLoginAuthenticator",
+        "org.apache.catalina.authenticator.SSLAuthenticator",
+        "org.apache.catalina.core.NamingContextListener",
+        "org.apache.catalina.core.StandardContextValve",
+        "org.apache.catalina.core.StandardEngineValve",
+        "org.apache.catalina.core.StandardHostValve",
+        "org.apache.catalina.startup.ContextConfig",
+        "org.apache.catalina.startup.EngineConfig",
+        "org.apache.catalina.startup.HostConfig",
+        "org.apache.catalina.valves.CertificatesValve",
+        "org.apache.catalina.valves.ErrorReportValve",
+        "org.apache.catalina.valves.RequestListenerValve",
+    };
+
+
+    /**
+     * The set of class names that are the standard implementations of 
+     * components, and hence should not be persisted.
+     */
+    private static String standardImplementations[] = {
+        "org.apache.catalina.core.StandardServer",
+        "org.apache.catalina.core.StandardService",
+        "org.apache.catalina.connector.CoyoteConnector",
+        "org.apache.catalina.core.StandardEngine",
+        "org.apache.catalina.core.StandardHost",
+        "org.apache.catalina.core.StandardContext"
+    };
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct a default instance of this class.
+     */
+    public StandardServer() {
+
+        super();
+        ServerFactory.setServer(this);
+
+        globalNamingResources = new NamingResources();
+        globalNamingResources.setContainer(this);
+
+        if (isUseNaming()) {
+            namingContextListener = new NamingContextListener();
+            namingContextListener.setDebug(getDebug());
+            addLifecycleListener(namingContextListener);
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Debugging detail level.
+     */
+    private int debug = 0;
+
+
+    /**
+     * Global naming resources context.
+     */
+    private javax.naming.Context globalNamingContext = null;
+
+
+    /**
+     * Global naming resources.
+     */
+    private NamingResources globalNamingResources = null;
+
+
+    /**
+     * Descriptive information about this Server implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardServer/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The naming context listener for this web application.
+     */
+    private NamingContextListener namingContextListener = null;
+
+
+    /**
+     * The port number on which we wait for shutdown commands.
+     */
+    private int port = 8005;
+
+
+    /**
+     * A random number generator that is <strong>only</strong> used if
+     * the shutdown command string is longer than 1024 characters.
+     */
+    private SecureRandom random = null;
+
+
+    /**
+     * The set of Services associated with this Server.
+     */
+    private Service services[] = new Service[0];
+
+    private final Object servicesMonitor = new Object();
+
+
+    /**
+     * The shutdown command string we are looking for.
+     */
+    private String shutdown = "SHUTDOWN";
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+
+    /**
+     * Has this component been initialized?
+     */
+    private boolean initialized = false;
+
+
+    /**
+     * The property change support for this component.
+     */
+    private PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the debugging detail level.
+     */
+    public int getDebug() {
+
+        return (this.debug);
+
+    }
+
+
+    /**
+     * Set the debugging detail level.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+
+        this.debug = debug;
+
+    }
+
+
+    /**
+     * Return the global naming resources context.
+     */
+    public javax.naming.Context getGlobalNamingContext() {
+
+        return (this.globalNamingContext);
+
+    }
+
+
+    /**
+     * Set the global naming resources context.
+     *
+     * @param globalNamingContext The new global naming resource context
+     */
+    public void setGlobalNamingContext
+        (javax.naming.Context globalNamingContext) {
+
+        this.globalNamingContext = globalNamingContext;
+
+    }
+
+
+    /**
+     * Return the global naming resources.
+     */
+    public NamingResources getGlobalNamingResources() {
+
+        return (this.globalNamingResources);
+
+    }
+
+
+    /**
+     * Set the global naming resources.
+     *
+     * @param globalNamingResources The new global naming resources
+     */
+    public void setGlobalNamingResources
+        (NamingResources globalNamingResources) {
+
+        NamingResources oldGlobalNamingResources =
+            this.globalNamingResources;
+        this.globalNamingResources = globalNamingResources;
+        this.globalNamingResources.setContainer(this);
+        support.firePropertyChange("globalNamingResources",
+                                   oldGlobalNamingResources,
+                                   this.globalNamingResources);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Server implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the port number we listen to for shutdown commands.
+     */
+    public int getPort() {
+
+        return (this.port);
+
+    }
+
+
+    /**
+     * Set the port number we listen to for shutdown commands.
+     *
+     * @param port The new port number
+     */
+    public void setPort(int port) {
+
+        this.port = port;
+
+    }
+
+
+    /**
+     * Return the shutdown command string we are waiting for.
+     */
+    public String getShutdown() {
+
+        return (this.shutdown);
+
+    }
+
+
+    /**
+     * Set the shutdown command we are waiting for.
+     *
+     * @param shutdown The new shutdown command
+     */
+    public void setShutdown(String shutdown) {
+
+        this.shutdown = shutdown;
+
+    }
+
+
+    // --------------------------------------------------------- Server Methods
+
+
+    /**
+     * Add a new Service to the set of defined Services.
+     *
+     * @param service The Service to be added
+     */
+    public void addService(Service service) {
+
+        service.setServer(this);
+
+        synchronized (servicesMonitor) {
+            Service results[] = new Service[services.length + 1];
+            System.arraycopy(services, 0, results, 0, services.length);
+            results[services.length] = service;
+            services = results;
+
+            if (initialized) {
+                try {
+                    service.initialize();
+                } catch (LifecycleException e) {
+                    String msg = MessageFormat.format(rb.getString(
+                            LogFacade.LIFECYCLE_EXCEPTION_DURING_SERVICE_INIT), e.toString());
+                    log.log(Level.SEVERE, msg, e);
+                }
+            }
+
+            if (started && (service instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) service).start();
+                } catch (LifecycleException e) {
+                    // Ignore
+                }
+            }
+
+            // Report this property change to interested listeners
+            support.firePropertyChange("service", null, service);
+        }
+
+    }
+
+
+    /**
+     * Wait until a proper shutdown command is received, then return.
+     */
+    public void await() {
+
+        // Set up a server socket to wait on
+        ServerSocket serverSocket = null;
+        try {
+            serverSocket =
+                new ServerSocket(port, 1,
+                                 InetAddress.getByName("127.0.0.1"));
+        } catch (IOException e) {
+            String msg = MessageFormat.format(
+                    rb.getString(LogFacade.STANDARD_SERVER_AWAIT_CREATE_EXCEPTION), port);
+            log.log(Level.SEVERE, msg, e);
+            System.exit(1);
+        }
+
+        // Loop waiting for a connection and a valid command
+        while (true) {
+
+            // Wait for the next connection
+            Socket socket = null;
+            InputStream stream = null;
+            try {
+                socket = serverSocket.accept();
+                socket.setSoTimeout(10 * 1000);  // Ten seconds
+                stream = socket.getInputStream();
+            } catch (AccessControlException ace) {
+                String msg = MessageFormat.format(
+                        rb.getString(LogFacade.STANDARD_SERVER_ACCEPT_SECURITY_EXCEPTION),
+                                     ace.getMessage());
+                log.log(Level.WARNING, msg, ace);
+                continue;
+            } catch (IOException e) {
+                String msg = MessageFormat.format(
+                        rb.getString(LogFacade.STANDARD_SERVER_AWAIT_ACCEPT_EXCEPTION),
+                                     e.toString());
+                log.log(Level.SEVERE, msg, e);
+                System.exit(1);
+            }
+
+            // Read a set of characters from the socket
+            StringBuilder command = new StringBuilder();
+            int expected = 1024; // Cut off to avoid DoS attack
+            while (expected < shutdown.length()) {
+                if (random == null)
+                    random = new SecureRandom();//use self seeding
+                    expected += random.nextInt(1024);
+            }
+            while (expected > 0) {
+                int ch = -1;
+                try {
+                    ch = stream.read();
+                } catch (IOException e) {
+                    String msg = MessageFormat.format(
+                            rb.getString(LogFacade.STANDARD_SERVER_AWAIT_READ_EXCEPTION),
+                                         e.toString());
+                    log.log(Level.WARNING, msg, e);
+                    ch = -1;
+                }
+                if (ch < 32)  // Control character or EOF terminates loop
+                    break;
+                command.append((char) ch);
+                expected--;
+            }
+
+            // Close the socket now that we are done with it
+            try {
+                socket.close();
+            } catch (IOException e) {
+                // Ignore
+            }
+
+            // Match against our command string
+            boolean match = command.toString().equals(shutdown);
+            if (match) {
+                break;
+            } else {
+                log.log(Level.WARNING, LogFacade.STANDARD_SERVER_AWAIT_INVALID_COMMAND_RECEIVED_EXCEPTION,
+                        neutralizeForLog(command.toString()));
+            }
+        }
+
+        // Close the server socket and return
+        try {
+            serverSocket.close();
+        } catch (IOException e) {
+            // Ignore
+        }
+
+    }
+
+
+    /**
+     * Return the specified Service (if it exists); otherwise return
+     * <code>null</code>.
+     *
+     * @param name Name of the Service to be returned
+     */
+    public Service findService(String name) {
+
+        if (name == null) {
+            return (null);
+        }
+        synchronized (servicesMonitor) {
+            for (int i = 0; i < services.length; i++) {
+                if (name.equals(services[i].getName())) {
+                    return (services[i]);
+                }
+            }
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Return the set of Services defined within this Server.
+     */
+    public Service[] findServices() {
+
+        return (services);
+
+    }
+    
+    /**
+     * @return the object names of all registered Service instances
+     */
+    public ObjectName[] getServiceNames() {
+        ObjectName onames[]=new ObjectName[ services.length ];
+        for( int i=0; i<services.length; i++ ) {
+            onames[i]=((StandardService)services[i]).getObjectName();
+        }
+        return onames;
+    }
+
+
+    /**
+     * Remove the specified Service from the set associated from this
+     * Server.
+     *
+     * @param service The Service to be removed
+     */
+    public void removeService(Service service) {
+
+        synchronized (servicesMonitor) {
+            int j = -1;
+            for (int i = 0; i < services.length; i++) {
+                if (service == services[i]) {
+                    j = i;
+                    break;
+                }
+            }
+            if (j < 0)
+                return;
+            if (services[j] instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) services[j]).stop();
+                } catch (LifecycleException e) {
+                    // Ignore
+                }
+            }
+            int k = 0;
+            Service results[] = new Service[services.length - 1];
+            for (int i = 0; i < services.length; i++) {
+                if (i != j)
+                    results[k++] = services[i];
+            }
+            services = results;
+
+            // Report this property change to interested listeners
+            support.firePropertyChange("service", service, null);
+        }
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("StandardServer[");
+        sb.append(getPort());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Return true if naming should be used.
+     */
+    private boolean isUseNaming() {
+        boolean useNaming = true;
+        // Reading the "catalina.useNaming" environment variable
+        String useNamingProperty = System.getProperty("catalina.useNaming");
+        if ((useNamingProperty != null)
+            && (useNamingProperty.equals("false"))) {
+            useNaming = false;
+        }
+        return useNaming;
+    }
+
+
+    // ---------------------------------------------------- Lifecycle Methods
+
+
+    /**
+     * Add a LifecycleEvent listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners
+     * associated with this StandardServer.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a LifecycleEvent listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called before any of the public
+     * methods of this component are utilized.  It should also send a
+     * LifecycleEvent of type START_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "This server has already been started");
+            }
+            return;
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Start our defined Services
+        synchronized (servicesMonitor) {
+            for (int i = 0; i < services.length; i++) {
+                if (services[i] instanceof Lifecycle)
+                    ((Lifecycle) services[i]).start();
+            }
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.  It should also send a LifecycleEvent
+     * of type STOP_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            return;
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Stop our defined Services
+        for (int i = 0; i < services.length; i++) {
+            if (services[i] instanceof Lifecycle)
+                ((Lifecycle) services[i]).stop();
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+    }
+
+    public void init() throws Exception {
+        initialize();
+    }
+    
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     */
+    public void initialize()
+        throws LifecycleException 
+    {
+        if (initialized) {
+            log.log(Level.INFO, LogFacade.STANDARD_SERVER_INITIALIZE_INITIALIZED);
+            return;
+        }
+        // START GlassFish 2439
+        lifecycle.fireLifecycleEvent(INIT_EVENT, null);
+        // END GlassFish 2439
+        initialized = true;
+
+        if( oname==null ) {
+            try {
+                oname=new ObjectName( "Catalina:type=Server");
+            } catch (Exception e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_REGISTERING), e.toString());
+                log.log(Level.SEVERE, msg, e);
+            }
+        }
+        
+        // Initialize our defined Services
+        for (int i = 0; i < services.length; i++) {
+            services[i].initialize();
+        }
+    }
+    
+    private String type;
+    private String domain;
+    private String suffix;
+    private ObjectName oname;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardService.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardService.java
new file mode 100644
index 0000000..4cdbfce
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardService.java
@@ -0,0 +1,673 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.util.LifecycleSupport;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.ObjectName;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Standard implementation of the <code>Service</code> interface.  The
+ * associated Container is generally an instance of Engine, but this is
+ * not required.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public class StandardService
+        implements Lifecycle, Service
+ {
+
+     private static final Logger log = LogFacade.getLogger();
+     private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Descriptive information about this component implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardService/1.0";
+
+
+    /**
+     * The name of this service.
+     */
+    private String name = null;
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The <code>Server</code> that owns this Service, if any.
+     */
+    private Server server = null;
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    protected int debug = 0;
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * The set of Connectors associated with this Service.
+     */
+    protected Connector connectors[] = new Connector[0];
+
+    protected final Object connectorsMonitor = new Object();
+
+    /**
+     * The Container associated with this Service. (In the case of the
+     * org.apache.catalina.startup.Embedded subclass, this holds the most
+     * recently added Engine.)
+     */
+    protected Container container = null;
+
+
+    /**
+     * Has this component been initialized?
+     */
+    protected boolean initialized = false;
+
+    /**
+     * The broadcaster that sends j2ee notifications.
+     */
+    private NotificationBroadcasterSupport broadcaster = null;
+
+
+    // ----------------------------------------------------------- Constructors
+
+     /**
+      * Construct a default instance of this class.
+      */
+
+     public StandardService() {
+         this.broadcaster = new NotificationBroadcasterSupport();
+     }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the <code>Container</code> that handles requests for all
+     * <code>Connectors</code> associated with this Service.
+     */
+    public Container getContainer() {
+
+        return (this.container);
+
+    }
+
+
+    /**
+     * Set the <code>Container</code> that handles requests for all
+     * <code>Connectors</code> associated with this Service.
+     *
+     * @param container The new Container
+     */
+    public void setContainer(Container container) {
+
+        Container oldContainer = this.container;
+        if ((oldContainer != null) && (oldContainer instanceof Engine))
+            ((Engine) oldContainer).setService(null);
+        this.container = container;
+        if (this.container instanceof Engine)
+            ((Engine) this.container).setService(this);
+        if (started && (this.container != null) &&
+            (this.container instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) this.container).start();
+            } catch (LifecycleException e) {
+                // Ignore
+            }
+        }
+        synchronized (connectorsMonitor) {
+            for (int i = 0; i < connectors.length; i++)
+                connectors[i].setContainer(this.container);
+        }
+        if (started && (oldContainer != null) &&
+            (oldContainer instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) oldContainer).stop();
+            } catch (LifecycleException e) {
+                // Ignore
+            }
+        }
+
+        // Report this property change to interested listeners
+        support.firePropertyChange("container", oldContainer, this.container);
+
+    }
+
+    public ObjectName getContainerName() {
+        if( container instanceof ContainerBase ) {
+            return ((ContainerBase)container).getJmxName();
+        }
+        return null;
+    }
+
+
+    /**
+     * Return the debugging detail level of this component.
+     */
+    public int getDebug() {
+
+        return (this.debug);
+
+    }
+
+
+    /**
+     * Set the debugging detail level of this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+
+        int oldDebug = this.debug;
+        this.debug = debug;
+        support.firePropertyChange("debug", Integer.valueOf(oldDebug),
+                                   Integer.valueOf(this.debug));
+    }
+
+
+    /**
+     * Return descriptive information about this Service implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    /**
+     * Return the name of this Service.
+     */
+    public String getName() {
+
+        return (this.name);
+
+    }
+
+
+    /**
+     * Set the name of this Service.
+     *
+     * @param name The new service name
+     */
+    public void setName(String name) {
+
+        this.name = name;
+
+    }
+
+
+    /**
+     * Return the <code>Server</code> with which we are associated (if any).
+     */
+    public Server getServer() {
+
+        return (this.server);
+
+    }
+
+
+    /**
+     * Set the <code>Server</code> with which we are associated (if any).
+     *
+     * @param server The server that owns this Service
+     */
+    public void setServer(Server server) {
+
+        this.server = server;
+
+    }
+
+     /**
+      * Return the <code>NotificationBroadcasterSupport</code> that sends notification for this Service.
+      */
+     public NotificationBroadcasterSupport getBroadcaster() {
+
+         return broadcaster;
+
+     }
+
+     /**
+      * Set the <code>NotificationBroadcasterSupport</code> that sends notification for this Service
+      *
+      * @param broadcaster The new NotificationBroadcasterSupport
+      */
+
+     public void setBroadcaster(NotificationBroadcasterSupport broadcaster) {
+
+         this.broadcaster = broadcaster;
+
+     }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a new Connector to the set of defined Connectors, and associate it
+     * with this Service's Container.
+     *
+     * @param connector The Connector to be added
+     */
+    public void addConnector(Connector connector) {
+
+        synchronized (connectorsMonitor) {
+            connector.setContainer(this.container);
+            connector.setService(this);
+            Connector results[] = new Connector[connectors.length + 1];
+            System.arraycopy(connectors, 0, results, 0, connectors.length);
+            results[connectors.length] = connector;
+            connectors = results;
+
+            if (initialized) {
+                try {
+                    connector.initialize();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, "Connector.initialize", e);
+                }
+            }
+
+            if (started && (connector instanceof Lifecycle)) {
+                try {
+                    ((Lifecycle) connector).start();
+                } catch (LifecycleException e) {
+                    log.log(Level.SEVERE, "Connector.start", e);
+                }
+            }
+
+            // Report this property change to interested listeners
+            support.firePropertyChange("connector", null, connector);
+        }
+
+    }
+
+    public ObjectName[] getConnectorNames() {
+        ObjectName results[] = new ObjectName[connectors.length];
+        for( int i=0; i<results.length; i++ ) {
+            // if it's a coyote connector
+            //if( connectors[i] instanceof CoyoteConnector ) {
+            //    results[i]=((CoyoteConnector)connectors[i]).getJmxName();
+            //}
+        }
+        return results;
+    }
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Find and return the set of Connectors associated with this Service.
+     */
+    public Connector[] findConnectors() {
+
+        return (connectors);
+
+    }
+
+
+    /**
+     * Find and return the Connector associated with this Service and Connector name.
+     */
+    public Connector findConnector(String name) {
+
+        for (Connector connector : connectors) {
+            if (connector.getName().equals(name)) {
+                return connector;
+            }
+        }
+        return null;
+
+    }
+
+
+    /**
+     * Remove the specified Connector from the set associated from this
+     * Service.  The removed Connector will also be disassociated from our
+     * Container.
+     *
+     * @param connector The Connector to be removed
+     */
+    // START SJSAS 6231069
+    //public void removeConnector(Connector connector) {
+    public void removeConnector(Connector connector) throws LifecycleException{
+    // END SJSAS 6231069
+
+        synchronized (connectorsMonitor) {
+            int j = -1;
+            for (int i = 0; i < connectors.length; i++) {
+                if (connector == connectors[i]) {
+                    j = i;
+                    break;
+                }
+            }
+           
+            if (j < 0)
+                return;
+
+            // START SJSAS 6231069
+            /*if (started && (connectors[j] instanceof Lifecycle)) {
+                try {
+                   ((Lifecycle) connectors[j]).stop();
+               } catch (LifecycleException e) {
+                   log.error("Connector.stop", e);
+               }
+            }*/
+            ((Lifecycle) connectors[j]).stop();
+            // END SJSAS 6231069
+
+            // START SJSAS 6231069
+            /*connectors[j].setContainer(null);
+            connector.setService(null);*/
+            // END SJSAS 6231069           
+            int k = 0;
+            Connector results[] = new Connector[connectors.length - 1];
+            for (int i = 0; i < connectors.length; i++) {
+                if (i != j)
+                    results[k++] = connectors[i];
+            }
+            connectors = results;
+
+            // Report this property change to interested listeners
+            support.firePropertyChange("connector", connector, null);
+        }
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("StandardService[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a LifecycleEvent listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners
+     * associated with this StandardService.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a LifecycleEvent listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called before any of the public
+     * methods of this component are utilized.  It should also send a
+     * LifecycleEvent of type START_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.SERVICE_STARTED);
+            }
+        }
+        
+        if( ! initialized )
+            init(); 
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.STARTING_SERVICE, this.name);
+        }
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Start our defined Container first
+        if (container != null) {
+            synchronized (container) {
+                if (container instanceof Lifecycle) {
+                    ((Lifecycle) container).start();
+                }
+            }
+        }
+
+        // Start our defined Connectors second
+        synchronized (connectorsMonitor) {
+            for (int i = 0; i < connectors.length; i++) {
+                if (connectors[i] instanceof Lifecycle)
+                    ((Lifecycle) connectors[i]).start();
+            }
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.  It should also send a LifecycleEvent
+     * of type STOP_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started) {
+            return;
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.STOPPING_SERVICE, neutralizeForLog(this.name));
+        }
+        started = false;
+
+        // Stop our defined Connectors first
+        synchronized (connectorsMonitor) {
+            for (int i = 0; i < connectors.length; i++) {
+                if (connectors[i] instanceof Lifecycle)
+                    ((Lifecycle) connectors[i]).stop();
+            }
+        }
+
+        // Stop our defined Container second
+        if (container != null) {
+            synchronized (container) {
+                if (container instanceof Lifecycle) {
+                    ((Lifecycle) container).stop();
+                }
+            }
+        }
+
+        // Notify our interested LifecycleListeners
+        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+    }
+
+
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     */
+    public void initialize()
+            throws LifecycleException
+    {
+        // Service shouldn't be used with embedded, so it doesn't matter
+        if (initialized) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.SERVICE_HAS_BEEN_INIT);
+            }
+            return;
+        }
+        initialized = true;
+
+        if( oname==null ) {
+            try {
+                // Hack - Server should be deprecated...
+                Container engine=this.getContainer();
+                domain=engine.getName();
+                oname=new ObjectName(domain + ":type=Service,serviceName="+name);
+            } catch (Exception e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_REGISTER_SERVICE_EXCEPTION), neutralizeForLog(domain));
+                log.log(Level.SEVERE, msg, e);
+            }
+            
+            
+        }
+        if( server==null ) {
+            // Register with the server 
+            // HACK: ServerFactory should be removed...
+            
+            ServerFactory.getServer().addService(this);
+        }
+               
+
+        // Initialize our defined Connectors
+        synchronized (connectorsMonitor) {
+                for (int i = 0; i < connectors.length; i++) {
+                    connectors[i].initialize();
+                }
+        }
+    }
+    
+    public void destroy() throws LifecycleException {
+        if( started ) stop();
+        // unregister should be here probably
+        // START CR 6368091
+        if (initialized) {
+            initialized = false;
+        }
+        // END CR 6368091
+    }
+
+    public void init() {
+        try {
+            initialize();
+        } catch( Throwable t ) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FAILED_SERVICE_INIT_EXCEPTION), neutralizeForLog(domain));
+            log.log(Level.SEVERE, msg, t);
+        }
+    }
+
+    protected String domain;
+    protected ObjectName oname;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapper.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapper.java
new file mode 100644
index 0000000..c972770
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapper.java
@@ -0,0 +1,2160 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.text.MessageFormat;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.ObjectName;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.SingleThreadModel;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerServlet;
+import org.apache.catalina.Context;
+import static org.apache.catalina.InstanceEvent.EventType.AFTER_DESTROY_EVENT;
+import static org.apache.catalina.InstanceEvent.EventType.AFTER_INIT_EVENT;
+import static org.apache.catalina.InstanceEvent.EventType.AFTER_SERVICE_EVENT;
+import static org.apache.catalina.InstanceEvent.EventType.BEFORE_DESTROY_EVENT;
+import static org.apache.catalina.InstanceEvent.EventType.BEFORE_INIT_EVENT;
+import static org.apache.catalina.InstanceEvent.EventType.BEFORE_SERVICE_EVENT;
+import org.apache.catalina.InstanceListener;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Loader;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.InstanceSupport;
+import org.glassfish.web.valve.GlassFishValve;
+// END GlassFish 1343
+
+/**
+ * Standard implementation of the <b>Wrapper</b> interface that represents
+ * an individual servlet definition.  No child Containers are allowed, and
+ * the parent Container must be a Context.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.12.2.1 $ $Date: 2008/04/17 18:37:09 $
+ */
+public class StandardWrapper
+        extends ContainerBase
+        implements ServletConfig, Wrapper {
+
+    private static final String[] DEFAULT_SERVLET_METHODS = new String[] {
+                                                    "GET", "HEAD", "POST" };
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new StandardWrapper component with the default basic Valve.
+     */
+    public StandardWrapper() {
+
+        super();
+        swValve=new StandardWrapperValve();
+        pipeline.setBasic(swValve);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The date and time at which this servlet will become available (in
+     * milliseconds since the epoch), or zero if the servlet is available.
+     * If this value equals Long.MAX_VALUE, the unavailability of this
+     * servlet is considered permanent.
+     */
+    private long available = 0L;
+
+
+    /**
+     * The broadcaster that sends j2ee notifications.
+     */
+    private NotificationBroadcasterSupport broadcaster = null;
+
+
+    /**
+     * The count of allocations that are currently active (even if they
+     * are for the same instance, as will be true on a non-STM servlet).
+     */
+    private AtomicInteger countAllocated = new AtomicInteger(0);
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The facade associated with this wrapper.
+     */
+    private StandardWrapperFacade facade =
+        new StandardWrapperFacade(this);
+
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.core.StandardWrapper/1.0";
+
+
+    /**
+     * The (single) initialized instance of this servlet.
+     */
+    private volatile Servlet instance = null;
+
+
+    /**
+     * Flag that indicates if this instance has been initialized
+     */
+    protected volatile boolean instanceInitialized = false;
+    
+
+    /**
+     * The support object for our instance listeners.
+     */
+    private InstanceSupport instanceSupport = new InstanceSupport(this);
+
+
+    /**
+     * The context-relative URI of the JSP file for this servlet.
+     */
+    private String jspFile = null;
+
+
+    /**
+     * The load-on-startup order value (negative value means load on
+     * first call) for this servlet.
+     */
+    private int loadOnStartup = -1;
+
+
+    /**
+     * Mappings associated with the wrapper.
+     */
+    private ArrayList<String> mappings = new ArrayList<String>();
+
+
+    /**
+     * The initialization parameters for this servlet, keyed by
+     * parameter name.
+     */
+    private Map<String, String> parameters = new HashMap<String, String>();
+
+
+    /**
+     * The security role references for this servlet, keyed by role name
+     * used in the servlet.  The corresponding value is the role name of
+     * the web application itself.
+     */
+    private HashMap<String, String> references = new HashMap<String, String>();
+
+
+    /**
+     * The run-as identity for this servlet.
+     */
+    private String runAs = null;
+
+
+    /**
+     * The notification sequence number.
+     */
+    private long sequenceNumber = 0;
+
+
+    /**
+     * The fully qualified servlet class name for this servlet.
+     */
+    private String servletClassName = null;
+
+
+    /**
+     * The class from which this servlet will be instantiated
+     */
+    private Class <? extends Servlet> servletClass = null;
+
+
+    /**
+     * Does this servlet implement the SingleThreadModel interface?
+     */
+    private volatile boolean singleThreadModel = false;
+
+
+    /**
+     * Are we unloading our servlet instance at the moment?
+     */
+    private boolean unloading = false;
+
+
+    /**
+     * Maximum number of STM instances.
+     */
+    private int maxInstances = 20;
+
+
+    /**
+     * Number of instances currently loaded for a STM servlet.
+     */
+    private int nInstances = 0;
+
+
+    /**
+     * Stack containing the STM instances.
+     */
+    private Stack<Servlet> instancePool = null;
+
+
+    /**
+     * Wait time for servlet unload in ms.
+     */
+    protected long unloadDelay = 2000;
+
+
+    /**
+     * True if this StandardWrapper is for the JspServlet
+     */
+    private boolean isJspServlet;
+
+
+    /**
+     * The ObjectName of the JSP monitoring mbean
+     */
+    private ObjectName jspMonitorON;
+
+
+    // To support jmx attributes
+    private StandardWrapperValve swValve;
+    private long loadTime=0;
+    private int classLoadTime=0;
+
+    private String description;
+
+
+    /**
+     * Async support
+     */
+    private boolean isAsyncSupported = false;
+    //private long asyncTimeout;
+
+
+    /**
+     * Static class array used when the SecurityManager is turned on and 
+     * <code>Servlet.init</code> is invoked.
+     */
+    private static Class<?>[] classType = new Class[]{ServletConfig.class};
+    
+    
+    /**
+     * Static class array used when the SecurityManager is turned on and 
+     * <code>Servlet.service</code>  is invoked.
+     */                                                 
+    private static Class<?>[] classTypeUsedInService = new Class[]{
+                                                         ServletRequest.class,
+                                                         ServletResponse.class};
+
+    /**
+     * File upload (multipart) support 
+     */
+    private boolean multipartConfigured = false;
+    private String multipartLocation = null;
+    private long multipartMaxFileSize = -1L;
+    private long multipartMaxRequestSize = -1L;
+    private int multipartFileSizeThreshold = 10240;  // 10K
+
+    private boolean osgi = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the available date/time for this servlet, in milliseconds since
+     * the epoch.  If this date/time is Long.MAX_VALUE, it is considered to mean
+     * that unavailability is permanent and any request for this servlet will return
+     * an SC_NOT_FOUND error.  If this date/time is in the future, any request for
+     * this servlet will return an SC_SERVICE_UNAVAILABLE error.  If it is zero,
+     * the servlet is currently available.
+     */
+    public long getAvailable() {
+
+        return (this.available);
+
+    }
+
+
+    /**
+     * Set the available date/time for this servlet, in milliseconds since the
+     * epoch.  If this date/time is Long.MAX_VALUE, it is considered to mean
+     * that unavailability is permanent and any request for this servlet will return
+     * an SC_NOT_FOUND error. If this date/time is in the future, any request for
+     * this servlet will return an SC_SERVICE_UNAVAILABLE error.
+     *
+     * @param available The new available date/time
+     */
+    public void setAvailable(long available) {
+
+        long oldAvailable = this.available;
+        if (available > System.currentTimeMillis())
+            this.available = available;
+        else
+            this.available = 0L;
+        support.firePropertyChange("available", oldAvailable, this.available);
+
+    }
+
+
+    /**
+     * Return the number of active allocations of this servlet, even if they
+     * are all for the same instance (as will be true for servlets that do
+     * not implement <code>SingleThreadModel</code>.
+     */
+    public int getCountAllocated() {
+
+        return (this.countAllocated.get());
+
+    }
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+        return (this.debug);
+    }
+
+
+    /**
+     * Set the debugging detail level for this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+        int oldDebug = this.debug;
+        this.debug = debug;
+        support.firePropertyChange("debug", oldDebug,
+                (long) this.debug);
+    }
+
+
+    public String getEngineName() {
+        return ((StandardContext)getParent()).getEngineName();
+    }
+
+
+    /**
+     * Return descriptive information about this Container implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+
+    /**
+     * Return the InstanceSupport object for this Wrapper instance.
+     */
+    public InstanceSupport getInstanceSupport() {
+        return (this.instanceSupport);
+    }
+
+
+    /**
+     * Return the context-relative URI of the JSP file for this servlet.
+     */
+    public String getJspFile() {
+        return (this.jspFile);
+    }
+
+
+    /**
+     * Set the context-relative URI of the JSP file for this servlet.
+     *
+     * @param jspFile JSP file URI
+     */
+    public void setJspFile(String jspFile) {
+
+        String oldJspFile = this.jspFile;
+        this.jspFile = jspFile;
+        support.firePropertyChange("jspFile", oldJspFile, this.jspFile);
+
+        // Each jsp-file needs to be represented by its own JspServlet and
+        // corresponding JspMonitoring mbean, because it may be initialized
+        // with its own init params
+        isJspServlet = true;
+    }
+
+
+    /**
+     * Return the load-on-startup order value (negative value means
+     * load on first call).
+     */
+    public int getLoadOnStartup() {
+
+        if (isJspServlet && loadOnStartup < 0) {
+            /*
+             * JspServlet must always be preloaded, because its instance is
+             * used during registerJMX (when registering the JSP
+             * monitoring mbean)
+             */
+             return Integer.MAX_VALUE;
+        } else {
+            return (this.loadOnStartup);
+        }
+    }
+
+
+    /**
+     * Set the load-on-startup order value (negative value means
+     * load on first call).
+     *
+     * @param value New load-on-startup value
+     */
+    public void setLoadOnStartup(int value) {
+
+        int oldLoadOnStartup = this.loadOnStartup;
+        this.loadOnStartup = value;
+        support.firePropertyChange("loadOnStartup",
+                                   Integer.valueOf(oldLoadOnStartup),
+                                   Integer.valueOf(this.loadOnStartup));
+
+    }
+
+
+    /**
+     * Set the load-on-startup order value from a (possibly null) string.
+     * Per the specification, any missing or non-numeric value is converted
+     * to a zero, so that this servlet will still be loaded at startup
+     * time, but in an arbitrary order.
+     *
+     * @param value New load-on-startup value
+     */
+    public void setLoadOnStartupString(String value) {
+
+        try {
+            setLoadOnStartup(Integer.parseInt(value));
+        } catch (NumberFormatException e) {
+            setLoadOnStartup(0);
+        }
+    }
+
+
+    public String getLoadOnStartupString() {
+        return Integer.toString( getLoadOnStartup());
+    }
+
+
+    /**
+     * Sets the description of this servlet.
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * Gets the description of this servlet.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+
+    /**
+     * Return maximum number of instances that will be allocated when a single
+     * thread model servlet is used.
+     */
+    public int getMaxInstances() {
+        return (this.maxInstances);
+    }
+
+
+    /**
+     * Set the maximum number of instances that will be allocated when a single
+     * thread model servlet is used.
+     *
+     * @param maxInstances New value of maxInstances
+     */
+    public void setMaxInstances(int maxInstances) {
+        int oldMaxInstances = this.maxInstances;
+        this.maxInstances = maxInstances;
+        support.firePropertyChange("maxInstances", oldMaxInstances,
+                                   this.maxInstances);
+    }
+
+
+    /**
+     * Set the parent Container of this Wrapper, but only if it is a Context.
+     *
+     * @param container Proposed parent Container
+     */
+    public void setParent(Container container) {
+        if ((container != null) &&
+            !(container instanceof Context))
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.PARENT_CONTAINER_MUST_BE_CONTEXT_EXCEPTION));
+        if (container instanceof StandardContext) {
+            unloadDelay = ((StandardContext)container).getUnloadDelay();
+            notifyContainerListeners =
+                ((StandardContext)container).isNotifyContainerListeners();
+        }
+        super.setParent(container);
+    }
+
+
+    /**
+     * Return the run-as identity for this servlet.
+     */
+    public String getRunAs() {
+        return (this.runAs);
+    }
+
+
+    /**
+     * Set the run-as identity for this servlet.
+     *
+     * @param runAs New run-as identity value
+     */
+    public void setRunAs(String runAs) {
+        String oldRunAs = this.runAs;
+        this.runAs = runAs;
+        support.firePropertyChange("runAs", oldRunAs, this.runAs);
+    }
+
+
+    /**
+     * Marks the wrapped servlet as supporting async operations or not.
+     *
+     * @param isAsyncSupported true if the wrapped servlet supports async mode,
+     * false otherwise
+     */
+    public void setIsAsyncSupported(boolean isAsyncSupported) {
+        this.isAsyncSupported = isAsyncSupported;
+    }
+
+
+    /**
+     * Checks if the wrapped servlet has been annotated or flagged in the
+     * deployment descriptor as being able to support asynchronous operations.
+     *
+     * @return true if the wrapped servlet supports async operations, and
+     * false otherwise
+     */
+    public boolean isAsyncSupported() {
+        return isAsyncSupported;
+    }
+
+
+    /**
+     * Return the fully qualified servlet class name for this servlet.
+     */
+    public String getServletClassName() {
+        return this.servletClassName;
+    }
+
+
+    /**
+     * Set the fully qualified servlet class name for this servlet.
+     *
+     * @param className Servlet class name
+     */
+    public void setServletClassName(String className) {
+        if (className == null) {
+            throw new NullPointerException("Null servlet class name");
+        }
+        if (servletClassName != null) {
+            throw new IllegalStateException(
+                "Wrapper already initialized with servlet instance, " +
+                "class, or name");
+        }
+        servletClassName = className;
+        // oldServletClassName is null
+        support.firePropertyChange("servletClassName", null,
+                                   servletClassName);
+        if (Constants.JSP_SERVLET_CLASS.equals(servletClassName)) {
+            isJspServlet = true;
+        }
+    }
+
+
+    /**
+     * @return the servlet class, or null if the servlet class has not
+     * been loaded yet
+     */
+    public Class <? extends Servlet> getServletClass() {
+        return servletClass;
+    }
+
+    /**
+     * Sets the class object from which this servlet will be instantiated.
+     *
+     * @param clazz The class object from which this servlet will
+     * be instantiated
+     */
+    public void setServletClass(Class <? extends Servlet> clazz) {
+        if (clazz == null) {
+            throw new NullPointerException("Null servlet class");
+        }
+        if ((servletClass != null) ||
+                servletClassName != null &&
+                    !servletClassName.equals(clazz.getName())) {
+            throw new IllegalStateException(
+                "Wrapper already initialized with servlet instance, " +
+                "class, or name");
+        }
+        servletClass = clazz;
+        servletClassName = clazz.getName();
+        if (Constants.JSP_SERVLET_CLASS.equals(servletClassName)) {
+            isJspServlet = true;
+        }
+    }
+
+
+    /**
+     * @return the servlet instance, or null if the servlet has not yet
+     * been instantiated
+     */
+    public Servlet getServlet() {
+        return instance;
+    }
+
+
+    /**
+     * Sets the servlet instance for this wrapper.
+     *
+     * @param instance the servlet instance
+     */
+    public void setServlet(Servlet instance) {
+        if (instance == null) {
+            throw new NullPointerException("Null servlet instance");
+        }
+        if (servletClassName != null) {
+            throw new IllegalStateException(
+                "Wrapper already initialized with servlet instance, " +
+                "class, or name");
+        }
+        this.instance = instance;
+        servletClass = instance.getClass();
+        servletClassName = servletClass.getName();
+        if (Constants.JSP_SERVLET_CLASS.equals(servletClassName)) {
+            isJspServlet = true;
+        }
+    }
+
+
+    /**
+     * Set the name of this servlet.  This is an alias for the normal
+     * <code>Container.setName()</code> method, and complements the
+     * <code>getServletName()</code> method required by the
+     * <code>ServletConfig</code> interface.
+     *
+     * @param name The new name of this servlet
+     */
+    public void setServletName(String name) {
+        setName(name);
+    }
+
+
+    /**
+     * Is this servlet currently unavailable?
+     */
+    public boolean isUnavailable() {
+        if (available == 0L)
+            return (false);
+        else if (available <= System.currentTimeMillis()) {
+            available = 0L;
+            return (false);
+        } else
+            return (true);
+    }
+
+
+    /**
+     * Gets the names of the methods supported by the underlying servlet.
+     *
+     * This is the same set of methods included in the Allow response header
+     * in response to an OPTIONS request method processed by the underlying
+     * servlet.
+     *
+     * @return Array of names of the methods supported by the underlying
+     * servlet
+     */
+    public String[] getServletMethods() throws ServletException {
+	
+        loadServletClass();
+
+        if (!javax.servlet.http.HttpServlet.class.isAssignableFrom(
+                                                        servletClass)) {
+            return DEFAULT_SERVLET_METHODS;
+        }
+
+        HashSet<String> allow = new HashSet<String>();
+        allow.add("TRACE");
+        allow.add("OPTIONS");
+	
+        Method[] methods = getAllDeclaredMethods(servletClass);
+        for (int i=0; methods != null && i<methods.length; i++) {
+            Method m = methods[i];
+            Class<?> params[] = m.getParameterTypes();
+
+            if (!(params.length == 2 &&
+                    params[0] == HttpServletRequest.class &&
+                    params[1] == HttpServletResponse.class)) {
+                continue;
+            }
+
+            if (m.getName().equals("doGet")) {
+                allow.add("GET");
+                allow.add("HEAD");
+            } else if (m.getName().equals("doPost")) {
+                allow.add("POST");
+            } else if (m.getName().equals("doPut")) {
+                allow.add("PUT");
+            } else if (m.getName().equals("doDelete")) {
+                allow.add("DELETE");
+            }
+        }
+
+        String[] methodNames = new String[allow.size()];
+        return allow.toArray(methodNames);
+    }
+
+
+    public boolean isMultipartConfigured() {
+        return multipartConfigured;
+    }
+
+    /**
+     * Sets the multipart location
+     */
+    public void setMultipartLocation(String location) {
+        multipartConfigured = true;
+        multipartLocation = location;
+    }
+
+
+    /**
+     * Gets the multipart location
+     */
+    public String getMultipartLocation(){
+        return multipartLocation;
+    }
+
+
+    /**
+     * Sets the multipart max-file-size
+     */
+    public void setMultipartMaxFileSize(long maxFileSize) {
+        multipartConfigured = true;
+        multipartMaxFileSize = maxFileSize;
+    }
+
+
+    /**
+     * Gets the multipart max-file-size
+     */
+    public long getMultipartMaxFileSize() {
+        return multipartMaxFileSize;
+    }
+
+
+    /**
+     * Sets the multipart max-request-size
+     */
+    public void setMultipartMaxRequestSize(long maxRequestSize) {
+        multipartConfigured = true;
+        multipartMaxRequestSize = maxRequestSize;
+    }
+
+
+    /**
+     * Gets the multipart max-request-Size
+     */
+    public long getMultipartMaxRequestSize() {
+        return multipartMaxRequestSize;
+    }
+
+
+    /**
+     * Sets the multipart file-size-threshold
+     */
+    public void setMultipartFileSizeThreshold(int fileSizeThreshold) {
+        multipartConfigured = true;
+        multipartFileSizeThreshold = fileSizeThreshold;
+    }
+
+
+    /**
+     * Gets the multipart file-size-threshol
+     */
+    public int getMultipartFileSizeThreshold() {
+        return multipartFileSizeThreshold;
+    }
+
+
+    protected boolean isOSGi() {
+        return osgi;
+    }
+
+
+    protected void setOSGi(boolean osgi) {
+        this.osgi = osgi;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // START GlassFish 1343
+    public synchronized void addValve(GlassFishValve valve) {
+        /*
+         * This exception should never be thrown in reality, because we never
+         * add any valves to a StandardWrapper. 
+         * This exception is added here as an alert mechanism only, should
+         * there ever be a need to add valves to a StandardWrapper in the
+         * future.
+         * In that case, the optimization in StandardContextValve related to
+         * GlassFish 1343 will need to be adjusted, by calling
+         * pipeline.getValves() and checking the pipeline's length to
+         * determine whether the basic valve may be invoked directly. The
+         * optimization currently avoids a call to pipeline.getValves(),
+         * because it is expensive.
+         */
+        throw new UnsupportedOperationException(
+            "Adding valves to wrappers not supported");
+    }
+    // END GlassFish 1343
+
+
+    /**
+     * Extract the root cause from a servlet exception.
+     * 
+     * @param e The servlet exception
+     */
+    public static Throwable getRootCause(ServletException e) {
+        Throwable rootCause = e;
+        Throwable rootCauseCheck;
+        // Extra aggressive rootCause finding
+        int loops = 0;
+        do {
+            loops++;
+            rootCauseCheck = rootCause.getCause();
+            if (rootCauseCheck != null)
+                rootCause = rootCauseCheck;
+        } while (rootCauseCheck != null && (loops < 20));
+        return rootCause;
+    }
+
+
+    /**
+     * Refuse to add a child Container, because Wrappers are the lowest level
+     * of the Container hierarchy.
+     *
+     * @param child Child container to be added
+     */
+    public void addChild(Container child) {
+        throw new IllegalStateException
+                (rb.getString(LogFacade.WRAPPER_CONTAINER_NO_CHILD_EXCEPTION));
+    }
+
+
+    /**
+     * Adds the initialization parameter with the given name and value
+     * to this servlet.
+     *
+     * @param name the name of the init parameter
+     * @param value the value of the init parameter
+     */
+    public void addInitParameter(String name, String value) {
+        setInitParameter(name, value, true);
+        if (notifyContainerListeners) {
+            fireContainerEvent("addInitParameter", name);
+        }
+    }
+
+
+    /**
+     * Sets the init parameter with the given name and value
+     * on this servlet.
+     *
+     * @param name the init parameter name
+     * @param value the init parameter value
+     * @param override true if the given init param is supposed to
+     * override an existing init param with the same name, and false
+     * otherwise
+     *
+     * @return true if the init parameter with the given name and value
+     * was set, false otherwise
+     */
+    public boolean setInitParameter(String name, String value, 
+                                    boolean override) {
+        if (null == name || null == value) {
+            throw new IllegalArgumentException(
+                "Null servlet init parameter name or value");
+        }
+
+        synchronized (parameters) {
+            if (override || !parameters.containsKey(name)) {
+                parameters.put(name, value);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+
+    /**
+     * Sets the initialization parameters contained in the given map
+     * on this servlet.
+     *
+     * @param initParameters the map with the init params to set
+     *
+     * @return the (possibly empty) Set of initialization parameter names
+     * that are in conflict
+     */
+    public Set<String> setInitParameters(Map<String, String> initParameters) {
+        if (null == initParameters) {
+            throw new IllegalArgumentException("Null init parameters");
+        }
+
+        synchronized (parameters) {
+            Set<String> conflicts = null;
+            for (Map.Entry<String, String> e : initParameters.entrySet()) {
+                if (e.getKey() == null || e.getValue() == null) {
+                    throw new IllegalArgumentException(
+                        "Null parameter name or value");
+                }
+                if (parameters.containsKey(e.getKey())) {
+                    if (conflicts == null) {
+                        conflicts = new HashSet<String>();    
+                    }
+                    conflicts.add(e.getKey());
+                }
+            }
+
+            if (conflicts != null) {
+                return conflicts;
+            }
+
+            for (Map.Entry<String, String> e : initParameters.entrySet()) {
+                setInitParameter(e.getKey(), e.getValue(), true);
+            }
+   
+            return Collections.emptySet();
+        }
+    }
+
+
+    /**
+     * Add a new listener interested in InstanceEvents.
+     *
+     * @param listener The new listener
+     */
+    public void addInstanceListener(InstanceListener listener) {
+        instanceSupport.addInstanceListener(listener);
+    }
+
+
+    /**
+     * Add a mapping associated with the Wrapper.
+     *
+     * @param mapping The new wrapper mapping
+     */
+    public void addMapping(String mapping) {
+        synchronized (mappings) {
+            mappings.add(mapping);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addMapping", mapping);
+        }
+    }
+
+
+    public Collection<String> getMappings() {
+        synchronized (mappings) {
+            return Collections.unmodifiableList(mappings);
+        }
+    }
+
+
+    /**
+     * Add a new security role reference record to the set of records for
+     * this servlet.
+     *
+     * @param name Role name used within this servlet
+     * @param link Role name used within the web application
+     */
+    public void addSecurityReference(String name, String link) {
+        synchronized (references) {
+            references.put(name, link);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("addSecurityReference", name);
+        }
+    }
+
+
+    /**
+     * Allocate an initialized instance of this Servlet that is ready to have
+     * its <code>service()</code> method called.  If the servlet class does
+     * not implement <code>SingleThreadModel</code>, the (only) initialized
+     * instance may be returned immediately.  If the servlet class implements
+     * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
+     * that this instance is not allocated again until it is deallocated by a
+     * call to <code>deallocate()</code>.
+     *
+     * @exception ServletException if the servlet init() method threw
+     *  an exception
+     * @exception ServletException if a loading error occurs
+     */
+    public synchronized Servlet allocate() throws ServletException {
+
+        // If we are currently unloading this servlet, throw an exception
+        if (unloading) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_ALLOCATE_SERVLET_EXCEPTION), getName());
+            throw new ServletException(msg);
+        }
+
+        // If not SingleThreadedModel, return the same instance every time
+        if (!singleThreadModel) {
+
+            // Load and initialize our instance if necessary
+            if (instance == null) {
+                // No instance. Instantiate and initialize
+                try {
+                    if (log.isLoggable(Level.FINEST))
+                        log.log(Level.FINEST, "Allocating non-STM instance");
+                    instance = loadServlet();
+                    initServlet(instance);
+                } catch (ServletException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    throw new ServletException
+                            (rb.getString(LogFacade.ERROR_ALLOCATE_SERVLET_INSTANCE_EXCEPTION), e);
+                }
+            } else if (!instanceInitialized) {
+                /*
+                 * Instance not yet initialized. This is the case
+                 * when the instance was registered via
+                 * ServletContext#addServlet
+                 */
+                initServlet(instance);
+            }
+
+            if (!singleThreadModel) {
+                if (log.isLoggable(Level.FINEST))
+                    log.log(Level.FINEST, "Returning non-STM instance");
+                countAllocated.incrementAndGet();
+                return (instance);
+            }
+        }
+
+        synchronized (instancePool) {
+
+            while (countAllocated.get() >= nInstances) {
+                // Allocate a new instance if possible, or else wait
+                if (nInstances < maxInstances) {
+                    try {
+                        Servlet servlet = loadServlet();
+                        initServlet(servlet);
+                        instancePool.push(servlet);
+                        nInstances++;
+                    } catch (ServletException e) {
+                        throw e;
+                    } catch (Throwable e) {
+                        throw new ServletException
+                                (rb.getString(LogFacade.ERROR_ALLOCATE_SERVLET_INSTANCE_EXCEPTION), e);
+                    }
+                } else {
+                    try {
+                        instancePool.wait();
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+            }
+            if (log.isLoggable(Level.FINEST)) {
+                log.log(Level.FINEST, "Returning allocated STM instance");
+            }
+            countAllocated.incrementAndGet();
+            return instancePool.pop();
+        }
+    }
+
+
+    /**
+     * Return this previously allocated servlet to the pool of available
+     * instances.  If this servlet class does not implement SingleThreadModel,
+     * no action is actually required.
+     *
+     * @param servlet The servlet to be returned
+     *
+     * @exception ServletException if a deallocation error occurs
+     */
+    public void deallocate(Servlet servlet) throws ServletException {
+
+        // If not SingleThreadModel, no action is required
+        if (!singleThreadModel) {
+            countAllocated.decrementAndGet();
+            return;
+        }
+
+        // Unlock and free this instance
+        synchronized (instancePool) {
+            countAllocated.decrementAndGet();
+            instancePool.push(servlet);
+            instancePool.notify();
+        }
+    }
+
+
+    /**
+     * Return the value for the specified initialization parameter name,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the requested initialization parameter
+     */
+    public String findInitParameter(String name) {
+        synchronized (parameters) {
+            return parameters.get(name);
+        }
+    }
+
+
+    /**
+     * Return the names of all defined initialization parameters for this
+     * servlet.
+     */
+    public String[] findInitParameters() {
+        synchronized (parameters) {
+            String results[] = new String[parameters.size()];
+            return parameters.keySet().toArray(results);
+        }
+    }
+
+
+    /**
+     * Return the mappings associated with this wrapper.
+     */
+    public String[] findMappings() {
+        synchronized (mappings) {
+            return mappings.toArray(new String[mappings.size()]);
+        }
+    }
+
+
+    /**
+     * Return the security role link for the specified security role
+     * reference name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Security role reference used within this servlet
+     */
+    public String findSecurityReference(String name) {
+        synchronized (references) {
+            return references.get(name);
+        }
+    }
+
+
+    /**
+     * Return the set of security role reference names associated with
+     * this servlet, if any; otherwise return a zero-length array.
+     */
+    public String[] findSecurityReferences() {
+        synchronized (references) {
+            String results[] = new String[references.size()];
+            return references.keySet().toArray(results);
+        }
+    }
+
+
+    /**
+     * FIXME: Fooling introspection ...
+     */
+    public Wrapper findMappingObject() {
+        return (Wrapper) getMappingObject();
+    }
+
+
+    /**
+     * Loads and initializes an instance of the servlet, if there is not
+     * already at least one initialized instance.
+     * This can be used, for example, to load servlets that are marked in
+     * the deployment descriptor to be loaded at server startup time.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  Servlets whose classnames begin with
+     * <code>org.apache.catalina.</code> (so-called "container" servlets)
+     * are loaded by the same classloader that loaded this class, rather than
+     * the classloader for the current web application.
+     * This gives such classes access to Catalina internals, which are
+     * prevented for classes loaded for web applications.
+     *
+     * @exception ServletException if the servlet init() method threw
+     *  an exception
+     * @exception ServletException if some other loading problem occurs
+     */
+    public synchronized void load() throws ServletException {
+        instance = loadServlet();
+        initServlet(instance);
+    }
+
+
+    /**
+     * Creates an instance of the servlet, if there is not already
+     * at least one initialized instance.
+     */
+    private synchronized Servlet loadServlet() throws ServletException {
+
+        // Nothing to do if we already have an instance or an instance pool
+        if (!singleThreadModel && (instance != null)) {
+            return instance;
+        }
+
+        long t1 = System.currentTimeMillis();
+
+        loadServletClass();
+
+        // Instantiate the servlet class
+        Servlet servlet = null;
+        try {
+            servlet = ((StandardContext)getParent()).createServletInstance(
+                servletClass);
+        } catch (ClassCastException e) {
+            unavailable(null);
+            // Restore the context ClassLoader
+            String msg = MessageFormat.format(rb.getString(LogFacade.CLASS_IS_NOT_SERVLET_EXCEPTION), servletClass.getName());
+            throw new ServletException(msg, e);
+        } catch (Throwable e) {
+            unavailable(null);
+            // Restore the context ClassLoader
+            String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_INSTANTIATE_SERVLET_CLASS_EXCEPTION), servletClass.getName());
+            throw new ServletException(msg, e);
+        }
+
+        // Check if loading the servlet in this web application should be
+        // allowed
+        if (!isServletAllowed(servlet)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.PRIVILEGED_SERVLET_CANNOT_BE_LOADED_EXCEPTION), servletClass.getName());
+            throw new SecurityException(msg);
+        }
+
+        // Special handling for ContainerServlet instances
+        if ((servlet instanceof ContainerServlet) &&
+              (isContainerProvidedServlet(servletClass.getName()) ||
+                ((Context)getParent()).getPrivileged() )) {
+            ((ContainerServlet) servlet).setWrapper(this);
+        }
+
+        classLoadTime = (int) (System.currentTimeMillis() -t1);
+
+        // Register our newly initialized instance
+        singleThreadModel = servlet instanceof SingleThreadModel;
+        if (singleThreadModel) {
+            if (instancePool == null)
+                instancePool = new Stack<Servlet>();
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("load", this);
+        }
+
+        loadTime = System.currentTimeMillis() - t1;
+
+        return servlet;
+    }
+
+
+    /*
+     * Loads the servlet class
+     */
+    private synchronized void loadServletClass() throws ServletException {
+        if (servletClass != null) {
+            return;
+        }
+
+        // If this "servlet" is really a JSP file, get the right class.
+        String actualClass = servletClassName;
+        if ((actualClass == null) && (jspFile != null)) {
+            Wrapper jspWrapper = (Wrapper)
+                ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
+            if (jspWrapper != null) {
+                actualClass = jspWrapper.getServletClassName();
+                // Merge init parameters
+                String paramNames[] = jspWrapper.findInitParameters();
+                for (String paramName : paramNames) {
+                    if (parameters.get(paramName) == null) {
+                        parameters.put(paramName,
+                                       jspWrapper.findInitParameter(paramName));
+                    }
+                }
+            }
+        }
+
+        // Complain if no servlet class has been specified
+        if (actualClass == null) {
+            unavailable(null);
+            String msg = MessageFormat.format(rb.getString(LogFacade.NO_SERVLET_BE_SPECIFIED_EXCEPTION), getName());
+            throw new ServletException(msg);
+        }
+
+        // Acquire an instance of the class loader to be used
+        Loader loader = getLoader();
+        if (loader == null) {
+            unavailable(null);
+            String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_FIND_LOADER_EXCEPTION), getName());
+            throw new ServletException(msg);
+        }
+
+        ClassLoader classLoader = loader.getClassLoader();
+
+        // Special case class loader for a container provided servlet
+        //  
+        if (isContainerProvidedServlet(actualClass) && 
+                ! ((Context)getParent()).getPrivileged() ) {
+            // If it is a priviledged context - using its own
+            // class loader will work, since it's a child of the container
+            // loader
+            classLoader = this.getClass().getClassLoader();
+        }
+
+        // Load the specified servlet class from the appropriate class loader
+        Class clazz = null;
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                final ClassLoader fclassLoader = classLoader;
+                final String factualClass = actualClass;
+                try{
+                    clazz = AccessController.doPrivileged(
+                        new PrivilegedExceptionAction<Class>(){
+                            public Class run() throws Exception{
+                                if (fclassLoader != null) {
+                                    return fclassLoader.loadClass(factualClass);
+                                } else {
+                                    return Class.forName(factualClass);
+                                }
+                            }
+                    });
+                } catch(PrivilegedActionException pax){
+                    Exception ex = pax.getException();
+                    if (ex instanceof ClassNotFoundException){
+                        throw (ClassNotFoundException)ex;
+                    } else {
+                        String msgErrorLoadingInfo = MessageFormat.format(rb.getString(LogFacade.ERROR_LOADING_INFO),
+                                                          new Object[] {fclassLoader, factualClass});
+                        getServletContext().log(msgErrorLoadingInfo, ex );
+                    }
+                }
+            } else {
+                if (classLoader != null) {
+                    clazz = classLoader.loadClass(actualClass);
+                } else {
+                    clazz = Class.forName(actualClass);
+                }
+            }
+        } catch (ClassNotFoundException e) {
+            unavailable(null);
+            String msgErrorLoadingInfo = MessageFormat.format(rb.getString(LogFacade.ERROR_LOADING_INFO),
+                    new Object[] {classLoader, actualClass});
+            getServletContext().log(msgErrorLoadingInfo, e );
+            String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_FIND_SERVLET_CLASS_EXCEPTION), actualClass);
+            throw new ServletException(msg, e);
+        }
+
+        if (clazz == null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_FIND_SERVLET_CLASS_EXCEPTION), actualClass);
+            unavailable(null);
+            throw new ServletException(msg);
+        }
+
+        servletClass = castToServletClass(clazz);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Class<? extends Servlet> castToServletClass(Class<?> clazz) {
+        return (Class<? extends Servlet>)clazz;
+    }
+
+
+    /**
+     * Initializes the given servlet instance, by calling its init method.
+     */
+    private void initServlet(Servlet servlet) throws ServletException {
+        if (instanceInitialized && !singleThreadModel) {
+            // Servlet has already been initialized
+            return;
+        }
+
+        try {
+            instanceSupport.fireInstanceEvent(BEFORE_INIT_EVENT, servlet);
+            // START SJS WS 7.0 6236329
+            //if( System.getSecurityManager() != null) {
+            if ( SecurityUtil.executeUnderSubjectDoAs() ){
+            // END OF SJS WS 7.0 6236329
+                Object[] initType = new Object[1];
+                initType[0] = facade;
+                SecurityUtil.doAsPrivilege("init", servlet, classType,
+                                           initType);
+                initType = null;
+            } else {
+                servlet.init(facade);
+            }
+
+            instanceInitialized = true;
+
+            // Invoke jspInit on JSP pages
+            if ((loadOnStartup >= 0) && (jspFile != null)) {
+                // Invoking jspInit
+                DummyRequest req = new DummyRequest();
+                req.setServletPath(jspFile);
+                req.setQueryString("jsp_precompile=true");
+
+                // START PWC 4707989
+                String allowedMethods = (String) parameters.get("httpMethods");
+                if (allowedMethods != null
+                        && allowedMethods.length() > 0) {
+                    String[] s = allowedMethods.split(",");
+                    if (s.length > 0) {
+                        req.setMethod(s[0].trim());
+                    }
+                }
+                // END PWC 4707989
+
+                DummyResponse res = new DummyResponse();
+
+                // START SJS WS 7.0 6236329
+                //if( System.getSecurityManager() != null) {
+                if ( SecurityUtil.executeUnderSubjectDoAs() ){
+                // END OF SJS WS 7.0 6236329
+                    Object[] serviceType = new Object[2];
+                    serviceType[0] = req;
+                    serviceType[1] = res;                
+                    SecurityUtil.doAsPrivilege("service", servlet,
+                                               classTypeUsedInService,
+                                               serviceType);
+                } else {
+                    servlet.service(req, res);
+                }
+            }
+            instanceSupport.fireInstanceEvent(AFTER_INIT_EVENT,servlet);
+
+        } catch (UnavailableException f) {
+            instanceSupport.fireInstanceEvent(AFTER_INIT_EVENT,servlet, f);
+            unavailable(f);
+            throw f;
+
+        } catch (ServletException f) {
+            instanceSupport.fireInstanceEvent(AFTER_INIT_EVENT,servlet, f);
+            // If the servlet wanted to be unavailable it would have
+            // said so, so do not call unavailable(null).
+            throw f;
+
+        } catch (Throwable f) {
+            getServletContext().log("StandardWrapper.Throwable", f);
+            instanceSupport.fireInstanceEvent(AFTER_INIT_EVENT,servlet, f);
+            // If the servlet wanted to be unavailable it would have
+            // said so, so do not call unavailable(null).
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_INIT_EXCEPTION), getName());
+            throw new ServletException(msg, f);
+        }
+    }
+
+
+    // START IASRI 4665318
+    void service(ServletRequest request, ServletResponse response,
+                 Servlet serv)
+             throws IOException, ServletException {
+
+        InstanceSupport supp = getInstanceSupport();
+
+        try {
+            supp.fireInstanceEvent(BEFORE_SERVICE_EVENT,
+                                   serv, request, response);
+            if (!isAsyncSupported()) {
+                RequestFacadeHelper reqFacHelper =
+                    RequestFacadeHelper.getInstance(request);
+                if (reqFacHelper != null) {
+                    reqFacHelper.disableAsyncSupport();
+                }
+            } 
+            if ((request instanceof HttpServletRequest) &&
+                (response instanceof HttpServletResponse)) {
+                    
+                if ( SecurityUtil.executeUnderSubjectDoAs() ){
+                    final ServletRequest req = request;
+                    final ServletResponse res = response;
+                    Principal principal = 
+                        ((HttpServletRequest) req).getUserPrincipal();
+
+                    Object[] serviceType = new Object[2];
+                    serviceType[0] = req;
+                    serviceType[1] = res;
+                    
+                    SecurityUtil.doAsPrivilege("service",
+                                               serv,
+                                               classTypeUsedInService, 
+                                               serviceType,
+                                               principal);                                                   
+                } else {  
+                    serv.service((HttpServletRequest) request,
+                                 (HttpServletResponse) response);
+                }
+            } else {
+                serv.service(request, response);
+            }
+            supp.fireInstanceEvent(AFTER_SERVICE_EVENT,
+                                   serv, request, response);
+        } catch (IOException e) {
+            // Set response status before firing event, see IT 10022
+            if (response instanceof HttpServletResponse) {
+                ((HttpServletResponse)response).setStatus(
+                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+            supp.fireInstanceEvent(AFTER_SERVICE_EVENT,
+                                   serv, request, response, e);
+            throw e;
+        } catch (ServletException e) {
+            // Set response status before firing event, see IT 10022
+            if (response instanceof HttpServletResponse) {
+                ((HttpServletResponse)response).setStatus(
+                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+            supp.fireInstanceEvent(AFTER_SERVICE_EVENT,
+                                   serv, request, response, e);
+            throw e;
+        } catch (RuntimeException e) {
+            // Set response status before firing event, see IT 10022
+            if (response instanceof HttpServletResponse) {
+                ((HttpServletResponse)response).setStatus(
+                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+            supp.fireInstanceEvent(AFTER_SERVICE_EVENT,
+                                   serv, request, response, e);
+            throw e;
+        } catch (Error e) {
+            // Set response status before firing event, see IT 10022
+            if (response instanceof HttpServletResponse) {
+                ((HttpServletResponse)response).setStatus(
+                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+            supp.fireInstanceEvent(AFTER_SERVICE_EVENT,
+                                   serv, request, response, e);
+            throw e;
+        } catch (Throwable e) {
+            // Set response status before firing event, see IT 10022
+            ((HttpServletResponse)response).setStatus(
+                HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            supp.fireInstanceEvent(AFTER_SERVICE_EVENT,
+                                   serv, request, response, e);
+            throw new ServletException(rb.getString(LogFacade.SERVLET_EXECUTION_EXCEPTION), e);
+        }
+
+    }
+    // END IASRI 4665318
+
+
+    /**
+     * Remove the specified initialization parameter from this servlet.
+     *
+     * @param name Name of the initialization parameter to remove
+     */
+    public void removeInitParameter(String name) {
+
+        synchronized (parameters) {
+            parameters.remove(name);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeInitParameter", name);
+        }
+    }
+
+
+    /**
+     * Remove a listener no longer interested in InstanceEvents.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeInstanceListener(InstanceListener listener) {
+
+        instanceSupport.removeInstanceListener(listener);
+
+    }
+
+
+    /**
+     * Remove a mapping associated with the wrapper.
+     *
+     * @param mapping The pattern to remove
+     */
+    public void removeMapping(String mapping) {
+
+        synchronized (mappings) {
+            mappings.remove(mapping);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeMapping", mapping);
+        }
+    }
+
+
+    /**
+     * Remove any security role reference for the specified role name.
+     *
+     * @param name Security role used within this servlet to be removed
+     */
+    public void removeSecurityReference(String name) {
+
+        synchronized (references) {
+            references.remove(name);
+        }
+
+        if (notifyContainerListeners) {
+            fireContainerEvent("removeSecurityReference", name);
+        }
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder();
+        if (getParent() != null) {
+            sb.append(getParent().toString());
+            sb.append(".");
+        }
+        sb.append("StandardWrapper[");
+        sb.append(getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    /**
+     * Process an UnavailableException, marking this servlet as unavailable
+     * for the specified amount of time.
+     *
+     * @param unavailable The exception that occurred, or <code>null</code>
+     *  to mark this servlet as permanently unavailable
+     */
+    public void unavailable(UnavailableException unavailable) {
+        String msg = MessageFormat.format(rb.getString(LogFacade.MARK_SERVLET_UNAVAILABLE), neutralizeForLog(getName()));
+        getServletContext().log(msg);
+        if (unavailable == null)
+            setAvailable(Long.MAX_VALUE);
+        else if (unavailable.isPermanent())
+            setAvailable(Long.MAX_VALUE);
+        else {
+            int unavailableSeconds = unavailable.getUnavailableSeconds();
+            if (unavailableSeconds <= 0)
+                unavailableSeconds = 60;        // Arbitrary default
+            setAvailable(System.currentTimeMillis() +
+                         (unavailableSeconds * 1000L));
+        }
+
+    }
+
+
+    /**
+     * Unload all initialized instances of this servlet, after calling the
+     * <code>destroy()</code> method for each instance.  This can be used,
+     * for example, prior to shutting down the entire servlet engine, or
+     * prior to reloading all of the classes from the Loader associated with
+     * our Loader's repository.
+     *
+     * @exception ServletException if an exception is thrown by the
+     *  destroy() method
+     */
+    public synchronized void unload() throws ServletException {
+
+        // Nothing to do if we have never loaded the instance
+        if (!singleThreadModel && (instance == null))
+            return;
+        unloading = true;
+
+        // Loaf a while if the current instance is allocated
+        // (possibly more than once if non-STM)
+        if (countAllocated.get() > 0) {
+            int nRetries = 0;
+            long delay = unloadDelay / 20;
+            while ((nRetries < 21) && (countAllocated.get() > 0)) {
+                if ((nRetries % 10) == 0) {
+                    if (log.isLoggable(Level.FINE)) {
+                        log.log(Level.FINE, LogFacade.WAITING_INSTANCE_BE_DEALLOCATED, new Object[] {countAllocated.toString(),
+                                instance.getClass().getName()});
+                    }
+                }
+                try {
+                    Thread.sleep(delay);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+                nRetries++;
+            }
+        }
+
+        ClassLoader oldCtxClassLoader =
+            Thread.currentThread().getContextClassLoader();
+        ClassLoader classLoader = instance.getClass().getClassLoader();
+
+        // Call the servlet destroy() method
+        try {
+            instanceSupport.fireInstanceEvent(BEFORE_DESTROY_EVENT, instance);
+
+            Thread.currentThread().setContextClassLoader(classLoader);
+            // START SJS WS 7.0 6236329
+            //if( System.getSecurityManager() != null) {
+            if ( SecurityUtil.executeUnderSubjectDoAs() ){
+            // END OF SJS WS 7.0 6236329
+                SecurityUtil.doAsPrivilege("destroy", instance);
+                SecurityUtil.remove(instance);                           
+            } else {
+                instance.destroy();
+            }
+
+            instanceSupport.fireInstanceEvent(AFTER_DESTROY_EVENT, instance);
+        } catch (Throwable t) {
+            instanceSupport.fireInstanceEvent(AFTER_DESTROY_EVENT, instance, t);
+            instance = null;
+            instancePool = null;
+            nInstances = 0;
+            if (notifyContainerListeners) {
+                fireContainerEvent("unload", this);
+            }
+            unloading = false;
+            String msg = MessageFormat.format(rb.getString(LogFacade.DESTROY_SERVLET_EXCEPTION), getName());
+            throw new ServletException(msg, t);
+        } finally {
+            // restore the context ClassLoader
+            Thread.currentThread().setContextClassLoader(oldCtxClassLoader);
+        }
+
+        // Deregister the destroyed instance
+        instance = null;
+
+        if (singleThreadModel && (instancePool != null)) {
+            try {
+                Thread.currentThread().setContextClassLoader(classLoader);
+                while (!instancePool.isEmpty()) {
+                    // START SJS WS 7.0 6236329
+                    //if( System.getSecurityManager() != null) {
+                    if ( SecurityUtil.executeUnderSubjectDoAs() ){
+                    // END OF SJS WS 7.0 6236329
+                        SecurityUtil.doAsPrivilege("destroy",
+                                                   instancePool.pop());
+                        SecurityUtil.remove(instance);                           
+                    } else {
+                        instancePool.pop().destroy();
+                    }
+                }
+            } catch (Throwable t) {
+                instancePool = null;
+                nInstances = 0;
+                unloading = false;
+                if (notifyContainerListeners) {
+                    fireContainerEvent("unload", this);
+                }
+                String msg = MessageFormat.format(rb.getString(LogFacade.DESTROY_SERVLET_EXCEPTION), getName());
+                throw new ServletException(msg, t);
+            } finally {
+                // restore the context ClassLoader
+                Thread.currentThread().setContextClassLoader
+                    (oldCtxClassLoader);
+            }
+            instancePool = null;
+            nInstances = 0;
+        }
+
+        singleThreadModel = false;
+
+        unloading = false;
+   
+        if (notifyContainerListeners) {
+            fireContainerEvent("unload", this);
+        }
+    }
+
+
+    // -------------------------------------------------- ServletConfig Methods
+
+
+    /**
+     * Return the initialization parameter value for the specified name,
+     * if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the initialization parameter to retrieve
+     */
+    public String getInitParameter(String name) {
+        return findInitParameter(name);
+    }
+
+
+    public Map<String, String> getInitParameters() {
+        synchronized (parameters) {
+            return Collections.unmodifiableMap(parameters);
+        }
+    }
+
+
+    /**
+     * Return the set of initialization parameter names defined for this
+     * servlet.  If none are defined, an empty Enumeration is returned.
+     */
+    public Enumeration<String> getInitParameterNames() {
+        synchronized (parameters) {
+            return (new Enumerator<String>(parameters.keySet()));
+        }
+    }
+
+
+    /**
+     * Return the servlet context with which this servlet is associated.
+     */
+    public ServletContext getServletContext() {
+        if (parent == null)
+            return (null);
+        else if (!(parent instanceof Context))
+            return (null);
+        else
+            return (((Context) parent).getServletContext());
+    }
+
+
+    /**
+     * Return the name of this servlet.
+     */
+    public String getServletName() {
+        return (getName());
+    }
+
+    public long getLoadTime() {
+        return loadTime;
+    }
+
+    public void setLoadTime(long loadTime) {
+        this.loadTime = loadTime;
+    }
+
+    public int getClassLoadTime() {
+        return classLoadTime;
+    }
+
+    // -------------------------------------------------------- Package Methods
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Add a default Mapper implementation if none have been configured
+     * explicitly.
+     *
+     * @param mapperClass Java class name of the default Mapper
+     */
+    protected void addDefaultMapper(String mapperClass) {
+
+        // No need for a default Mapper on a Wrapper
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified class name represents a
+     * container provided servlet class that should be loaded by the
+     * server class loader.
+     *
+     * @param classname Name of the class to be checked
+     */
+    private boolean isContainerProvidedServlet(String classname) {
+
+        if (classname.startsWith("org.apache.catalina.")) {
+            return (true);
+        }
+        try {
+            Class<?> clazz =
+                this.getClass().getClassLoader().loadClass(classname);
+            return (ContainerServlet.class.isAssignableFrom(clazz));
+        } catch (Throwable t) {
+            return (false);
+        }
+
+    }
+
+
+    /**
+     * Return <code>true</code> if loading this servlet is allowed.
+     */
+    private boolean isServletAllowed(Object servlet) {
+
+        if (servlet instanceof ContainerServlet) {
+            if (((Context) getParent()).getPrivileged()
+                || (servlet.getClass().getName().equals
+                    ("org.apache.catalina.servlets.InvokerServlet"))) {
+                return (true);
+            } else {
+                return (false);
+            }
+        }
+
+        return (true);
+
+    }
+
+
+    /**
+     * Log the abbreviated name of this Container for logging messages.
+     */
+    protected String logName() {
+
+        StringBuilder sb = new StringBuilder("StandardWrapper[");
+        if (getParent() != null)
+            sb.append(getParent().getName());
+        else
+            sb.append("null");
+        sb.append(':');
+        sb.append(getName());
+        sb.append(']');
+        return (sb.toString());
+
+    }
+
+
+    private Method[] getAllDeclaredMethods(Class<?> c) {
+
+        if (c.equals(javax.servlet.http.HttpServlet.class)) {
+            return null;
+        }
+
+        Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
+
+        Method[] thisMethods = c.getDeclaredMethods();
+        if (thisMethods.length == 0) {
+            return parentMethods;
+        }
+
+        if ((parentMethods != null) && (parentMethods.length > 0)) {
+            Method[] allMethods =
+                new Method[parentMethods.length + thisMethods.length];
+	    System.arraycopy(parentMethods, 0, allMethods, 0,
+                             parentMethods.length);
+	    System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
+                             thisMethods.length);
+
+	    thisMethods = allMethods;
+	}
+
+	return thisMethods;
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Start this component, pre-loading the servlet if the load-on-startup
+     * value is set appropriately.
+     *
+     * @exception LifecycleException if a fatal error occurs during startup
+     */
+    public void start() throws LifecycleException {
+    
+        // Send j2ee.state.starting notification 
+        if (this.getObjectName() != null) {
+            Notification notification = new Notification("j2ee.state.starting", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+        
+        // Start up this component
+        super.start();
+
+        if( oname != null )
+            registerJMX((StandardContext)getParent());
+        
+        // Load and initialize an instance of this servlet if requested
+        // MOVED TO StandardContext START() METHOD
+
+        setAvailable(0L);
+        
+        // Send j2ee.state.running notification 
+        if (this.getObjectName() != null) {
+            Notification notification = 
+                new Notification("j2ee.state.running", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+
+    }
+
+
+    /**
+     * Stop this component, gracefully shutting down the servlet if it has
+     * been initialized.
+     *
+     * @exception LifecycleException if a fatal error occurs during shutdown
+     */
+    public void stop() throws LifecycleException {
+
+        setAvailable(Long.MAX_VALUE);
+        
+        // Send j2ee.state.stopping notification 
+        if (this.getObjectName() != null) {
+            Notification notification = 
+                new Notification("j2ee.state.stopping", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+        
+        // Shut down our servlet instance (if it has been initialized)
+        try {
+            unload();
+        } catch (ServletException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_UNLOAD_EXCEPTION), neutralizeForLog(getName()));
+            getServletContext().log(msg, e);
+        }
+
+        // Shut down this component
+        super.stop();
+
+        // Send j2ee.state.stoppped notification 
+        if (this.getObjectName() != null) {
+            Notification notification = 
+                new Notification("j2ee.state.stopped", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+        
+        if( oname != null ) {
+            
+            // Send j2ee.object.deleted notification 
+            Notification notification = 
+                new Notification("j2ee.object.deleted", this, sequenceNumber++);
+            sendNotification(notification);
+        }
+
+    }
+
+    protected void registerJMX(StandardContext ctx) {
+
+        String parentName = ctx.getEncodedPath();
+        parentName = ("".equals(parentName)) ? "/" : parentName;
+
+        String hostName = ctx.getParent().getName();
+        hostName = (hostName==null) ? "DEFAULT" : hostName;
+
+        String domain = ctx.getDomain();
+
+        String webMod= "//" + hostName + parentName;
+        String onameStr = domain + ":j2eeType=Servlet,name=" + getName() +
+                          ",WebModule=" + webMod + ",J2EEApplication=" +
+                          ctx.getJ2EEApplication() + ",J2EEServer=" +
+                          ctx.getJ2EEServer();
+        if (isOSGi()) {
+            onameStr += ",osgi=true";
+        }
+
+        try {
+            oname=new ObjectName(onameStr);
+            controller=oname;
+            
+            // Send j2ee.object.created notification 
+            if (this.getObjectName() != null) {
+                Notification notification = new Notification( "j2ee.object.created", this, sequenceNumber++);
+                sendNotification(notification);
+            }
+        } catch( Exception ex ) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO,
+                    "Error registering servlet with jmx " + this, ex);
+            }
+        }
+
+        if (isJspServlet) {
+            // Register JSP monitoring mbean
+            onameStr = domain + ":type=JspMonitor,name=" + getName()
+                       + ",WebModule=" + webMod
+                       + ",J2EEApplication=" + ctx.getJ2EEApplication()
+                       + ",J2EEServer=" + ctx.getJ2EEServer();
+            try {
+                jspMonitorON = new ObjectName(onameStr);
+            } catch( Exception ex ) {
+                if (log.isLoggable(Level.INFO)) {
+                    log.log(Level.INFO,
+                        "Error registering JSP monitoring with jmx " +
+                        instance, ex);
+                }
+            }
+        }
+
+    }
+
+    public void sendNotification(Notification notification) {
+
+        if (broadcaster == null) {
+            broadcaster = ((StandardEngine)getParent().getParent().getParent()).getService().getBroadcaster();
+        }
+        if (broadcaster != null) {
+            broadcaster.sendNotification(notification);
+        }
+        return;
+    }
+    
+
+    // ------------------------------------------------------------- Attributes
+        
+        
+    public boolean isEventProvider() {
+        return false;
+    }
+    
+    public boolean isStateManageable() {
+        return false;
+    }
+    
+    public boolean isStatisticsProvider() {
+        return false;
+    }
+        
+        
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapperFacade.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapperFacade.java
new file mode 100644
index 0000000..857e84f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapperFacade.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import java.util.Enumeration;
+
+/**
+ * Facade for the <b>StandardWrapper</b> object.
+ *
+ * @author Remy Maucharat
+ * @version $Revision: 1.3 $ $Date: 2006/11/13 19:26:30 $
+ */
+public final class StandardWrapperFacade
+    implements ServletConfig {
+
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Create a new facade around a StandardWrapper.
+     */
+    public StandardWrapperFacade(StandardWrapper config) {
+
+        super();
+        this.config = config;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped config.
+     */
+    private ServletConfig config = null;
+
+
+    /**
+     * The context facade object for this wrapper.
+     */
+    private ServletContext context = null;
+
+
+    // -------------------------------------------------- ServletConfig Methods
+
+
+    public String getServletName() {
+        return config.getServletName();
+    }
+
+
+    public ServletContext getServletContext() {
+
+        if (context == null) {
+            context = config.getServletContext();
+            if ((context != null) && (context instanceof ApplicationContext)) {
+                context = ((ApplicationContext) context).getFacade();
+            }
+        }
+
+        return context;
+    }
+
+
+    public String getInitParameter(String name) {
+        return config.getInitParameter(name);
+    }
+
+
+    public Enumeration<String> getInitParameterNames() {
+        return config.getInitParameterNames();
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapperValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapperValve.java
new file mode 100644
index 0000000..71a55f4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardWrapperValve.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.core;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.catalina.connector.RequestFacade;
+import org.apache.catalina.valves.ValveBase;
+import org.glassfish.grizzly.http.util.DataChunk;
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardWrapper</code> container implementation.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.10 $ $Date: 2007/05/05 05:31:54 $
+ */
+
+final class StandardWrapperValve extends ValveBase {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Invoke the servlet we are managing, respecting the rules regarding
+     * servlet lifecycle and SingleThreadModel support.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+    @Override
+    public int invoke(Request request, Response response)
+            throws IOException, ServletException {
+
+        boolean unavailable = false;
+        Throwable throwable = null;
+        Servlet servlet = null;
+
+        StandardWrapper wrapper = (StandardWrapper) getContainer();
+        Context context = (Context) wrapper.getParent();
+
+        HttpRequest hrequest = (HttpRequest) request;
+
+        /*
+         * Create a request facade such that if the request was received
+         * at the root context, and the root context is mapped to a
+         * default-web-module, the default-web-module mapping is masked from
+         * the application code to which the request facade is being passed.
+         * For example, the request.facade's getContextPath() method will 
+         * return "/", rather than the context root of the default-web-module,
+         * in this case.
+         */
+        RequestFacade hreq = (RequestFacade) request.getRequest(true);
+        HttpServletResponse hres =
+            (HttpServletResponse) response.getResponse();
+
+        // Check for the application being marked unavailable
+        if (!context.getAvailable()) {
+            // BEGIN S1AS 4878272
+            hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+            response.setDetailMessage(rb.getString(LogFacade.APP_UNAVAILABLE));
+            // END S1AS 4878272
+            unavailable = true;
+        }
+
+        // Check for the servlet being marked unavailable
+        if (!unavailable && wrapper.isUnavailable()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_UNAVAILABLE), wrapper.getName());
+            log(msg);
+            if (hres == null) {
+                ;       // NOTE - Not much we can do generically
+            } else {
+                long available = wrapper.getAvailable();
+                if ((available > 0L) && (available < Long.MAX_VALUE)) {
+                    hres.setDateHeader("Retry-After", available);
+                    // BEGIN S1AS 4878272
+                    hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+
+                    response.setDetailMessage(msg);
+                    // END S1AS 4878272
+                } else if (available == Long.MAX_VALUE) {
+                    // BEGIN S1AS 4878272
+                    hres.sendError(HttpServletResponse.SC_NOT_FOUND);
+                    msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_NOT_FOUND), wrapper.getName());
+                    response.setDetailMessage(msg);
+                    // END S1AS 4878272
+                }
+            }
+            unavailable = true;
+        }
+
+        // Allocate a servlet instance to process this request
+        try {
+            if (!unavailable) {
+                servlet = wrapper.allocate();
+            }
+        } catch (UnavailableException e) {
+            if (e.isPermanent()) {
+                // BEGIN S1AS 4878272
+                hres.sendError(HttpServletResponse.SC_NOT_FOUND);
+
+                String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_NOT_FOUND), wrapper.getName());
+                response.setDetailMessage(msg);
+
+                // END S1AS 4878272
+            } else {
+                hres.setDateHeader("Retry-After", e.getUnavailableSeconds());
+                // BEGIN S1AS 4878272
+                hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+                String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_UNAVAILABLE), wrapper.getName());
+                response.setDetailMessage(msg);
+                // END S1AS 4878272
+            }
+        } catch (ServletException e) {
+
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_ALLOCATE_EXCEPTION), wrapper.getName());
+            log(msg, StandardWrapper.getRootCause(e));
+
+            throwable = e;
+            exception(request, response, e);
+            servlet = null;
+        } catch (Throwable e) {
+
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_ALLOCATE_EXCEPTION), wrapper.getName());
+            log(msg, e);
+
+            throwable = e;
+            exception(request, response, e);
+            servlet = null;
+        }
+
+        // Acknowlege the request
+        try {
+            response.sendAcknowledgement();
+        } catch (IOException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SEND_ACKNOWLEDGEMENT_EXCEPTION), wrapper.getName());
+            log(msg, e);
+            throwable = e;
+            exception(request, response, e);
+        } catch (Throwable e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SEND_ACKNOWLEDGEMENT_EXCEPTION), wrapper.getName());
+            log(msg, e);
+            throwable = e;
+            exception(request, response, e);
+            servlet = null;
+        }
+        DataChunk requestPathMB = hrequest.getRequestPathMB();
+        hreq.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
+                          requestPathMB);
+
+        // Create the filter chain for this request
+        ApplicationFilterFactory factory =
+            ApplicationFilterFactory.getInstance();
+        ApplicationFilterChain filterChain =
+            factory.createFilterChain((ServletRequest) request,
+                                      wrapper, servlet);
+
+        // Call the filter chain for this request
+        // NOTE: This also calls the servlet's service() method
+        try {
+            String jspFile = wrapper.getJspFile();
+            if (jspFile != null) {
+                hreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
+            } 
+            /* IASRI 4665318
+            if ((servlet != null) && (filterChain != null)) {
+                filterChain.doFilter(hreq, hres);
+            }
+            */
+            // START IASRI 4665318
+            if (servlet != null) {
+                if (filterChain != null) {
+                    filterChain.setWrapper(wrapper);
+                    filterChain.doFilter(hreq, hres);
+                } else {
+                    wrapper.service(hreq, hres, servlet);
+                }
+            }
+            // END IASRI 4665318
+        } catch (ClientAbortException e) {
+            throwable = e;
+            exception(request, response, e);
+        } catch (IOException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_SERVICE_EXCEPTION), wrapper.getName());
+            log(msg, e);
+            throwable = e;
+            exception(request, response, e);
+        } catch (UnavailableException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_SERVICE_EXCEPTION), wrapper.getName());
+            log(msg, e);
+            //            throwable = e;
+            //            exception(request, response, e);
+            wrapper.unavailable(e);
+            long available = wrapper.getAvailable();
+            if ((available > 0L) && (available < Long.MAX_VALUE)) {
+                hres.setDateHeader("Retry-After", available);
+                // BEGIN S1AS 4878272
+                hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+                String msgServletUnavailable = MessageFormat.format(rb.getString(LogFacade.SERVLET_UNAVAILABLE), wrapper.getName());
+                response.setDetailMessage(msgServletUnavailable);
+                // END S1AS 4878272
+            } else if (available == Long.MAX_VALUE) {
+                // BEGIN S1AS 4878272
+                hres.sendError(HttpServletResponse.SC_NOT_FOUND);
+                String msgServletNotFound = MessageFormat.format(rb.getString(LogFacade.SERVLET_NOT_FOUND), wrapper.getName());
+                response.setDetailMessage(msgServletNotFound);
+                // END S1AS 4878272
+            }
+            // Do not save exception in 'throwable', because we
+            // do not want to do exception(request, response, e) processing
+        } catch (ServletException e) {
+            Throwable rootCause = StandardWrapper.getRootCause(e);
+            if (!(rootCause instanceof ClientAbortException)) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_SERVICE_EXCEPTION), wrapper.getName());
+                log(msg, rootCause);
+            }
+            throwable = e;
+            exception(request, response, e);
+        } catch (Throwable e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_SERVICE_EXCEPTION), wrapper.getName());
+            log(msg, e);
+            throwable = e;
+            exception(request, response, e);
+        }
+
+        // Release the filter chain (if any) for this request
+        try {
+            if (filterChain != null)
+                filterChain.release();
+        } catch (Throwable e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.RELEASE_FILTERS_EXCEPTION), wrapper.getName());
+            log(msg, e);
+            if (throwable == null) {
+                throwable = e;
+                exception(request, response, e);
+            }
+        }
+
+        // Deallocate the allocated servlet instance
+        try {
+            if (servlet != null) {
+                wrapper.deallocate(servlet);
+            }
+        } catch (Throwable e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DEALLOCATE_EXCEPTION), wrapper.getName());
+            log(msg, e);
+            if (throwable == null) {
+                throwable = e;
+                exception(request, response, e);
+            }
+        }
+
+        // If this servlet has been marked permanently unavailable,
+        // unload it and release this instance
+        try {
+            if ((servlet != null) &&
+                (wrapper.getAvailable() == Long.MAX_VALUE)) {
+                wrapper.unload();
+            }
+        } catch (Throwable e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVLET_UNLOAD_EXCEPTION), wrapper.getName());
+            log(msg, e);
+            if (throwable == null) {
+                exception(request, response, e);
+            }
+        }
+
+        return END_PIPELINE;
+    }
+
+
+    /**
+     * Tomcat style invocation.
+     */
+    @Override
+    public void invoke(org.apache.catalina.connector.Request request,
+                       org.apache.catalina.connector.Response response)
+            throws IOException, ServletException {
+        invoke((Request) request, (Response) 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) {
+        org.apache.catalina.Logger logger = null;
+        String containerName = null;
+        if (container != null) {
+            logger = container.getLogger();
+            containerName = container.getName();
+        }
+        if (logger != null) {
+            logger.log("StandardWrapperValve[" + containerName + "]: " +
+                       message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.STANDARD_WRAPPER_VALVE, new Object[] {containerName, message});
+            }
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    private void log(String message, Throwable t) {
+        org.apache.catalina.Logger logger = null;
+        String containerName = null;
+        if (container != null) {
+            logger = container.getLogger();
+            containerName = container.getName();
+        }
+        if (logger != null) {
+            logger.log("StandardWrapperValve[" + containerName + "]: " +
+                message, t, org.apache.catalina.Logger.WARNING);
+        } else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_WRAPPER_VALVE),
+                                              new Object[] {containerName, message});
+            log.log(Level.WARNING, msg, t);
+        }
+    }
+
+
+    /**
+     * Handle the specified ServletException encountered while processing
+     * the specified Request to produce the specified Response.  Any
+     * exceptions that occur during generation of the exception report are
+     * logged and swallowed.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     * @param exception The exception that occurred (which possibly wraps
+     *  a root cause exception
+     */
+    private void exception(Request request, Response response,
+                           Throwable exception) {
+        ServletRequest sreq = request.getRequest();
+        sreq.setAttribute(RequestDispatcher.ERROR_EXCEPTION, exception);
+
+        ServletResponse sresponse = response.getResponse();
+        
+        /* GlassFish 6386229
+        if (sresponse instanceof HttpServletResponse)
+            ((HttpServletResponse) sresponse).setStatus
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        */
+        // START GlassFish 6386229
+        ((HttpServletResponse) sresponse).setStatus
+            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        // END GlassFish 6386229
+    }
+
+    // Don't register in JMX
+
+    public ObjectName createObjectName(String domain, ObjectName parent)
+            throws MalformedObjectNameException {
+        return null;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ApplicationParameter.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ApplicationParameter.java
new file mode 100644
index 0000000..ff87456
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ApplicationParameter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a context initialization parameter that is configured
+ * in the server configuration file, rather than the application deployment
+ * descriptor.  This is convenient for establishing default values (which
+ * may be configured to allow application overrides or not) without having
+ * to modify the application deployment descriptor itself.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:39 $
+ */
+
+public class ApplicationParameter implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this environment entry.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The name of this application parameter.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * Does this application parameter allow overrides by the application
+     * deployment descriptor?
+     */
+    private boolean override = true;
+
+    public boolean getOverride() {
+        return (this.override);
+    }
+
+    public void setOverride(boolean override) {
+        this.override = override;
+    }
+
+
+    /**
+     * The value of this application parameter.
+     */
+    private String value = null;
+
+    public String getValue() {
+        return (this.value);
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ApplicationParameter[");
+        sb.append("name=");
+        sb.append(name);
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        sb.append(", value=");
+        sb.append(value);
+        sb.append(", override=");
+        sb.append(override);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    public boolean equals(Object obj) {
+        if (name == null) {
+            throw new IllegalStateException();
+        }
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof ApplicationParameter)) {
+            return false;
+        }
+	ApplicationParameter that = (ApplicationParameter) obj;
+	return this.name.equals(that.name);
+    }
+
+
+    public int hashCode() {
+        if (name == null) {
+            throw new IllegalStateException();
+        }
+       
+        return name.hashCode();
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextEjb.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextEjb.java
new file mode 100644
index 0000000..8204b2a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextEjb.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of an EJB resource reference for a web application, as
+ * represented in a <code>&lt;ejb-ref&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:39 $
+ */
+
+public class ContextEjb extends ResourceBase {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this EJB.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The name of the EJB home implementation class.
+     */
+    private String home = null;
+
+    public String getHome() {
+        return (this.home);
+    }
+
+    public void setHome(String home) {
+        this.home = home;
+    }
+
+
+    /**
+     * The link to a J2EE EJB definition.
+     */
+    private String link = null;
+
+    public String getLink() {
+        return (this.link);
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+
+    /**
+     * The name of this EJB.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The name of the EJB remote implementation class.
+     */
+    private String remote = null;
+
+    public String getRemote() {
+        return (this.remote);
+    }
+
+    public void setRemote(String remote) {
+        this.remote = remote;
+    }
+
+
+    /**
+     * The name of the EJB bean implementation class.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ContextEjb[");
+        sb.append("name=");
+        sb.append(name);
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        if (type != null) {
+            sb.append(", type=");
+            sb.append(type);
+        }
+        if (home != null) {
+            sb.append(", home=");
+            sb.append(home);
+        }
+        if (remote != null) {
+            sb.append(", remote=");
+            sb.append(remote);
+        }
+        if (link != null) {
+            sb.append(", link=");
+            sb.append(link);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextEnvironment.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextEnvironment.java
new file mode 100644
index 0000000..b27af10
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextEnvironment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of an application environment entry, as represented in
+ * an <code>&lt;env-entry&gt;</code> element in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:39 $
+ */
+
+public class ContextEnvironment extends ResourceBase {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this environment entry.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The name of this environment entry.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * Does this environment entry allow overrides by the application
+     * deployment descriptor?
+     */
+    private boolean override = true;
+
+    public boolean getOverride() {
+        return (this.override);
+    }
+
+    public void setOverride(boolean override) {
+        this.override = override;
+    }
+
+
+    /**
+     * The type of this environment entry.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * The value of this environment entry.
+     */
+    private String value = null;
+
+    public String getValue() {
+        return (this.value);
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ContextEnvironment[");
+        sb.append("name=");
+        sb.append(name);
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        if (type != null) {
+            sb.append(", type=");
+            sb.append(type);
+        }
+        if (value != null) {
+            sb.append(", value=");
+            sb.append(value);
+        }
+        sb.append(", override=");
+        sb.append(override);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextLocalEjb.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextLocalEjb.java
new file mode 100644
index 0000000..8153f11
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextLocalEjb.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a local EJB resource reference for a web application, as
+ * represented in a <code>&lt;ejb-local-ref&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:39 $
+ */
+
+public class ContextLocalEjb extends ResourceBase {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this EJB.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The name of the EJB home implementation class.
+     */
+    private String home = null;
+
+    public String getHome() {
+        return (this.home);
+    }
+
+    public void setHome(String home) {
+        this.home = home;
+    }
+
+
+    /**
+     * The link to a J2EE EJB definition.
+     */
+    private String link = null;
+
+    public String getLink() {
+        return (this.link);
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+
+    /**
+     * The name of the EJB local implementation class.
+     */
+    private String local = null;
+
+    public String getLocal() {
+        return (this.local);
+    }
+
+    public void setLocal(String local) {
+        this.local = local;
+    }
+
+
+    /**
+     * The name of this EJB.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The name of the EJB bean implementation class.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ContextLocalEjb[");
+        sb.append("name=");
+        sb.append(name);
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        if (type != null) {
+            sb.append(", type=");
+            sb.append(type);
+        }
+        if (home != null) {
+            sb.append(", home=");
+            sb.append(home);
+        }
+        if (link != null) {
+            sb.append(", link=");
+            sb.append(link);
+        }
+        if (local != null) {
+            sb.append(", local=");
+            sb.append(local);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextResource.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextResource.java
new file mode 100644
index 0000000..fa4fb41
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextResource.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a resource reference for a web application, as
+ * represented in a <code>&lt;resource-ref&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:40 $
+ */
+
+public class ContextResource extends ResourceBase {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The authorization requirement for this resource
+     * (<code>Application</code> or <code>Container</code>).
+     */
+    private String auth = null;
+
+    public String getAuth() {
+        return (this.auth);
+    }
+
+    public void setAuth(String auth) {
+        this.auth = auth;
+    }
+
+
+    /**
+     * The description of this resource.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The name of this resource.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The sharing scope of this resource factory (<code>Shareable</code>
+     * or <code>Unshareable</code>).
+     */
+    private String scope = "Shareable";
+
+    public String getScope() {
+        return (this.scope);
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+
+    /**
+     * The type of this resource.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ContextResource[");
+        sb.append("name=");
+        sb.append(name);
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        if (type != null) {
+            sb.append(", type=");
+            sb.append(type);
+        }
+        if (auth != null) {
+            sb.append(", auth=");
+            sb.append(auth);
+        }
+        if (scope != null) {
+            sb.append(", scope=");
+            sb.append(scope);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextResourceLink.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextResourceLink.java
new file mode 100644
index 0000000..1799efe
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ContextResourceLink.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a resource link for a web application, as
+ * represented in a <code>&lt;ResourceLink&gt;</code> element in the
+ * server configuration file.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:40 $
+ */
+
+public class ContextResourceLink extends ResourceBase {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The name of this resource.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The type of this resource.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * The global name of this resource.
+     */
+    private String global = null;
+
+    public String getGlobal() {
+        return (this.global);
+    }
+
+    public void setGlobal(String global) {
+        this.global = global;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ContextResourceLink[");
+        sb.append("name=");
+        sb.append(name);
+        if (type != null) {
+            sb.append(", type=");
+            sb.append(type);
+        }
+        if (global != null) {
+            sb.append(", global=");
+            sb.append(global);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ErrorPage.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ErrorPage.java
new file mode 100644
index 0000000..25e3bf0
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ErrorPage.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+
+import org.apache.catalina.util.RequestUtil;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of an error page element for a web application,
+ * as represented in a <code>&lt;error-page&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:27:40 $
+ */
+
+public class ErrorPage implements Serializable {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The error (status) code for which this error page is active.
+     */
+    private int errorCode = 0;
+
+
+    /**
+     * The exception type for which this error page is active.
+     */
+    private String exceptionType = null;
+
+
+    /**
+     * The context-relative location to handle this error or exception.
+     */
+    private String location = null;
+
+
+    // START SJSAS 6324911
+    /**
+     * The reason string to be displayed with the error (status) code
+     */
+    private String reason = null;
+    // END SJSAS 6324911
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the error code.
+     */
+    public int getErrorCode() {
+
+        return (this.errorCode);
+
+    }
+
+
+    /**
+     * Set the error code.
+     *
+     * @param errorCode The new error code
+     */
+    public void setErrorCode(int errorCode) {
+
+        this.errorCode = errorCode;
+
+    }
+
+
+    /**
+     * Set the error code (hack for default XmlMapper data type).
+     *
+     * @param errorCode The new error code
+     */
+    public void setErrorCode(String errorCode) {
+
+        try {
+            this.errorCode = Integer.parseInt(errorCode);
+        } catch (Throwable t) {
+            this.errorCode = 0;
+        }
+
+    }
+
+
+    /**
+     * Return the exception type.
+     */
+    public String getExceptionType() {
+
+        return (this.exceptionType);
+
+    }
+
+
+    /**
+     * Set the exception type.
+     *
+     * @param exceptionType The new exception type
+     */
+    public void setExceptionType(String exceptionType) {
+
+        this.exceptionType = exceptionType;
+
+    }
+
+
+    /**
+     * Return the location.
+     */
+    public String getLocation() {
+
+        return (this.location);
+
+    }
+
+
+    /**
+     * Set the location.
+     *
+     * @param location The new location
+     */
+    public void setLocation(String location) {
+
+        //        if ((location == null) || !location.startsWith("/"))
+        //            throw new IllegalArgumentException
+        //                ("Error Page Location must start with a '/'");
+        this.location = RequestUtil.urlDecode(location);
+
+    }
+
+
+    // START SJSAS 6324911
+    /**
+     * Gets the reason string that is associated with the error (status) code 
+     * for which this error page is active.
+     *
+     * @return The reason string of this error page
+     */
+    public String getReason() {
+        return reason;
+    }
+
+    /**
+     * Sets the reason string to be associated with the error (status) code 
+     * for which this error page is active.
+     *
+     * @param reason The reason string
+     */
+    public void setReason(String reason) {
+        this.reason = reason;
+    }
+    // END SJSAS 6324911
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ErrorPage[");
+        if (exceptionType == null) {
+            sb.append("errorCode=");
+            sb.append(errorCode);
+        } else {
+            sb.append("exceptionType=");
+            sb.append(exceptionType);
+        }
+        sb.append(", location=");
+        sb.append(location);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterDef.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterDef.java
new file mode 100644
index 0000000..bbc667e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterDef.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import org.apache.catalina.util.Enumerator;
+
+import javax.servlet.Filter;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Representation of a filter definition for a web application, as represented
+ * in a <code>&lt;filter&gt;</code> element in the deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3.6.1 $ $Date: 2008/04/17 18:37:10 $
+ */
+
+public class FilterDef implements Serializable {
+
+    /**
+     * The description of this filter.
+     */
+    private String description = null;
+
+    /**
+     * The display name of this filter.
+     */
+    private String displayName = null;
+
+    /**
+     * The fully qualified name of the Java class that implements this filter.
+     */
+    private String filterClassName = null;
+
+    /*
+     * The class from which this filter will be instantiated
+     */
+    private Class <? extends Filter> filterClass;
+
+    /*
+     * The filter instance
+     */
+    private transient Filter filter;
+
+    /**
+     * The name of this filter, which must be unique among the filters
+     * defined for a particular web application.
+     */
+    private String filterName = null;
+
+    /**
+     * The large icon associated with this filter.
+     */
+    private String largeIcon = null;
+
+    /**
+     * The small icon associated with this filter.
+     */
+    private String smallIcon = null;
+
+    /**
+     * The set of initialization parameters for this filter, keyed by
+     * parameter name.
+     */
+    private Map<String, String> parameters = new HashMap<String, String>();
+
+    /**
+     * True if this filter supports async operations, false otherwise
+     */
+    private boolean isAsyncSupported = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    public String getDisplayName() {
+        return (this.displayName);
+    }
+
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+
+    public String getFilterClassName() {
+        return (this.filterClassName);
+    }
+
+
+    public void setFilterClassName(String filterClassName) {
+        if (filterClass != null) {
+            throw new IllegalStateException("Filter class already set");
+        }
+        this.filterClassName = filterClassName;
+    }
+
+
+    public Class <? extends Filter> getFilterClass() {
+        return filterClass;
+    }
+
+
+    public void setFilterClass(Class <? extends Filter> filterClass) {
+        if (filterClassName != null) {
+            throw new IllegalStateException("Filter class name already set");
+        }
+        this.filterClass = filterClass;
+        this.filterClassName = filterClass.getName();
+    }
+
+
+    public Filter getFilter() {
+        return filter;
+    }
+
+
+    public void setFilter(Filter filter) {
+        if (filter == null) {
+            throw new NullPointerException("Null Filter instance");
+        }
+        if (filterClassName != null) {
+            throw new IllegalStateException("Filter class name already set");
+        }
+        this.filter = filter;
+        this.filterClassName = filter.getClass().getName();
+    }
+
+
+    public String getFilterName() {
+        return (this.filterName);
+    }
+
+
+    public void setFilterName(String filterName) {
+        this.filterName = filterName;
+    }
+
+
+    public String getLargeIcon() {
+        return (this.largeIcon);
+    }
+
+
+    public void setLargeIcon(String largeIcon) {
+        this.largeIcon = largeIcon;
+    }
+
+
+    public String getSmallIcon() {
+        return (this.smallIcon);
+    }
+
+
+    public void setSmallIcon(String smallIcon) {
+        this.smallIcon = smallIcon;
+    }
+
+
+    /**
+     * Configures this filter as either supporting or not supporting
+     * asynchronous operations.
+     *
+     * @param isAsyncSupported true if this filter supports asynchronous
+     * operations, false otherwise
+     */
+    public void setIsAsyncSupported(boolean isAsyncSupported) {
+        this.isAsyncSupported = isAsyncSupported;
+    }
+
+
+    /**
+     * Checks if this filter has been annotated or flagged in the deployment
+     * descriptor as being able to support asynchronous operations.
+     *
+     * @return true if this filter supports async operations, and false
+     * otherwise
+     */
+    public boolean isAsyncSupported() {
+        return isAsyncSupported;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Adds the initialization parameter with the given name and value
+     * on this filter.
+     *
+     * <p>If an init param with the given name already exists, its value
+     * will be overridden.
+     *
+     * @param name the init parameter name
+     * @param value the init parameter value
+     */
+    public void addInitParameter(String name, String value) {
+        setInitParameter(name, value, true);
+    }
+
+
+    /**
+     * Sets the init parameter with the given name and value
+     * on this filter.
+     *
+     * @param name the init parameter name
+     * @param value the init parameter value
+     * @param override true if the given init param is supposed to
+     * override an existing init param with the same name, and false
+     * otherwise
+     *
+     * @return true if the init parameter with the given name and value
+     * was set, false otherwise
+     */
+    public boolean setInitParameter(String name, String value, 
+                                    boolean override) {
+        if (null == name || null == value) {
+            throw new IllegalArgumentException(
+                "Null filter init parameter name or value");
+        }
+
+        synchronized (parameters) {
+            if (override || !parameters.containsKey(name)) {
+                parameters.put(name, value);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+
+    /**
+     * Sets the initialization parameters contained in the given map
+     * on this filter.
+     *
+     * @param initParameters the map with the init params to set
+     *
+     * @return the (possibly empty) Set of initialization parameter names
+     * that are in conflict
+     */
+    public Set<String> setInitParameters(Map<String, String> initParameters) {
+        if (null == initParameters) {
+            throw new IllegalArgumentException("Null init parameters");
+        }
+
+        synchronized (parameters) {
+            Set<String> conflicts = null;
+            for (Map.Entry<String, String> e : initParameters.entrySet()) {
+                if (e.getKey() == null || e.getValue() == null) {
+                    throw new IllegalArgumentException(
+                        "Null parameter name or value");
+                }
+                if (parameters.containsKey(e.getKey())) {
+                    if (conflicts == null) {
+                        conflicts = new HashSet<String>();    
+                    }
+                    conflicts.add(e.getKey());
+                }
+            }
+
+            if (conflicts != null) {
+                return conflicts;
+            }
+
+            for (Map.Entry<String, String> e : initParameters.entrySet()) {
+                setInitParameter(e.getKey(), e.getValue(), true);
+            }
+   
+            return Collections.emptySet();
+        }
+    }
+
+
+    public String getInitParameter(String name) {
+        synchronized (parameters) {
+            return parameters.get(name);
+        }
+    }        
+
+
+    public Map<String, String> getInitParameters() {
+        synchronized (parameters) {
+            return Collections.unmodifiableMap(parameters);
+        }
+    }
+
+
+    public Enumeration<String> getInitParameterNames() {
+        synchronized (parameters) {
+            return new Enumerator<String>(parameters.keySet());
+        }
+    }
+
+
+    /**
+     * Removes the initialization parameter with the given name.
+     *
+     * @param name the name of the initialization parameter to be removed
+     */
+    public void removeInitParameter(String name) {
+        synchronized (parameters) {
+            parameters.remove(name);
+        }
+    }
+
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("FilterDef[");
+        sb.append("filterName=");
+        sb.append(this.filterName);
+        sb.append(", filterClassname=");
+        sb.append(this.filterClassName);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterMap.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterMap.java
new file mode 100644
index 0000000..e5e7cd7
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterMap.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import org.apache.catalina.util.RequestUtil;
+
+import javax.servlet.DispatcherType;
+import java.io.Serializable;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * Representation of a filter mapping for a web application, as represented
+ * in a <code>&lt;filter-mapping&gt;</code> element in the deployment
+ * descriptor.  Each filter mapping must contain a filter name plus either
+ * a URL pattern or a servlet name.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2007/01/23 00:06:56 $
+ */
+
+public class FilterMap implements Serializable {
+
+    private static final EnumSet<DispatcherType> DEFAULT_DISPATCHER =
+        EnumSet.of(DispatcherType.REQUEST);
+
+
+    /**
+     * The name of the filter with which this filter mapping is associated
+     */
+    private String filterName = null;    
+
+    /**
+     * The servlet name for which this filter mapping applies
+     */
+    private String servletName = null;
+
+    /**
+     * The URL pattern for which this filter mapping applies
+     */
+    private String urlPattern = null;
+
+    /**
+     * The dispatcher types of this filter mapping
+     */
+    private Set<DispatcherType> dispatcherTypes;
+
+
+    // ------------------------------------------------------------- Properties
+
+    public String getFilterName() {
+        return (this.filterName);
+    }
+
+    public void setFilterName(String filterName) {
+        this.filterName = filterName;
+    }
+
+    public String getServletName() {
+        return (this.servletName);
+    }
+
+    public void setServletName(String servletName) {
+        this.servletName = servletName;
+    }
+
+    public String getURLPattern() {
+        return (this.urlPattern);
+    }
+
+    public void setURLPattern(String urlPattern) {
+        this.urlPattern = RequestUtil.urlDecode(urlPattern);
+    }
+    
+    public Set<DispatcherType> getDispatcherTypes() {
+        // Per the SRV.6.2.5 absence of any dispatcher elements is
+        // equivelant to a REQUEST value
+        return (dispatcherTypes == null || dispatcherTypes.isEmpty()) ?
+            DEFAULT_DISPATCHER : dispatcherTypes;
+    }
+
+    public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {
+        this.dispatcherTypes = dispatcherTypes;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("FilterMap[");
+        sb.append("filterName=");
+        sb.append(this.filterName);
+        if (servletName != null) {
+            sb.append(", servletName=");
+            sb.append(servletName);
+        }
+        if (urlPattern != null) {
+            sb.append(", urlPattern=");
+            sb.append(urlPattern);
+        }
+        sb.append("]");
+        return (sb.toString());
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterMaps.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterMaps.java
new file mode 100644
index 0000000..eadc3e5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/FilterMaps.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import javax.servlet.DispatcherType;
+import java.util.Set;
+
+/**
+ * Representation of a filter mapping for a web application, as represented
+ * in a <code>&lt;filter-mapping&gt;</code> element in the deployment
+ * descriptor.  Each filter mapping must contain a filter name and any 
+ * number of URL patterns and servlet names.
+ *
+ */
+
+public class FilterMaps {
+
+    private String[] urlPatterns = new String[0];
+    private String[] servletNames = new String[0];
+    private String filterName = null;
+    private Set<DispatcherType> dispatcherTypes;
+
+    // ------------------------------------------------------------ Properties
+    
+    public void setFilterName(String filterName) {
+        this.filterName = filterName;
+    }
+
+    public String getFilterName() {
+        return filterName;
+    }
+
+    public void addServletName(String servletName) {
+        String[] results = new String[servletNames.length + 1];
+        System.arraycopy(servletNames, 0, results, 0, servletNames.length);
+        results[servletNames.length] = servletName;
+        servletNames = results;
+    }
+
+    public String[] getServletNames() {
+        return servletNames;
+    }
+
+    public void addURLPattern(String urlPattern) {
+        String[] results = new String[urlPatterns.length + 1];
+        System.arraycopy(urlPatterns, 0, results, 0, urlPatterns.length);
+        results[urlPatterns.length] = urlPattern;
+        urlPatterns = results;
+    }
+
+    public String[] getURLPatterns() {
+        return urlPatterns;
+    }
+    
+    public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {
+        this.dispatcherTypes = dispatcherTypes;
+    }
+
+    public Set<DispatcherType> getDispatcherTypes() {
+        return dispatcherTypes;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/LoginConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/LoginConfig.java
new file mode 100644
index 0000000..79520ee
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/LoginConfig.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+
+import org.apache.catalina.util.RequestUtil;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a login configuration element for a web application,
+ * as represented in a <code>&lt;login-config&gt;</code> element in the
+ * deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:41 $
+ */
+
+public class LoginConfig implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new LoginConfig with default properties.
+     */
+    public LoginConfig() {
+
+        super();
+
+    }
+
+
+    /**
+     * Construct a new LoginConfig with the specified properties.
+     *
+     * @param authMethod The authentication method
+     * @param realmName The realm name
+     * @param loginPage The login page URI
+     * @param errorPage The error page URI
+     */
+    public LoginConfig(String authMethod, String realmName,
+                       String loginPage, String errorPage) {
+
+        super();
+        setAuthMethod(authMethod);
+        setRealmName(realmName);
+        setLoginPage(loginPage);
+        setErrorPage(errorPage);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The authentication method to use for application login.  Must be
+     * BASIC, DIGEST, FORM, or CLIENT-CERT.
+     */
+    private String authMethod = null;
+
+    public String getAuthMethod() {
+        return (this.authMethod);
+    }
+
+    public void setAuthMethod(String authMethod) {
+        this.authMethod = authMethod;
+    }
+
+
+    /**
+     * The context-relative URI of the error page for form login.
+     */
+    private String errorPage = null;
+
+    public String getErrorPage() {
+        return (this.errorPage);
+    }
+
+    public void setErrorPage(String errorPage) {
+        //        if ((errorPage == null) || !errorPage.startsWith("/"))
+        //            throw new IllegalArgumentException
+        //                ("Error Page resource path must start with a '/'");
+        this.errorPage = RequestUtil.urlDecode(errorPage);
+    }
+
+
+    /**
+     * The context-relative URI of the login page for form login.
+     */
+    private String loginPage = null;
+
+    public String getLoginPage() {
+        return (this.loginPage);
+    }
+
+    public void setLoginPage(String loginPage) {
+        //        if ((loginPage == null) || !loginPage.startsWith("/"))
+        //            throw new IllegalArgumentException
+        //                ("Login Page resource path must start with a '/'");
+        this.loginPage = RequestUtil.urlDecode(loginPage);
+    }
+
+
+    /**
+     * The realm name used when challenging the user for authentication
+     * credentials.
+     */
+    private String realmName = null;
+
+    public String getRealmName() {
+        return (this.realmName);
+    }
+
+    public void setRealmName(String realmName) {
+        this.realmName = realmName;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("LoginConfig[");
+        sb.append("authMethod=");
+        sb.append(authMethod);
+        if (realmName != null) {
+            sb.append(", realmName=");
+            sb.append(realmName);
+        }
+        if (loginPage != null) {
+            sb.append(", loginPage=");
+            sb.append(loginPage);
+        }
+        if (errorPage != null) {
+            sb.append(", errorPage=");
+            sb.append(errorPage);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/MessageDestination.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/MessageDestination.java
new file mode 100644
index 0000000..689bfe8
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/MessageDestination.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+
+/**
+ * <p>Representation of a message destination for a web application, as
+ * represented in a <code>&lt;message-destination&gt;</code> element
+ * in the deployment descriptor.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:41 $
+ * @since Tomcat 5.0
+ */
+
+public class MessageDestination {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this destination.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The display name of this destination.
+     */
+    private String displayName = null;
+
+    public String getDisplayName() {
+        return (this.displayName);
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+
+    /**
+     * The large icon of this destination.
+     */
+    private String largeIcon = null;
+
+    public String getLargeIcon() {
+        return (this.largeIcon);
+    }
+
+    public void setLargeIcon(String largeIcon) {
+        this.largeIcon = largeIcon;
+    }
+
+
+    /**
+     * The name of this destination.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The small icon of this destination.
+     */
+    private String smallIcon = null;
+
+    public String getSmallIcon() {
+        return (this.smallIcon);
+    }
+
+    public void setSmallIcon(String smallIcon) {
+        this.smallIcon = smallIcon;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("MessageDestination[");
+        sb.append("name=");
+        sb.append(name);
+        if (displayName != null) {
+            sb.append(", displayName=");
+            sb.append(displayName);
+        }
+        if (largeIcon != null) {
+            sb.append(", largeIcon=");
+            sb.append(largeIcon);
+        }
+        if (smallIcon != null) {
+            sb.append(", smallIcon=");
+            sb.append(smallIcon);
+        }
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/MessageDestinationRef.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/MessageDestinationRef.java
new file mode 100644
index 0000000..4ddb830
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/MessageDestinationRef.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+
+
+/**
+ * <p>Representation of a message destination reference for a web application,
+ * as represented in a <code>&lt;message-destination-ref&gt;</code> element
+ * in the deployment descriptor.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:41 $
+ * @since Tomcat 5.0
+ */
+
+public class MessageDestinationRef implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this destination ref.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+    /**
+     * The link of this destination ref.
+     */
+    private String link = null;
+
+    public String getLink() {
+        return (this.link);
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+
+    /**
+     * The name of this destination ref.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The type of this destination ref.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * The usage of this destination ref.
+     */
+    private String usage = null;
+
+    public String getUsage() {
+        return (this.usage);
+    }
+
+    public void setUsage(String usage) {
+        this.usage = usage;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("MessageDestination[");
+        sb.append("name=");
+        sb.append(name);
+        if (link != null) {
+            sb.append(", link=");
+            sb.append(link);
+        }
+        if (type != null) {
+            sb.append(", type=");
+            sb.append(type);
+        }
+        if (usage != null) {
+            sb.append(", usage=");
+            sb.append(usage);
+        }
+        if (description != null) {
+            sb.append(", description=");
+            sb.append(description);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * The NamingResources with which we are associated (if any).
+     */
+    protected NamingResources resources = null;
+
+    public NamingResources getNamingResources() {
+        return (this.resources);
+    }
+
+    void setNamingResources(NamingResources resources) {
+        this.resources = resources;
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/NamingResources.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/NamingResources.java
new file mode 100644
index 0000000..29a52fb
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/NamingResources.java
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Hashtable;
+
+
+/**
+ * Holds and manages the naming resources defined in the J2EE Enterprise 
+ * Naming Context and their associated JNDI context.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:42 $
+ */
+
+public class NamingResources implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new NamingResources instance.
+     */
+    public NamingResources() {
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated container object.
+     */
+    private Object container = null;
+
+
+    /**
+     * List of naming entries, keyed by name. The value is the entry type, as
+     * declared by the user.
+     */
+    private Hashtable<String, String> entries =
+        new Hashtable<String, String>();
+
+
+    /**
+     * The EJB resource references for this web application, keyed by name.
+     */
+    private HashMap<String, ContextEjb> ejbs = new HashMap<String, ContextEjb>();
+
+
+    /**
+     * The environment entries for this web application, keyed by name.
+     */
+    private HashMap<String, ContextEnvironment> envs =
+        new HashMap<String, ContextEnvironment>();
+
+
+    /**
+     * The local  EJB resource references for this web application, keyed by
+     * name.
+     */
+    private HashMap<String, ContextLocalEjb> localEjbs =
+        new HashMap<String, ContextLocalEjb>();
+
+
+    /**
+     * The message destination referencess for this web application,
+     * keyed by name.
+     */
+    private HashMap<String, MessageDestinationRef> mdrs =
+        new HashMap<String, MessageDestinationRef>();
+
+
+    /**
+     * The resource environment references for this web application,
+     * keyed by name.
+     */
+    private HashMap<String, String> resourceEnvRefs =
+        new HashMap<String, String>();
+
+
+    /**
+     * The resource references for this web application, keyed by name.
+     */
+    private HashMap<String, ContextResource> resources =
+        new HashMap<String, ContextResource>();
+
+
+    /**
+     * The resource links for this web application, keyed by name.
+     */
+    private HashMap<String, ContextResourceLink> resourceLinks =
+        new HashMap<String, ContextResourceLink>();
+
+
+    /**
+     * The resource parameters for this web application, keyed by name.
+     */
+    private HashMap<String, ResourceParams> resourceParams =
+        new HashMap<String, ResourceParams>();
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Get the container with which the naming resources are associated.
+     */
+    public Object getContainer() {
+        return container;
+    }
+
+
+    /**
+     * Set the container with which the naming resources are associated.
+     */
+    public void setContainer(Object container) {
+        this.container = container;
+    }
+
+
+    /**
+     * Add an EJB resource reference for this web application.
+     *
+     * @param ejb New EJB resource reference
+     */
+    public void addEjb(ContextEjb ejb) {
+
+        if (entries.containsKey(ejb.getName())) {
+            return;
+        } else {
+            entries.put(ejb.getName(), ejb.getType());
+        }
+
+        synchronized (ejbs) {
+            ejb.setNamingResources(this);
+            ejbs.put(ejb.getName(), ejb);
+        }
+        support.firePropertyChange("ejb", null, ejb);
+
+    }
+
+
+    /**
+     * Add an environment entry for this web application.
+     *
+     * @param environment New environment entry
+     */
+    public void addEnvironment(ContextEnvironment environment) {
+
+        if (entries.containsKey(environment.getName())) {
+            return;
+        } else {
+            entries.put(environment.getName(), environment.getType());
+        }
+
+        synchronized (envs) {
+            environment.setNamingResources(this);
+            envs.put(environment.getName(), environment);
+        }
+        support.firePropertyChange("environment", null, environment);
+
+    }
+
+
+    /**
+     * Add resource parameters for this web application.
+     *
+     * @param resourceParameters New resource parameters
+     */
+    public void addResourceParams(ResourceParams resourceParameters) {
+
+        synchronized (resourceParams) {
+            if (resourceParams.containsKey(resourceParameters.getName())) {
+                return;
+            }
+            resourceParameters.setNamingResources(this);
+            resourceParams.put(resourceParameters.getName(),
+                               resourceParameters);
+        }
+        support.firePropertyChange("resourceParams", null, resourceParameters);
+
+    }
+
+
+    /**
+     * Add a local EJB resource reference for this web application.
+     *
+     * @param ejb New EJB resource reference
+     */
+    public void addLocalEjb(ContextLocalEjb ejb) {
+
+        if (entries.containsKey(ejb.getName())) {
+            return;
+        } else {
+            entries.put(ejb.getName(), ejb.getType());
+        }
+
+        synchronized (localEjbs) {
+            ejb.setNamingResources(this);
+            localEjbs.put(ejb.getName(), ejb);
+        }
+        support.firePropertyChange("localEjb", null, ejb);
+
+    }
+
+
+    /**
+     * Add a message destination reference for this web application.
+     *
+     * @param mdr New message destination reference
+     */
+    public void addMessageDestinationRef(MessageDestinationRef mdr) {
+
+        if (entries.containsKey(mdr.getName())) {
+            return;
+        } else {
+            entries.put(mdr.getName(), mdr.getType());
+        }
+
+        synchronized (mdrs) {
+            mdr.setNamingResources(this);
+            mdrs.put(mdr.getName(), mdr);
+        }
+        support.firePropertyChange("messageDestinationRef", null, mdr);
+
+    }
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Add a resource reference for this web application.
+     *
+     * @param resource New resource reference
+     */
+    public void addResource(ContextResource resource) {
+
+        if (entries.containsKey(resource.getName())) {
+            return;
+        } else {
+            entries.put(resource.getName(), resource.getType());
+        }
+
+        synchronized (resources) {
+            resource.setNamingResources(this);
+            resources.put(resource.getName(), resource);
+        }
+        support.firePropertyChange("resource", null, resource);
+
+    }
+
+
+    /**
+     * Add a resource environment reference for this web application.
+     *
+     * @param name The resource environment reference name
+     * @param type The resource environment reference type
+     */
+    public void addResourceEnvRef(String name, String type) {
+
+        if (entries.containsKey(name)) {
+            return;
+        } else {
+            entries.put(name, type);
+        }
+
+        synchronized (resourceEnvRefs) {
+            resourceEnvRefs.put(name, type);
+        }
+        support.firePropertyChange("resourceEnvRef", null,
+                                   name + ":" + type);
+
+    }
+
+
+    /**
+     * Add a resource link for this web application.
+     *
+     * @param resource New resource link
+     */
+    public void addResourceLink(ContextResourceLink resourceLink) {
+
+        if (entries.containsKey(resourceLink.getName())) {
+            return;
+        } else {
+            String value = resourceLink.getType();
+            if (value == null) {
+                value = "";
+            }
+            entries.put(resourceLink.getName(), value);
+        }
+
+        synchronized (resourceLinks) {
+            resourceLink.setNamingResources(this);
+            resourceLinks.put(resourceLink.getName(), resourceLink);
+        }
+        support.firePropertyChange("resourceLink", null, resourceLink);
+
+    }
+
+
+    /**
+     * Return the EJB resource reference with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired EJB resource reference
+     */
+    public ContextEjb findEjb(String name) {
+
+        synchronized (ejbs) {
+            return ejbs.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the defined EJB resource references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    public ContextEjb[] findEjbs() {
+
+        synchronized (ejbs) {
+            ContextEjb results[] = new ContextEjb[ejbs.size()];
+            return ejbs.values().toArray(results);
+        }
+
+    }
+
+
+    /**
+     * Return the environment entry with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired environment entry
+     */
+    public ContextEnvironment findEnvironment(String name) {
+
+        synchronized (envs) {
+            return envs.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the set of defined environment entries for this web
+     * application.  If none have been defined, a zero-length array
+     * is returned.
+     */
+    public ContextEnvironment[] findEnvironments() {
+
+        synchronized (envs) {
+            ContextEnvironment results[] = new ContextEnvironment[envs.size()];
+            return envs.values().toArray(results);
+        }
+
+    }
+
+
+    /**
+     * Return the local EJB resource reference with the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired EJB resource reference
+     */
+    public ContextLocalEjb findLocalEjb(String name) {
+
+        synchronized (localEjbs) {
+            return localEjbs.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the defined local EJB resource references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    public ContextLocalEjb[] findLocalEjbs() {
+
+        synchronized (localEjbs) {
+            ContextLocalEjb results[] = new ContextLocalEjb[localEjbs.size()];
+            return localEjbs.values().toArray(results);
+        }
+
+    }
+
+
+    /**
+     * Return the message destination reference with the specified name,
+     * if any; otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired message destination reference
+     */
+    public MessageDestinationRef findMessageDestinationRef(String name) {
+
+        synchronized (mdrs) {
+            return mdrs.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the defined message destination references for this application.
+     * If there are none, a zero-length array is returned.
+     */
+    public MessageDestinationRef[] findMessageDestinationRefs() {
+
+        synchronized (mdrs) {
+            MessageDestinationRef results[] =
+                new MessageDestinationRef[mdrs.size()];
+            return mdrs.values().toArray(results);
+        }
+
+    }
+
+
+    /**
+     * Return the resource reference with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource reference
+     */
+    public ContextResource findResource(String name) {
+
+        synchronized (resources) {
+            return resources.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the resource link with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource link
+     */
+    public ContextResourceLink findResourceLink(String name) {
+
+        synchronized (resourceLinks) {
+            return resourceLinks.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the defined resource links for this application.  If
+     * none have been defined, a zero-length array is returned.
+     */
+    public ContextResourceLink[] findResourceLinks() {
+
+        synchronized (resourceLinks) {
+            ContextResourceLink results[] = 
+                new ContextResourceLink[resourceLinks.size()];
+            return resourceLinks.values().toArray(results);
+        }
+
+    }
+
+
+    /**
+     * Return the defined resource references for this application.  If
+     * none have been defined, a zero-length array is returned.
+     */
+    public ContextResource[] findResources() {
+
+        synchronized (resources) {
+            ContextResource results[] = new ContextResource[resources.size()];
+            return resources.values().toArray(results);
+        }
+
+    }
+
+
+    /**
+     * Return the resource environment reference type for the specified
+     * name, if any; otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource environment reference
+     */
+    public String findResourceEnvRef(String name) {
+
+        synchronized (resourceEnvRefs) {
+            return resourceEnvRefs.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the set of resource environment reference names for this
+     * web application.  If none have been specified, a zero-length
+     * array is returned.
+     */
+    public String[] findResourceEnvRefs() {
+
+        synchronized (resourceEnvRefs) {
+            String results[] = new String[resourceEnvRefs.size()];
+            return resourceEnvRefs.keySet().toArray(results);
+        }
+
+    }
+
+
+    /**
+     * Return the resource parameters with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource parameters
+     */
+    public ResourceParams findResourceParams(String name) {
+
+        synchronized (resourceParams) {
+            return resourceParams.get(name);
+        }
+
+    }
+
+
+    /**
+     * Return the resource parameters with the specified name, if any;
+     * otherwise return <code>null</code>.
+     *
+     * @param name Name of the desired resource parameters
+     */
+    public ResourceParams[] findResourceParams() {
+
+        synchronized (resourceParams) {
+            ResourceParams results[] = 
+                new ResourceParams[resourceParams.size()];
+            return resourceParams.values().toArray(results);
+        }
+
+    }
+
+
+    /**
+     * Return true if the name specified already exists.
+     */
+    public boolean exists(String name) {
+
+        return (entries.containsKey(name));
+
+    }
+
+
+    /**
+     * Remove any EJB resource reference with the specified name.
+     *
+     * @param name Name of the EJB resource reference to remove
+     */
+    public void removeEjb(String name) {
+
+        entries.remove(name);
+
+        ContextEjb ejb = null;
+        synchronized (ejbs) {
+            ejb = ejbs.remove(name);
+        }
+        if (ejb != null) {
+            support.firePropertyChange("ejb", ejb, null);
+            ejb.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any environment entry with the specified name.
+     *
+     * @param name Name of the environment entry to remove
+     */
+    public void removeEnvironment(String name) {
+
+        entries.remove(name);
+
+        ContextEnvironment environment = null;
+        synchronized (envs) {
+            environment = envs.remove(name);
+        }
+        if (environment != null) {
+            support.firePropertyChange("environment", environment, null);
+            environment.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any local EJB resource reference with the specified name.
+     *
+     * @param name Name of the EJB resource reference to remove
+     */
+    public void removeLocalEjb(String name) {
+
+        entries.remove(name);
+
+        ContextLocalEjb localEjb = null;
+        synchronized (localEjbs) {
+            localEjb = localEjbs.remove(name);
+        }
+        if (localEjb != null) {
+            support.firePropertyChange("localEjb", localEjb, null);
+            localEjb.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any message destination reference with the specified name.
+     *
+     * @param name Name of the message destination resource reference to remove
+     */
+    public void removeMessageDestinationRef(String name) {
+
+        entries.remove(name);
+
+        MessageDestinationRef mdr = null;
+        synchronized (mdrs) {
+            mdr = mdrs.remove(name);
+        }
+        if (mdr != null) {
+            support.firePropertyChange("messageDestinationRef",
+                                       mdr, null);
+            mdr.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Remove any resource reference with the specified name.
+     *
+     * @param name Name of the resource reference to remove
+     */
+    public void removeResource(String name) {
+
+        entries.remove(name);
+
+        ContextResource resource = null;
+        synchronized (resources) {
+            resource = resources.remove(name);
+        }
+        if (resource != null) {
+            support.firePropertyChange("resource", resource, null);
+            resource.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any resource environment reference with the specified name.
+     *
+     * @param name Name of the resource environment reference to remove
+     */
+    public void removeResourceEnvRef(String name) {
+
+        entries.remove(name);
+
+        String type = null;
+        synchronized (resourceEnvRefs) {
+            type = resourceEnvRefs.remove(name);
+        }
+        if (type != null) {
+            support.firePropertyChange("resourceEnvRef",
+                                       name + ":" + type, null);
+        }
+
+    }
+
+
+    /**
+     * Remove any resource link with the specified name.
+     *
+     * @param name Name of the resource link to remove
+     */
+    public void removeResourceLink(String name) {
+
+        entries.remove(name);
+
+        ContextResourceLink resourceLink = null;
+        synchronized (resourceLinks) {
+            resourceLink = resourceLinks.remove(name);
+        }
+        if (resourceLink != null) {
+            support.firePropertyChange("resourceLink", resourceLink, null);
+            resourceLink.setNamingResources(null);
+        }
+
+    }
+
+
+    /**
+     * Remove any resource parameters with the specified name.
+     *
+     * @param name Name of the resource parameters to remove
+     */
+    public void removeResourceParams(String name) {
+
+        ResourceParams resourceParameters = null;
+        synchronized (resourceParams) {
+            resourceParameters = resourceParams.remove(name);
+        }
+        if (resourceParameters != null) {
+            support.firePropertyChange("resourceParams", resourceParameters,
+                                       null);
+            resourceParameters.setNamingResources(null);
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ResourceBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ResourceBase.java
new file mode 100644
index 0000000..1d6953b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ResourceBase.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+
+
+/**
+ * Representation of an Context element
+ *
+ * @author Peter Rossbach (pero@apache.org)
+ * @version $Revision$ $Date$
+ */
+
+public class ResourceBase implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The description of this Context Element.
+     */
+    private String description = null;
+
+    public String getDescription() {
+        return (this.description);
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+
+
+    /**
+     * The name of this context Element.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * The name of the EJB bean implementation class.
+     */
+    private String type = null;
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * Holder for our configured properties.
+     */
+    private HashMap<String, Object> properties =
+        new HashMap<String, Object>();
+
+    /**
+     * Return a configured property.
+     */
+    public Object getProperty(String name) {
+        return properties.get(name);
+    }
+
+    /**
+     * Set a configured property.
+     */
+    public void setProperty(String name, Object value) {
+        properties.put(name, value);
+    }
+
+    /** 
+     * remove a configured property.
+     */
+    public void removeProperty(String name) {
+        properties.remove(name);
+    }
+
+    /**
+     * List properties.
+     */
+    public Iterator<String> listProperties() {
+        return properties.keySet().iterator();
+    }
+    
+    
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * The NamingResources with which we are associated (if any).
+     */
+    protected NamingResources resources = null;
+
+    public NamingResources getNamingResources() {
+        return (this.resources);
+    }
+
+    void setNamingResources(NamingResources resources) {
+        this.resources = resources;
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ResourceParams.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ResourceParams.java
new file mode 100644
index 0000000..bc8c2eb
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ResourceParams.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+import java.util.Hashtable;
+
+/**
+ * Representation of additional parameters which will be used to initialize
+ * external resources defined in the web application deployment descriptor.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:42 $
+ */
+
+public class ResourceParams implements Serializable {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The name of this resource parameters. Must be the name of the resource
+     * in the java: namespace.
+     */
+    private String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    private Hashtable<String, String> resourceParams = new Hashtable<String, String>();
+
+    public void addParameter(String name, String value) {
+        resourceParams.put(name, value);
+    }
+
+    public Hashtable<String, String> getParameters() {
+        return resourceParams;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ResourceParams[");
+        sb.append("name=");
+        sb.append(name);
+        sb.append(", parameters=");
+        sb.append(resourceParams.toString());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * The NamingResources with which we are associated (if any).
+     */
+    protected NamingResources resources = null;
+
+    public NamingResources getNamingResources() {
+        return (this.resources);
+    }
+
+    void setNamingResources(NamingResources resources) {
+        this.resources = resources;
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/SecurityCollection.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/SecurityCollection.java
new file mode 100644
index 0000000..a7c48cc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/SecurityCollection.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+
+import org.apache.catalina.util.RequestUtil;
+
+import java.io.Serializable;
+
+
+/**
+ * Representation of a web resource collection for a web application's security
+ * constraint, as represented in a <code>&lt;web-resource-collection&gt;</code>
+ * element in the deployment descriptor.
+ * <p>
+ * <b>WARNING</b>:  It is assumed that instances of this class will be created
+ * and modified only within the context of a single thread, before the instance
+ * is made visible to the remainder of the application.  After that, only read
+ * access is expected.  Therefore, none of the read and write access within
+ * this class is synchronized.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:42 $
+ */
+
+public class SecurityCollection implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new security collection instance with default values.
+     */
+    public SecurityCollection() {
+
+        this(null, null);
+
+    }
+
+
+    /**
+     * Construct a new security collection instance with specified values.
+     *
+     * @param name Name of this security collection
+     */
+    public SecurityCollection(String name) {
+
+        this(name, null);
+
+    }
+
+
+    /**
+     * Construct a new security collection instance with specified values.
+     *
+     * @param name Name of this security collection
+     * @param description Description of this security collection
+     */
+    public SecurityCollection(String name, String description) {
+
+        super();
+        setName(name);
+        setDescription(description);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Description of this web resource collection.
+     */
+    private String description = null;
+
+
+    /**
+     * The HTTP methods covered by this web resource collection.
+     */
+    private String methods[] = new String[0];
+
+
+    /**
+     * The HTTP method omissions covered by this web resource collection.
+     */
+    private String methodOmissions[] = new String[0];
+
+
+    /**
+     * The name of this web resource collection.
+     */
+    private String name = null;
+
+
+    /**
+     * The URL patterns protected by this security collection.
+     */
+    private String patterns[] = new String[0];
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the description of this web resource collection.
+     */
+    public String getDescription() {
+
+        return (this.description);
+
+    }
+
+
+    /**
+     * Set the description of this web resource collection.
+     *
+     * @param description The new description
+     */
+    public void setDescription(String description) {
+
+        this.description = description;
+
+    }
+
+
+    /**
+     * Return the name of this web resource collection.
+     */
+    public String getName() {
+
+        return (this.name);
+
+    }
+
+
+    /**
+     * Set the name of this web resource collection
+     *
+     * @param name The new name
+     */
+    public void setName(String name) {
+
+        this.name = name;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an HTTP request method to be part of this web resource collection.
+     */
+    public void addMethod(String method) {
+
+        if (method == null)
+            return;
+        String results[] = new String[methods.length + 1];
+        for (int i = 0; i < methods.length; i++)
+            results[i] = methods[i];
+        results[methods.length] = method;
+        methods = results;
+
+    }
+
+
+    /**
+     * Add an HTTP request method omission to be part of this web resource collection.
+     */
+    public void addMethodOmission(String methodOmission) {
+
+        if (methodOmission == null)
+            return;
+        String results[] = new String[methodOmissions.length + 1];
+        for (int i = 0; i < methodOmissions.length; i++)
+            results[i] = methodOmissions[i];
+        results[methodOmissions.length] = methodOmission;
+        methodOmissions = results;
+
+    }
+
+
+    /**
+     * Add a URL pattern to be part of this web resource collection.
+     */
+    public void addPattern(String pattern) {
+
+        if (pattern == null)
+            return;
+        pattern = RequestUtil.urlDecode(pattern);
+        String results[] = new String[patterns.length + 1];
+        for (int i = 0; i < patterns.length; i++)
+            results[i] = patterns[i];
+        results[patterns.length] = pattern;
+        patterns = results;
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified HTTP request method is
+     * part of this web resource collection.
+     *
+     * @param method Request method to check
+     */
+    public boolean findMethod(String method) {
+
+        if (methods.length == 0)
+            return (true);
+        for (int i = 0; i < methods.length; i++) {
+            if (methods[i].equals(method))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the set of HTTP request methods that are part of this web
+     * resource collection, or a zero-length array if all request methods
+     * are included.
+     */
+    public String[] findMethods() {
+
+        return (methods);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified HTTP request method omission is
+     * part of this web resource collection.
+     *
+     * @param method Request method to check
+     */
+    public boolean findMethodOmission(String methodOmission) {
+
+        if (methodOmissions.length == 0)
+            return (false);
+        for (int i = 0; i < methodOmissions.length; i++) {
+            if (methodOmissions[i].equals(methodOmission))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the set of HTTP request method omissions that are part of this web
+     * resource collection.
+     */
+    public String[] findMethodOmissions() {
+
+        return (methodOmissions);
+
+    }
+
+
+    /**
+     * Is the specified pattern part of this web resource collection?
+     *
+     * @param pattern Pattern to be compared
+     */
+    public boolean findPattern(String pattern) {
+
+        for (int i = 0; i < patterns.length; i++) {
+            if (patterns[i].equals(pattern))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the set of URL patterns that are part of this web resource
+     * collection.  If none have been specified, a zero-length array is
+     * returned.
+     */
+    public String[] findPatterns() {
+
+        return (patterns);
+
+    }
+
+
+    /**
+     * Remove the specified HTTP request method from those that are part
+     * of this web resource collection.
+     *
+     * @param method Request method to be removed
+     */
+    public void removeMethod(String method) {
+
+        if (method == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < methods.length; i++) {
+            if (methods[i].equals(method)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            String results[] = new String[methods.length - 1];
+            for (int i = 0; i < methods.length; i++) {
+                if (i != n)
+                    results[j++] = methods[i];
+            }
+            methods = results;
+        }
+
+    }
+
+
+    /**
+     * Remove the specified HTTP request method omission from those that are part
+     * of this web resource collection.
+     *
+     * @param method Request method to be removed
+     */
+    public void removeMethodOmission(String methodOmission) {
+
+        if (methodOmission == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < methodOmissions.length; i++) {
+            if (methodOmissions[i].equals(methodOmission)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            String results[] = new String[methodOmissions.length - 1];
+            for (int i = 0; i < methodOmissions.length; i++) {
+                if (i != n)
+                    results[j++] = methodOmissions[i];
+            }
+            methodOmissions = results;
+        }
+
+    }
+
+
+    /**
+     * Remove the specified URL pattern from those that are part of this
+     * web resource collection.
+     *
+     * @param pattern Pattern to be removed
+     */
+    public void removePattern(String pattern) {
+
+        if (pattern == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < patterns.length; i++) {
+            if (patterns[i].equals(pattern)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            String results[] = new String[patterns.length - 1];
+            for (int i = 0; i < patterns.length; i++) {
+                if (i != n)
+                    results[j++] = patterns[i];
+            }
+            patterns = results;
+        }
+
+    }
+
+
+    /**
+     * Return a String representation of this security collection.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("SecurityCollection: ");
+        sb.append(" name: ").append(name);
+        sb.append(" description ").append(description);
+        for (String method : methods) {
+            sb.append(" method: ").append(method);
+        }
+        for (String methodOmission : methodOmissions) {
+            sb.append(" methodOmission ").append(methodOmission);
+        }
+        for (String pattern : patterns) {
+            sb.append(" pattern ").append(pattern);
+        }
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/SecurityConstraint.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/SecurityConstraint.java
new file mode 100644
index 0000000..42c334d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/SecurityConstraint.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+
+/**
+ * Representation of a security constraint element for a web application,
+ * as represented in a <code>&lt;security-constraint&gt;</code> element in the
+ * deployment descriptor.
+ * <p>
+ * <b>WARNING</b>:  It is assumed that instances of this class will be created
+ * and modified only within the context of a single thread, before the instance
+ * is made visible to the remainder of the application.  After that, only read
+ * access is expected.  Therefore, none of the read and write access within
+ * this class is synchronized.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:27:42 $
+ */
+
+public class SecurityConstraint implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new security constraint instance with default values.
+     */
+    public SecurityConstraint() {
+
+        super();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Was the "all roles" wildcard included in the authorization constraints
+     * for this security constraint?
+     */
+    private boolean allRoles = false;
+
+
+    /**
+     * Was an authorization constraint included in this security constraint?
+     * This is necessary to distinguish the case where an auth-constraint with
+     * no roles (signifying no direct access at all) was requested, versus
+     * a lack of auth-constraint which implies no access control checking.
+     */
+    private boolean authConstraint = false;
+
+
+    /**
+     * The set of roles permitted to access resources protected by this
+     * security constraint.
+     */
+    private String authRoles[] = new String[0];
+
+
+    /**
+     * The set of web resource collections protected by this security
+     * constraint.
+     */
+    private SecurityCollection collections[] = new SecurityCollection[0];
+
+
+    /**
+     * The display name of this security constraint.
+     */
+    private String displayName = null;
+
+
+    /**
+     * The user data constraint for this security constraint.  Must be NONE,
+     * INTEGRAL, or CONFIDENTIAL.
+     */
+    private String userConstraint = "NONE";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Was the "all roles" wildcard included in this authentication
+     * constraint?
+     */
+    public boolean getAllRoles() {
+
+        return (this.allRoles);
+
+    }
+
+
+    /**
+     * Return the authorization constraint present flag for this security
+     * constraint.
+     */
+    public boolean getAuthConstraint() {
+
+        return (this.authConstraint);
+
+    }
+
+
+    /**
+     * Set the authorization constraint present flag for this security
+     * constraint.
+     */
+    public void setAuthConstraint(boolean authConstraint) {
+
+        this.authConstraint = authConstraint;
+
+    }
+
+
+    /**
+     * Return the display name of this security constraint.
+     */
+    public String getDisplayName() {
+
+        return (this.displayName);
+
+    }
+
+
+    /**
+     * Set the display name of this security constraint.
+     */
+    public void setDisplayName(String displayName) {
+
+        this.displayName = displayName;
+
+    }
+
+
+    /**
+     * Return the user data constraint for this security constraint.
+     */
+    public String getUserConstraint() {
+
+        return (userConstraint);
+
+    }
+
+
+    /**
+     * Set the user data constraint for this security constraint.
+     *
+     * @param userConstraint The new user data constraint
+     */
+    public void setUserConstraint(String userConstraint) {
+
+        if (userConstraint != null)
+            this.userConstraint = userConstraint;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add an authorization role, which is a role name that will be
+     * permitted access to the resources protected by this security constraint.
+     *
+     * @param authRole Role name to be added
+     */
+    public void addAuthRole(String authRole) {
+
+        if (authRole == null)
+            return;
+        if ("*".equals(authRole)) {
+            allRoles = true;
+            return;
+        }
+        String results[] = new String[authRoles.length + 1];
+        for (int i = 0; i < authRoles.length; i++)
+            results[i] = authRoles[i];
+        results[authRoles.length] = authRole;
+        authRoles = results;
+        authConstraint = true;
+
+    }
+
+
+    /**
+     * Add a new web resource collection to those protected by this
+     * security constraint.
+     *
+     * @param collection The new web resource collection
+     */
+    public void addCollection(SecurityCollection collection) {
+
+        if (collection == null)
+            return;
+        SecurityCollection results[] =
+            new SecurityCollection[collections.length + 1];
+        for (int i = 0; i < collections.length; i++)
+            results[i] = collections[i];
+        results[collections.length] = collection;
+        collections = results;
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified role is permitted access to
+     * the resources protected by this security constraint.
+     *
+     * @param role Role name to be checked
+     */
+    public boolean findAuthRole(String role) {
+
+        if (role == null)
+            return (false);
+        for (int i = 0; i < authRoles.length; i++) {
+            if (role.equals(authRoles[i]))
+                return (true);
+        }
+        return (false);
+
+    }
+
+
+    /**
+     * Return the set of roles that are permitted access to the resources
+     * protected by this security constraint.  If none have been defined,
+     * a zero-length array is returned (which implies that all authenticated
+     * users are permitted access).
+     */
+    public String[] findAuthRoles() {
+
+        return (authRoles);
+
+    }
+
+
+    /**
+     * Return the web resource collection for the specified name, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Web resource collection name to return
+     */
+    public SecurityCollection findCollection(String name) {
+
+        if (name == null)
+            return (null);
+        for (int i = 0; i < collections.length; i++) {
+            if (name.equals(collections[i].getName()))
+                return (collections[i]);
+        }
+        return (null);
+
+    }
+
+
+    /**
+     * Return all of the web resource collections protected by this
+     * security constraint.  If there are none, a zero-length array is
+     * returned.
+     */
+    public SecurityCollection[] findCollections() {
+
+        return (collections);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the specified context-relative URI (and
+     * associated HTTP method) are protected by this security constraint.
+     *
+     * @param uri Context-relative URI to check
+     * @param method Request method being used
+     */
+    /* SJSWS 6324431
+    public boolean included(String uri, String method) {
+    */
+    // START SJSWS 6324431
+    public boolean included(String uri, String method, 
+                            boolean caseSensitiveMapping) {
+    // END SJSWS 6324431
+
+        // We cannot match without a valid request method
+        if (method == null)
+            return (false);
+
+        // Check all of the collections included in this constraint
+        for (int i = 0; i < collections.length; i++) {
+            if (!collections[i].findMethod(method))
+                continue;
+            String patterns[] = collections[i].findPatterns();
+            for (int j = 0; j < patterns.length; j++) {
+                /* SJSWS 6324431
+                if (matchPattern(uri, patterns[j]))
+                */
+                // START SJSWS 6324431
+                if (matchPattern(uri, patterns[j], 
+                                 caseSensitiveMapping))
+                // END SJSWS 6324431
+                    return (true);
+            }
+        }
+
+        // No collection included in this constraint matches this request
+        return (false);
+
+    }
+
+
+    /**
+     * Remove the specified role from the set of roles permitted to access
+     * the resources protected by this security constraint.
+     *
+     * @param authRole Role name to be removed
+     */
+    public void removeAuthRole(String authRole) {
+
+        if (authRole == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < authRoles.length; i++) {
+            if (authRoles[i].equals(authRole)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            String results[] = new String[authRoles.length - 1];
+            for (int i = 0; i < authRoles.length; i++) {
+                if (i != n)
+                    results[j++] = authRoles[i];
+            }
+            authRoles = results;
+        }
+
+    }
+
+
+    /**
+     * Remove the specified web resource collection from those protected by
+     * this security constraint.
+     *
+     * @param collection Web resource collection to be removed
+     */
+    public void removeCollection(SecurityCollection collection) {
+
+        if (collection == null)
+            return;
+        int n = -1;
+        for (int i = 0; i < collections.length; i++) {
+            if (collections[i].equals(collection)) {
+                n = i;
+                break;
+            }
+        }
+        if (n >= 0) {
+            int j = 0;
+            SecurityCollection results[] =
+                new SecurityCollection[collections.length - 1];
+            for (int i = 0; i < collections.length; i++) {
+                if (i != n)
+                    results[j++] = collections[i];
+            }
+            collections = results;
+        }
+
+    }
+
+
+    /**
+     * Return a String representation of this security constraint.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("SecurityConstraint: ");
+        for (SecurityCollection collection : collections) {
+            sb.append(" collection: ").append(collection);
+        }
+        for (String authRole : authRoles) {
+            sb.append(" authRole: "+authRole);
+        }
+        sb.append(" userConstraint: ").append(userConstraint);
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Does the specified request path match the specified URL pattern?
+     * This method follows the same rules (in the same order) as those used
+     * for mapping requests to servlets.
+     *
+     * @param path Context-relative request path to be checked
+     *  (must start with '/')
+     * @param pattern URL pattern to be compared against
+     */
+    /* SJSWS 6324431
+    private boolean matchPattern(String path, String pattern) {
+    */
+    // START SJSWS 6324431
+    private boolean matchPattern(String path, String pattern,
+                                 boolean caseSensitiveMapping) {
+    // END SJSWS 6324431        
+
+        // Normalize the argument strings
+        if ((path == null) || (path.length() == 0))
+            path = "/";
+        if ((pattern == null) || (pattern.length() == 0))
+            pattern = "/";
+
+        // START SJSWS 6324431
+        if (!caseSensitiveMapping) {
+            path = path.toLowerCase(Locale.ENGLISH);
+            pattern = pattern.toLowerCase(Locale.ENGLISH);
+        }
+        // END SJSWS 6324431
+
+        // Check for exact match
+        if (path.equals(pattern))
+            return (true);
+
+        // Check for path prefix matching
+        if (pattern.startsWith("/") && pattern.endsWith("/*")) {
+            pattern = pattern.substring(0, pattern.length() - 2);
+            if (pattern.length() == 0)
+                return (true);  // "/*" is the same as "/"
+            if (path.endsWith("/"))
+                path = path.substring(0, path.length() - 1);
+            while (true) {
+                if (pattern.equals(path))
+                    return (true);
+                int slash = path.lastIndexOf('/');
+                if (slash <= 0)
+                    break;
+                path = path.substring(0, slash);
+            }
+            return (false);
+        }
+
+        // Check for suffix matching
+        if (pattern.startsWith("*.")) {
+            int slash = path.lastIndexOf('/');
+            int period = path.lastIndexOf('.');
+            if ((slash >= 0) && (period > slash) &&
+                path.endsWith(pattern.substring(1))) {
+                return (true);
+            }
+            return (false);
+        }
+
+        // Check for universal mapping
+        if (pattern.equals("/"))
+            return (true);
+
+        return (false);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ServletMap.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ServletMap.java
new file mode 100644
index 0000000..6eeeaf4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/ServletMap.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+/**
+ * Class representing a servlet mapping containing multiple URL patterns.
+ * See Servlet 2.5, SRV.18.0.3 ("Multiple Occurrences of Servlet Mappings")
+ * for details.
+ */
+public class ServletMap {
+    String servletName;
+    String[] urlPatterns = new String[0];
+    
+    public void setServletName(String name) {
+        servletName = name;
+    }
+
+    public void addURLPattern(String pattern) {
+        String[] results = new String[urlPatterns.length + 1];
+        System.arraycopy(urlPatterns, 0, results, 0, urlPatterns.length);
+        results[urlPatterns.length] = pattern;
+        urlPatterns = results;
+    }
+
+    public String getServletName() {
+        return servletName;
+    }
+        
+    public String[] getURLPatterns() {
+        return urlPatterns;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/Test.java b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/Test.java
new file mode 100644
index 0000000..1b62dd1
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/Test.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.deploy;
+
+public final class Test {
+
+    public static void main(String args[]) {
+
+        String list[] = null;
+
+        System.out.println("Creating new collection");
+        SecurityCollection collection = new SecurityCollection();
+
+        System.out.println("Adding GET and POST methods");
+        collection.addMethod("GET");
+        collection.addMethod("POST");
+
+        System.out.println("Currently defined methods:");
+        list = collection.findMethods();
+        for (int i = 0; i < list.length; i++)
+            System.out.println(" " + list[i]);
+        System.out.println("Is DELETE included? " +
+                           collection.findMethod("DELETE"));
+        System.out.println("Is POST included? " +
+                           collection.findMethod("POST"));
+
+        System.out.println("Removing POST method");
+        collection.removeMethod("POST");
+
+        System.out.println("Currently defined methods:");
+        list = collection.findMethods();
+        for (int i = 0; i < list.length; i++)
+            System.out.println(" " + list[i]);
+        System.out.println("Is DELETE included? " +
+                           collection.findMethod("DELETE"));
+        System.out.println("Is POST included? " +
+                           collection.findMethod("POST"));
+
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/package.html b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/package.html
new file mode 100644
index 0000000..5d2e60d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/deploy/package.html
@@ -0,0 +1,29 @@
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<body>
+
+<p>This package contains Java objects that represent complex data structures
+from the web application deployment descriptor file (<code>web.xml</code>).
+It is assumed that these objects will be initialized within the context of
+a single thread, and then referenced in a read-only manner subsequent to that
+time.  Therefore, no multi-thread synchronization is utilized within the
+implementation classes.</p>
+
+</body>
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ByteArrayOutputStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ByteArrayOutputStream.java
new file mode 100644
index 0000000..d32b4ec
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ByteArrayOutputStream.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+ 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.catalina.util.RequestUtil;
+
+/**
+ * This class implements an output stream in which the data is 
+ * written into a byte array. The buffer automatically grows as data 
+ * is written to it.
+ * <p> 
+ * The data can be retrieved using <code>toByteArray()</code> and
+ * <code>toString()</code>.
+ * <p>
+ * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
+ * this class can be called after the stream has been closed without
+ * generating an <tt>IOException</tt>.
+ * <p>
+ * This is an alternative implementation of the java.io.ByteArrayOutputStream
+ * class. The original implementation only allocates 32 bytes at the beginning.
+ * As this class is designed for heavy duty it starts at 1024 bytes. In contrast
+ * to the original it doesn't reallocate the whole memory block but allocates
+ * additional buffers. This way no buffers need to be garbage collected and
+ * the contents don't have to be copied to the new buffer. This class is
+ * designed to behave exactly like the original. The only exception is the
+ * deprecated toString(int) method that has been ignored.
+ * 
+ * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
+ * @author Holger Hoffstatte
+ * @version $Id: ByteArrayOutputStream.java 610010 2008-01-08 14:50:59Z niallp $
+ */
+public class ByteArrayOutputStream extends OutputStream {
+
+    /** A singleton empty byte array. */
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    /** The list of buffers, which grows and never reduces. */
+    private List<byte[]> buffers = new ArrayList<byte[]>();
+    /** The index of the current buffer. */
+    private int currentBufferIndex;
+    /** The total count of bytes in all the filled buffers. */
+    private int filledBufferSum;
+    /** The current buffer. */
+    private byte[] currentBuffer;
+    /** The total count of bytes written. */
+    private int count;
+
+    /**
+     * Creates a new byte array output stream. The buffer capacity is 
+     * initially 1024 bytes, though its size increases if necessary. 
+     */
+    public ByteArrayOutputStream() {
+        this(1024);
+    }
+
+    /**
+     * Creates a new byte array output stream, with a buffer capacity of 
+     * the specified size, in bytes. 
+     *
+     * @param size  the initial size
+     * @throws IllegalArgumentException if size is negative
+     */
+    public ByteArrayOutputStream(int size) {
+        if (size < 0) {
+            throw new IllegalArgumentException(
+                "Negative initial size: " + size);
+        }
+        needNewBuffer(size);
+    }
+
+    /**
+     * Return the appropriate <code>byte[]</code> buffer 
+     * specified by index.
+     *
+     * @param index  the index of the buffer required
+     * @return the buffer
+     */
+    private byte[] getBuffer(int index) {
+        return buffers.get(index);
+    }
+
+    /**
+     * Makes a new buffer available either by allocating
+     * a new one or re-cycling an existing one.
+     *
+     * @param newcount  the size of the buffer if one is created
+     */
+    private void needNewBuffer(int newcount) {
+        if (currentBufferIndex < buffers.size() - 1) {
+            //Recycling old buffer
+            filledBufferSum += currentBuffer.length;
+            
+            currentBufferIndex++;
+            currentBuffer = getBuffer(currentBufferIndex);
+        } else {
+            //Creating new buffer
+            int newBufferSize;
+            if (currentBuffer == null) {
+                newBufferSize = newcount;
+                filledBufferSum = 0;
+            } else {
+                newBufferSize = Math.max(
+                    currentBuffer.length << 1, 
+                    newcount - filledBufferSum);
+                filledBufferSum += currentBuffer.length;
+            }
+            
+            currentBufferIndex++;
+            currentBuffer = new byte[newBufferSize];
+            buffers.add(currentBuffer);
+        }
+    }
+
+    /**
+     * Write the bytes to byte array.
+     * @param b the bytes to write
+     * @param off The start offset
+     * @param len The number of bytes to write
+     */
+    public void write(byte[] b, int off, int len) {
+        if ((off < 0) 
+                || (off > b.length) 
+                || (len < 0) 
+                || ((off + len) > b.length) 
+                || ((off + len) < 0)) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return;
+        }
+        synchronized (this) {
+            int newcount = count + len;
+            int remaining = len;
+            int inBufferPos = count - filledBufferSum;
+            while (remaining > 0) {
+                int part = Math.min(remaining, currentBuffer.length - inBufferPos);
+                System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
+                remaining -= part;
+                if (remaining > 0) {
+                    needNewBuffer(newcount);
+                    inBufferPos = 0;
+                }
+            }
+            count = newcount;
+        }
+    }
+
+    /**
+     * Write a byte to byte array.
+     * @param b the byte to write
+     */
+    public synchronized void write(int b) {
+        int inBufferPos = count - filledBufferSum;
+        if (inBufferPos == currentBuffer.length) {
+            needNewBuffer(count + 1);
+            inBufferPos = 0;
+        }
+        currentBuffer[inBufferPos] = (byte) b;
+        count++;
+    }
+
+    /**
+     * Writes the entire contents of the specified input stream to this
+     * byte stream. Bytes from the input stream are read directly into the
+     * internal buffers of this streams.
+     *
+     * @param in the input stream to read from
+     * @return total number of bytes read from the input stream
+     *         (and written to this stream)
+     * @throws IOException if an I/O error occurs while reading the input stream
+     * @since Commons IO 1.4
+     */
+    public synchronized int write(InputStream in) throws IOException {
+        int readCount = 0;
+        int inBufferPos = count - filledBufferSum;
+        int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
+        while (n != -1) {
+            readCount += n;
+            inBufferPos += n;
+            count += n;
+            if (inBufferPos == currentBuffer.length) {
+                needNewBuffer(currentBuffer.length);
+                inBufferPos = 0;
+            }
+            n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
+        }
+        return readCount;
+    }
+
+    /**
+     * Return the current size of the byte array.
+     * @return the current size of the byte array
+     */
+    public synchronized int size() {
+        return count;
+    }
+
+    /**
+     * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
+     * this class can be called after the stream has been closed without
+     * generating an <tt>IOException</tt>.
+     *
+     * @throws IOException never (this method should not declare this exception
+     * but it has to now due to backwards compatability)
+     */
+    public void close() throws IOException {
+        //nop
+    }
+
+    /**
+     * @see java.io.ByteArrayOutputStream#reset()
+     */
+    public synchronized void reset() {
+        count = 0;
+        filledBufferSum = 0;
+        currentBufferIndex = 0;
+        currentBuffer = getBuffer(currentBufferIndex);
+    }
+
+    /**
+     * Writes the entire contents of this byte stream to the
+     * specified output stream.
+     *
+     * @param out  the output stream to write to
+     * @throws IOException if an I/O error occurs, such as if the stream is closed
+     * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
+     */
+    public synchronized void writeTo(OutputStream out) throws IOException {
+        int remaining = count;
+        for (int i = 0; i < buffers.size(); i++) {
+            byte[] buf = getBuffer(i);
+            int c = Math.min(buf.length, remaining);
+            out.write(buf, 0, c);
+            remaining -= c;
+            if (remaining == 0) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Gets the curent contents of this byte stream as a byte array.
+     * The result is independent of this stream.
+     *
+     * @return the current contents of this output stream, as a byte array
+     * @see java.io.ByteArrayOutputStream#toByteArray()
+     */
+    public synchronized byte[] toByteArray() {
+        int remaining = count;
+        if (remaining == 0) {
+            return EMPTY_BYTE_ARRAY; 
+        }
+        byte newbuf[] = new byte[remaining];
+        int pos = 0;
+        for (int i = 0; i < buffers.size(); i++) {
+            byte[] buf = getBuffer(i);
+            int c = Math.min(buf.length, remaining);
+            System.arraycopy(buf, 0, newbuf, pos, c);
+            pos += c;
+            remaining -= c;
+            if (remaining == 0) {
+                break;
+            }
+        }
+        return newbuf;
+    }
+
+    /**
+     * Gets the curent contents of this byte stream as a string.
+     * @return the contents of the byte array as a String
+     * @see java.io.ByteArrayOutputStream#toString()
+     */
+    public String toString() {
+        return new String(toByteArray(), Charset.defaultCharset());
+    }
+
+    /**
+     * Gets the curent contents of this byte stream as a string
+     * using the specified encoding.
+     *
+     * @param enc  the name of the character encoding
+     * @return the string converted from the byte array
+     * @throws UnsupportedEncodingException if the encoding is not supported
+     * @see java.io.ByteArrayOutputStream#toString(String)
+     */
+    public String toString(String enc) throws UnsupportedEncodingException {
+        return new String(toByteArray(), RequestUtil.lookupCharset(enc));
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/DeferredFileOutputStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/DeferredFileOutputStream.java
new file mode 100644
index 0000000..fa674dc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/DeferredFileOutputStream.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.io.*;
+
+/**
+ * An output stream which will retain data in memory until a specified
+ * threshold is reached, and only then commit it to disk. If the stream is
+ * closed before the threshold is reached, the data will not be written to
+ * disk at all.
+ * <p>
+ * This class originated in FileUpload processing. In this use case, you do
+ * not know in advance the size of the file being uploaded. If the file is small
+ * you want to store it in memory (for speed), but if the file is large you want
+ * to store it to file (to avoid memory issues).
+ *
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @author gaxzerow
+ *
+ * @version $Id: DeferredFileOutputStream.java 606381 2007-12-22 02:03:16Z ggregory $
+ */
+class DeferredFileOutputStream
+    extends ThresholdingOutputStream
+{
+
+    // ----------------------------------------------------------- Data members
+
+
+    /**
+     * The output stream to which data will be written prior to the theshold
+     * being reached.
+     */
+    private ByteArrayOutputStream memoryOutputStream;
+
+
+    /**
+     * The output stream to which data will be written at any given time. This
+     * will always be one of <code>memoryOutputStream</code> or
+     * <code>diskOutputStream</code>.
+     */
+    private OutputStream currentOutputStream;
+
+
+    /**
+     * The file to which output will be directed if the threshold is exceeded.
+     */
+    private File outputFile;
+
+    /**
+     * The temporary file prefix.
+     */
+    private String prefix;
+
+    /**
+     * The temporary file suffix.
+     */
+    private String suffix;
+
+    /**
+     * The directory to use for temporary files.
+     */
+    private File directory;
+
+    
+    /**
+     * True when close() has been called successfully.
+     */
+    private boolean closed = false;
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs an instance of this class which will trigger an event at the
+     * specified threshold, and save data to a file beyond that point.
+     *
+     * @param threshold  The number of bytes at which to trigger an event.
+     * @param outputFile The file to which data is saved beyond the threshold.
+     */
+    public DeferredFileOutputStream(int threshold, File outputFile)
+    {
+        super(threshold);
+        this.outputFile = outputFile;
+
+        memoryOutputStream = new ByteArrayOutputStream();
+        currentOutputStream = memoryOutputStream;
+    }
+
+
+    /**
+     * Constructs an instance of this class which will trigger an event at the
+     * specified threshold, and save data to a temporary file beyond that point.
+     *
+     * @param threshold  The number of bytes at which to trigger an event.
+     * @param prefix Prefix to use for the temporary file.
+     * @param suffix Suffix to use for the temporary file.
+     * @param directory Temporary file directory.
+     *
+     * @since Commons IO 1.4
+     */
+    public DeferredFileOutputStream(int threshold, String prefix, String suffix, File directory)
+    {
+        this(threshold, (File)null);
+        if (prefix == null) {
+            throw new IllegalArgumentException("Temporary file prefix is missing");
+        }
+        this.prefix = prefix;
+        this.suffix = suffix;
+        this.directory = directory;
+    }
+
+
+    // --------------------------------------- ThresholdingOutputStream methods
+
+
+    /**
+     * Returns the current output stream. This may be memory based or disk
+     * based, depending on the current state with respect to the threshold.
+     *
+     * @return The underlying output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected OutputStream getStream() throws IOException
+    {
+        return currentOutputStream;
+    }
+
+
+    /**
+     * Switches the underlying output stream from a memory based stream to one
+     * that is backed by disk. This is the point at which we realise that too
+     * much data is being written to keep in memory, so we elect to switch to
+     * disk-based storage.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected void thresholdReached() throws IOException
+    {
+        if (prefix != null) {
+            outputFile = File.createTempFile(prefix, suffix, directory);
+        }
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(outputFile);
+            memoryOutputStream.writeTo(fos);
+            currentOutputStream = fos;
+            memoryOutputStream = null;
+        } catch (IOException e) {
+            if (fos != null) {
+                fos.close();
+            }
+            throw e;
+        }
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Determines whether or not the data for this output stream has been
+     * retained in memory.
+     *
+     * @return <code>true</code> if the data is available in memory;
+     *         <code>false</code> otherwise.
+     */
+    public boolean isInMemory()
+    {
+        return (!isThresholdExceeded());
+    }
+
+
+    /**
+     * Returns the data for this output stream as an array of bytes, assuming
+     * that the data has been retained in memory. If the data was written to
+     * disk, this method returns <code>null</code>.
+     *
+     * @return The data for this output stream, or <code>null</code> if no such
+     *         data is available.
+     */
+    public byte[] getData()
+    {
+        if (memoryOutputStream != null)
+        {
+            return memoryOutputStream.toByteArray();
+        }
+        return null;
+    }
+
+
+    /**
+     * Returns either the output file specified in the constructor or
+     * the temporary file created or null.
+     * <p>
+     * If the constructor specifying the file is used then it returns that
+     * same output file, even when threashold has not been reached.
+     * <p>
+     * If constructor specifying a temporary file prefix/suffix is used
+     * then the temporary file created once the threashold is reached is returned
+     * If the threshold was not reached then <code>null</code> is returned.
+     *
+     * @return The file for this output stream, or <code>null</code> if no such
+     *         file exists.
+     */
+    public File getFile()
+    {
+        return outputFile;
+    }
+    
+        
+    /**
+     * Closes underlying output stream, and mark this as closed
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void close() throws IOException
+    {
+        super.close();
+        closed = true;
+    }
+    
+    
+    /**
+     * Writes the data from this output stream to the specified output stream,
+     * after it has been closed.
+     *
+     * @param out output stream to write to.
+     * @exception IOException if this stream is not yet closed or an error occurs.
+     */
+    public void writeTo(OutputStream out) throws IOException 
+    {
+        // we may only need to check if this is closed if we are working with a file
+        // but we should force the habit of closing wether we are working with
+        // a file or memory.
+        if (!closed)
+        {
+            throw new IOException("Stream not closed");
+        }
+        
+        if(isInMemory())
+        {
+            memoryOutputStream.writeTo(out);
+        }
+        else
+        {
+            FileInputStream fis = new FileInputStream(outputFile);
+            Streams.copy(fis, out, false);
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/LimitedInputStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/LimitedInputStream.java
new file mode 100644
index 0000000..cff0743
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/LimitedInputStream.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * An input stream, which limits its data size. This stream is
+ * used, if the content length is unknown.
+ */
+abstract class LimitedInputStream
+        extends FilterInputStream {
+    /**
+     * The maximum size of an item, in bytes.
+     */
+    private long sizeMax;
+    /**
+     * The current number of bytes.
+     */
+    private long count;
+    /**
+     * Whether this stream is already closed.
+     */
+    private boolean closed;
+
+    /**
+     * Creates a new instance.
+     * @param pIn The input stream, which shall be limited.
+     * @param pSizeMax The limit; no more than this number of bytes
+     *   shall be returned by the source stream.
+     */
+    public LimitedInputStream(InputStream pIn, long pSizeMax) {
+        super(pIn);
+        sizeMax = pSizeMax;
+    }
+
+    /**
+     * Called to indicate, that the input streams limit has
+     * been exceeded.
+     * @param pSizeMax The input streams limit, in bytes.
+     * @param pCount The actual number of bytes.
+     * @throws SizeException The called method is expected
+     *   to raise an SizeException.
+     */
+    protected abstract void raiseError(long pSizeMax, long pCount)
+            throws SizeException;
+
+    /** Called to check, whether the input streams
+     * limit is reached.
+     * @throws SizeException The given limit is exceeded.
+     */
+    private void checkLimit() throws SizeException {
+        if (count > sizeMax) {
+            raiseError(sizeMax, count);
+        }
+    }
+
+    /**
+     * Reads the next byte of data from this input stream. The value
+     * byte is returned as an <code>int</code> in the range
+     * <code>0</code> to <code>255</code>. If no byte is available
+     * because the end of the stream has been reached, the value
+     * <code>-1</code> is returned. This method blocks until input data
+     * is available, the end of the stream is detected, or an exception
+     * is thrown.
+     * <p>
+     * This method
+     * simply performs <code>in.read()</code> and returns the result.
+     *
+     * @return     the next byte of data, or <code>-1</code> if the end of the
+     *             stream is reached.
+     * @exception  IOException  if an I/O error occurs.
+     * @see        java.io.FilterInputStream#in
+     */
+    public int read() throws IOException {
+        int res = super.read();
+        if (res != -1) {
+            count++;
+            checkLimit();
+        }
+        return res;
+    }
+
+    /**
+     * Reads up to <code>len</code> bytes of data from this input stream
+     * into an array of bytes. If <code>len</code> is not zero, the method
+     * blocks until some input is available; otherwise, no
+     * bytes are read and <code>0</code> is returned.
+     * <p>
+     * This method simply performs <code>in.read(b, off, len)</code>
+     * and returns the result.
+     *
+     * @param      b     the buffer into which the data is read.
+     * @param      off   The start offset in the destination array
+     *                   <code>b</code>.
+     * @param      len   the maximum number of bytes read.
+     * @return     the total number of bytes read into the buffer, or
+     *             <code>-1</code> if there is no more data because the end of
+     *             the stream has been reached.
+     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
+     * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
+     * <code>len</code> is negative, or <code>len</code> is greater than
+     * <code>b.length - off</code>
+     * @exception  IOException  if an I/O error occurs.
+     * @see        java.io.FilterInputStream#in
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        int res = super.read(b, off, len);
+        if (res > 0) {
+            count += res;
+            checkLimit();
+        }
+        return res;
+    }
+
+    /**
+     * Returns, whether this stream is already closed.
+     * @return True, if the stream is closed, otherwise false.
+     * @throws IOException An I/O error occurred.
+     */
+    public boolean isClosed() throws IOException {
+        return closed;
+    }
+
+    /**
+     * Closes this input stream and releases any system resources
+     * associated with the stream.
+     * This
+     * method simply performs <code>in.close()</code>.
+     *
+     * @exception  IOException  if an I/O error occurs.
+     * @see        java.io.FilterInputStream#in
+     */
+    public void close() throws IOException {
+        closed = true;
+        super.close();
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/Multipart.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/Multipart.java
new file mode 100644
index 0000000..757d098
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/Multipart.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * This class is the base for implementing servlet 3.0 file upload
+ *
+ * @author Kin-man Chung
+ */
+
+package org.apache.catalina.fileupload;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.Part;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.catalina.connector.Request;
+
+public class Multipart {
+
+    private final String location;
+    private final long maxFileSize;
+    private final long maxRequestSize;
+    private final int fileSizeThreshold;
+    private File repository;
+    private ProgressListener listener;
+
+    private final Request request;
+    private ArrayList<Part> parts;
+    private List<Part> unmodifiableParts;
+
+    public Multipart(Request request,
+                String location, long maxFileSize, long maxRequestSize,
+                int fileSizeThreshold) {
+        this.request = request;
+        this.location = location;
+        this.maxFileSize = maxFileSize;
+        this.maxRequestSize = maxRequestSize;
+        this.fileSizeThreshold = fileSizeThreshold;
+        repository = (File) request.getServletContext().getAttribute(
+            ServletContext.TEMPDIR);
+        if (location != null && location.length() != 0) {
+            File tempFile= new File(location);
+            if (tempFile.isAbsolute()) {
+                repository = tempFile;
+            } else {
+                repository = new File(repository, location);
+            }
+        }
+    }
+
+    public void init() {
+        try {
+            initParts();
+        } catch (Exception ex) {
+            throw new RuntimeException("Error in multipart initialization", ex);
+        }
+    }
+
+    public String getLocation() {
+        return location;
+    }
+
+    public int getFileSizeThreshold() {
+        return fileSizeThreshold;
+    }
+
+    public long getMaxFileSize() {
+        return maxFileSize;
+    }
+
+    public long getMaxRequestSize() {
+        return maxRequestSize;
+    }
+
+    public File getRepository() {
+        return repository;
+    }
+
+    private boolean isMultipart() {
+
+        if (!request.getMethod().toLowerCase(Locale.ENGLISH).equals("post")) {
+            return false;
+        }
+        String contentType = request.getContentType();
+        if (contentType == null) {
+            return false;
+        }
+        if (contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/form-data")) {
+            return true;
+        }
+        return false;
+    }
+
+    private void initParts() throws IOException, ServletException {
+        if (parts != null) {
+            return;
+        }
+        parts = new ArrayList<Part>();
+        try {
+            RequestItemIterator iter = new RequestItemIterator(this, request);
+            while (iter.hasNext()) {
+                RequestItem requestItem = iter.next();
+                PartItem partItem = new PartItem(this,
+                                         requestItem.getHeaders(),
+                                         requestItem.getFieldName(),
+                                         requestItem.getContentType(),
+                                         requestItem.isFormField(),
+                                         requestItem.getSubmittedFileName(),
+                                         request.getCharacterEncoding());
+                Streams.copy(requestItem.openStream(),
+                             partItem.getOutputStream(), true);
+                String fileName = partItem.getSubmittedFileName();
+                if (fileName == null || fileName.length() == 0) {
+                    // Add part name and value as a parameter
+                    request.addParameter(partItem.getName(),
+                                         new String[] {partItem.getString()});
+                }
+                parts.add((Part)partItem);
+            }
+        } catch (SizeException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    public synchronized Collection<Part> getParts()
+            throws IOException, ServletException {
+        if (! isMultipart()) {
+            throw new ServletException("The request content-type is not a multipart/form-data");
+        }
+
+        initParts();
+
+        if (null == unmodifiableParts) {
+            unmodifiableParts = Collections.unmodifiableList(parts);
+        }
+
+        return unmodifiableParts;
+    }
+
+    public Part getPart(String name) throws IOException, ServletException {
+
+        if (! isMultipart()) {
+            throw new ServletException("The request content-type is not a multipart/form-data");
+        }
+
+        initParts();
+        for (Part part: parts) {
+            String fieldName = part.getName();
+            if (name.equals(fieldName)) {
+                return part;
+            }
+        } 
+        return null;
+    }
+
+    /**
+     * Returns the progress listener.
+     * @return The progress listener, if any, or null.
+     */
+    public ProgressListener getProgressListener() {
+        return listener;
+    }
+
+    /**
+     * Sets the progress listener.
+     * @param pListener The progress listener, if any. Defaults to null.
+     */
+    public void setProgressListener(ProgressListener pListener) {
+        listener = pListener;
+    }
+
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/MultipartStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/MultipartStream.java
new file mode 100644
index 0000000..616de57
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/MultipartStream.java
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.io.ByteArrayOutputStream;
+import java.io.*;
+
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.catalina.LogFacade;
+
+/**
+ * <p> Low level API for processing file uploads.
+ *
+ * <p> This class can be used to process data streams conforming to MIME
+ * 'multipart' format as defined in
+ * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily
+ * large amounts of data in the stream can be processed under constant
+ * memory usage.
+ *
+ * <p> The format of the stream is defined in the following way:<br>
+ *
+ * <code>
+ *   multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
+ *   encapsulation := delimiter body CRLF<br>
+ *   delimiter := "--" boundary CRLF<br>
+ *   close-delimiter := "--" boudary "--"<br>
+ *   preamble := &lt;ignore&gt;<br>
+ *   epilogue := &lt;ignore&gt;<br>
+ *   body := header-part CRLF body-part<br>
+ *   header-part := 1*header CRLF<br>
+ *   header := header-name ":" header-value<br>
+ *   header-name := &lt;printable ascii characters except ":"&gt;<br>
+ *   header-value := &lt;any ascii characters except CR & LF&gt;<br>
+ *   body-data := &lt;arbitrary data&gt;<br>
+ * </code>
+ *
+ * <p>Note that body-data can contain another mulipart entity.  There
+ * is limited support for single pass processing of such nested
+ * streams.  The nested stream is <strong>required</strong> to have a
+ * boundary token of the same length as the parent stream (see {@link
+ * #setBoundary(byte[])}).
+ *
+ * <p>Here is an example of usage of this class.<br>
+ *
+ * <pre>
+ *    try {
+ *        MultipartStream multipartStream = new MultipartStream(input,
+ *                                                              boundary);
+ *        boolean nextPart = multipartStream.skipPreamble();
+ *        OutputStream output;
+ *        while(nextPart) {
+ *            header = chunks.readHeader();
+ *            // process headers
+ *            // create some output stream
+ *            multipartStream.readBodyPart(output);
+ *            nextPart = multipartStream.readBoundary();
+ *        }
+ *    } catch(MultipartStream.MalformedStreamException e) {
+ *          // the stream failed to follow required syntax
+ *    } catch(IOException) {
+ *          // a read or write error occurred
+ *    }
+ *
+ * </pre>
+ *
+ * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @author Sean C. Sullivan
+ *
+ * @version $Id: MultipartStream.java 607869 2008-01-01 16:42:17Z jochen $
+ */
+public class MultipartStream {
+
+    private static final Logger log = LogFacade.getLogger();
+
+    /**
+     * Internal class, which is used to invoke the
+     * {@link ProgressListener}.
+     */
+    static class ProgressNotifier {
+        /** The listener to invoke.
+         */
+        private final ProgressListener listener;
+        /** Number of expected bytes, if known, or -1.
+         */
+        private final long contentLength;
+        /** Number of bytes, which have been read so far.
+         */
+        private long bytesRead;
+        /** Number of items, which have been read so far.
+         */
+        private int items;
+        /** Creates a new instance with the given listener
+         * and content length.
+         * @param pListener The listener to invoke.
+         * @param pContentLength The expected content length.
+         */
+        ProgressNotifier(ProgressListener pListener, long pContentLength) {
+            listener = pListener;
+            contentLength = pContentLength;
+        }
+        /** Called to indicate that bytes have been read.
+         * @param pBytes Number of bytes, which have been read.
+         */
+        void noteBytesRead(int pBytes) {
+            /* Indicates, that the given number of bytes have been read from
+             * the input stream.
+             */
+            bytesRead += pBytes;
+            notifyListener();
+        }
+        /** Called to indicate, that a new file item has been detected.
+         */
+        void noteItem() {
+            ++items;
+        }
+        /** Called for notifying the listener.
+         */
+        private void notifyListener() {
+            if (listener != null) {
+                listener.update(bytesRead, contentLength, items);
+            }
+        }
+    }
+
+    // ----------------------------------------------------- Manifest constants
+
+
+    /**
+     * The Carriage Return ASCII character value.
+     */
+    public static final byte CR = 0x0D;
+
+
+    /**
+     * The Line Feed ASCII character value.
+     */
+    public static final byte LF = 0x0A;
+
+
+    /**
+     * The dash (-) ASCII character value.
+     */
+    public static final byte DASH = 0x2D;
+
+
+    /**
+     * The maximum length of <code>header-part</code> that will be
+     * processed (10 kilobytes = 10240 bytes.).
+     */
+    public static final int HEADER_PART_SIZE_MAX = 10240;
+
+
+    /**
+     * The default length of the buffer used for processing a request.
+     */
+    private static final int DEFAULT_BUFSIZE = 4096;
+
+
+    /**
+     * A byte sequence that marks the end of <code>header-part</code>
+     * (<code>CRLFCRLF</code>).
+     */
+    private static final byte[] HEADER_SEPARATOR = {
+        CR, LF, CR, LF };
+
+
+    /**
+     * A byte sequence that that follows a delimiter that will be
+     * followed by an encapsulation (<code>CRLF</code>).
+     */
+    private static final byte[] FIELD_SEPARATOR = {
+        CR, LF};
+
+
+    /**
+     * A byte sequence that that follows a delimiter of the last
+     * encapsulation in the stream (<code>--</code>).
+     */
+    private static final byte[] STREAM_TERMINATOR = {
+        DASH, DASH};
+
+
+    /**
+     * A byte sequence that precedes a boundary (<code>CRLF--</code>).
+     */
+    private static final byte[] BOUNDARY_PREFIX = {
+        CR, LF, DASH, DASH};
+
+
+
+    // ----------------------------------------------------------- Data members
+
+
+    /**
+     * The input stream from which data is read.
+     */
+    private final InputStream input;
+
+
+    /**
+     * The length of the boundary token plus the leading <code>CRLF--</code>.
+     */
+    private int boundaryLength;
+
+
+    /**
+     * The amount of data, in bytes, that must be kept in the buffer in order
+     * to detect delimiters reliably.
+     */
+    private int keepRegion;
+
+
+    /**
+     * The byte sequence that partitions the stream.
+     */
+    private byte[] boundary;
+
+
+    /**
+     * The length of the buffer used for processing the request.
+     */
+    private final int bufSize;
+
+
+    /**
+     * The buffer used for processing the request.
+     */
+    private final byte[] buffer;
+
+
+    /**
+     * The index of first valid character in the buffer.
+     * <br>
+     * 0 <= head < bufSize
+     */
+    private int head;
+
+
+    /**
+     * The index of last valid characer in the buffer + 1.
+     * <br>
+     * 0 <= tail <= bufSize
+     */
+    private int tail;
+
+
+    /**
+     * The content encoding to use when reading headers.
+     */
+    private String headerEncoding;
+
+
+    /**
+     * The progress notifier, if any, or null.
+     */
+    private final ProgressNotifier notifier;
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * <p> Constructs a <code>MultipartStream</code> with a custom size buffer.
+     *
+     * <p> Note that the buffer must be at least big enough to contain the
+     * boundary string, plus 4 characters for CR/LF and double dash, plus at
+     * least one byte of data.  Too small a buffer size setting will degrade
+     * performance.
+     *
+     * @param input    The <code>InputStream</code> to serve as a data source.
+     * @param boundary The token used for dividing the stream into
+     *                 <code>encapsulations</code>.
+     * @param bufSize  The size of the buffer to be used, in bytes.
+     * @param pNotifier The notifier, which is used for calling the
+     *                  progress listener, if any.
+     *
+     * @see #MultipartStream(InputStream, byte[],
+     *     MultipartStream.ProgressNotifier)
+     */
+    MultipartStream(InputStream input,
+            byte[] boundary,
+            int bufSize,
+            ProgressNotifier pNotifier) {
+        if (boundary == null) {
+            throw new IllegalArgumentException("boundary is null");
+        }
+
+        // We prepend CR/LF to the boundary to chop trailng CR/LF from
+        // body-data tokens.
+        this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length;
+        if (bufSize < this.boundaryLength + 1) {
+            throw new IllegalArgumentException(
+                    "The buffer size specified for the MultipartStream is too small");
+        }
+
+        this.input = input;
+        this.bufSize = Math.max(bufSize, boundaryLength * 2);
+        this.buffer = new byte[this.bufSize];
+        this.notifier = pNotifier;
+
+        this.boundary = new byte[this.boundaryLength];
+        this.keepRegion = this.boundary.length;
+        System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0,
+                BOUNDARY_PREFIX.length);
+        System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
+                boundary.length);
+
+        head = 0;
+        tail = 0;
+    }
+
+
+    /**
+     * <p> Constructs a <code>MultipartStream</code> with a default size buffer.
+     *
+     * @param input    The <code>InputStream</code> to serve as a data source.
+     * @param boundary The token used for dividing the stream into
+     *                 <code>encapsulations</code>.
+     * @param pNotifier An object for calling the progress listener, if any.
+     *
+     *
+     * @see #MultipartStream(InputStream, byte[], int,
+     *     MultipartStream.ProgressNotifier)
+     */
+    MultipartStream(InputStream input,
+            byte[] boundary,
+            ProgressNotifier pNotifier) {
+        this(input, boundary, DEFAULT_BUFSIZE, pNotifier);
+    }
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Retrieves the character encoding used when reading the headers of an
+     * individual part. When not specified, or <code>null</code>, the platform
+     * default encoding is used.
+
+     *
+     * @return The encoding used to read part headers.
+     */
+    public String getHeaderEncoding() {
+        return headerEncoding;
+    }
+
+
+    /**
+     * Specifies the character encoding to be used when reading the headers of
+     * individual parts. When not specified, or <code>null</code>, the platform
+     * default encoding is used.
+     *
+     * @param encoding The encoding used to read part headers.
+     */
+    public void setHeaderEncoding(String encoding) {
+        headerEncoding = encoding;
+    }
+
+
+    /**
+     * Reads a byte from the <code>buffer</code>, and refills it as
+     * necessary.
+     *
+     * @return The next byte from the input stream.
+     *
+     * @throws IOException if there is no more data available.
+     */
+    public byte readByte() throws IOException {
+        // Buffer depleted ?
+        if (head == tail) {
+            head = 0;
+            // Refill.
+            tail = input.read(buffer, head, bufSize);
+            if (tail == -1) {
+                // No more data available.
+                throw new IOException("No more data is available");
+            }
+            if (notifier != null) {
+                notifier.noteBytesRead(tail);
+            }
+        }
+        return buffer[head++];
+    }
+
+
+    /**
+     * Skips a <code>boundary</code> token, and checks whether more
+     * <code>encapsulations</code> are contained in the stream.
+     *
+     * @return <code>true</code> if there are more encapsulations in
+     *         this stream; <code>false</code> otherwise.
+     *
+     * @throws MalformedStreamException if the stream ends unexpecetedly or
+     *                                  fails to follow required syntax.
+     */
+    public boolean readBoundary()
+            throws MalformedStreamException {
+        byte[] marker = new byte[2];
+        boolean nextChunk = false;
+
+        head += boundaryLength;
+        try {
+            marker[0] = readByte();
+            if (marker[0] == LF) {
+                // Work around IE5 Mac bug with input type=image.
+                // Because the boundary delimiter, not including the trailing
+                // CRLF, must not appear within any file (RFC 2046, section
+                // 5.1.1), we know the missing CR is due to a buggy browser
+                // rather than a file containing something similar to a
+                // boundary.
+                return true;
+            }
+
+            marker[1] = readByte();
+            if (arrayequals(marker, STREAM_TERMINATOR, 2)) {
+                nextChunk = false;
+            } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) {
+                nextChunk = true;
+            } else {
+                throw new MalformedStreamException(
+                "Unexpected characters follow a boundary");
+            }
+        } catch (IOException e) {
+            throw new MalformedStreamException("Stream ended unexpectedly");
+        }
+        return nextChunk;
+    }
+
+
+    /**
+     * <p>Changes the boundary token used for partitioning the stream.
+     *
+     * <p>This method allows single pass processing of nested multipart
+     * streams.
+     *
+     * <p>The boundary token of the nested stream is <code>required</code>
+     * to be of the same length as the boundary token in parent stream.
+     *
+     * <p>Restoring the parent stream boundary token after processing of a
+     * nested stream is left to the application.
+     *
+     * @param boundary The boundary to be used for parsing of the nested
+     *                 stream.
+     *
+     * @throws IllegalBoundaryException if the <code>boundary</code>
+     *                                  has a different length than the one
+     *                                  being currently parsed.
+     */
+    public void setBoundary(byte[] boundary)
+            throws IllegalBoundaryException {
+        if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {
+            throw new IllegalBoundaryException(
+            "The length of a boundary token can not be changed");
+        }
+        System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
+                boundary.length);
+    }
+
+
+    /**
+     * <p>Reads the <code>header-part</code> of the current
+     * <code>encapsulation</code>.
+     *
+     * <p>Headers are returned verbatim to the input stream, including the
+     * trailing <code>CRLF</code> marker. Parsing is left to the
+     * application.
+     *
+     * <p><strong>TODO</strong> allow limiting maximum header size to
+     * protect against abuse.
+     *
+     * @return The <code>header-part</code> of the current encapsulation.
+     *
+     * @throws MalformedStreamException if the stream ends unexpecetedly.
+     */
+    public String readHeaders()
+    throws MalformedStreamException {
+        int i = 0;
+        byte b;
+        // to support multi-byte characters
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        int size = 0;
+        while (i < HEADER_SEPARATOR.length) {
+            try {
+                b = readByte();
+            } catch (IOException e) {
+                throw new MalformedStreamException("Stream ended unexpectedly");
+            }
+            if (++size > HEADER_PART_SIZE_MAX) {
+                throw new MalformedStreamException(
+                        "Header section has more than " + HEADER_PART_SIZE_MAX
+                        + " bytes (maybe it is not properly terminated)");
+            }
+            if (b == HEADER_SEPARATOR[i]) {
+                i++;
+            } else {
+                i = 0;
+            }
+            baos.write(b);
+        }
+
+        String headers = null;
+        if (headerEncoding != null) {
+            try {
+                headers = baos.toString(headerEncoding);
+            } catch (UnsupportedEncodingException e) {
+                // Fall back to platform default if specified encoding is not
+                // supported.
+                headers = baos.toString();
+            }
+        } else {
+            headers = baos.toString();
+        }
+
+        return headers;
+    }
+
+
+    /**
+     * <p>Reads <code>body-data</code> from the current
+     * <code>encapsulation</code> and writes its contents into the
+     * output <code>Stream</code>.
+     *
+     * <p>Arbitrary large amounts of data can be processed by this
+     * method using a constant size buffer. (see {@link
+     * #MultipartStream(InputStream,byte[],int,
+     *   MultipartStream.ProgressNotifier) constructor}).
+     *
+     * @param output The <code>Stream</code> to write data into. May
+     *               be null, in which case this method is equivalent
+     *               to {@link #discardBodyData()}.
+     *
+     * @return the amount of data written.
+     *
+     * @throws MalformedStreamException if the stream ends unexpectedly.
+     * @throws IOException              if an i/o error occurs.
+     */
+    public int readBodyData(OutputStream output)
+            throws MalformedStreamException, IOException {
+        final InputStream istream = newInputStream();
+        return (int) Streams.copy(istream, output, false);
+    }
+
+    /**
+     * Creates a new {@link ItemInputStream}.
+     * @return A new instance of {@link ItemInputStream}.
+     */
+    ItemInputStream newInputStream() {
+        return new ItemInputStream();
+    }
+
+    /**
+     * <p> Reads <code>body-data</code> from the current
+     * <code>encapsulation</code> and discards it.
+     *
+     * <p>Use this method to skip encapsulations you don't need or don't
+     * understand.
+     *
+     * @return The amount of data discarded.
+     *
+     * @throws MalformedStreamException if the stream ends unexpectedly.
+     * @throws IOException              if an i/o error occurs.
+     */
+    public int discardBodyData()
+    throws MalformedStreamException,
+    IOException {
+        return readBodyData(null);
+    }
+
+
+    /**
+     * Finds the beginning of the first <code>encapsulation</code>.
+     *
+     * @return <code>true</code> if an <code>encapsulation</code> was found in
+     *         the stream.
+     *
+     * @throws IOException if an i/o error occurs.
+     */
+    public boolean skipPreamble()
+    throws IOException {
+        // First delimiter may be not preceeded with a CRLF.
+        System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
+        boundaryLength = boundary.length - 2;
+        try {
+            // Discard all data up to the delimiter.
+            discardBodyData();
+
+            // Read boundary - if succeded, the stream contains an
+            // encapsulation.
+            return readBoundary();
+        } catch (MalformedStreamException e) {
+            return false;
+        } finally {
+            // Restore delimiter.
+            System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
+            boundaryLength = boundary.length;
+            boundary[0] = CR;
+            boundary[1] = LF;
+        }
+    }
+
+
+    /**
+     * Compares <code>count</code> first bytes in the arrays
+     * <code>a</code> and <code>b</code>.
+     *
+     * @param a     The first array to compare.
+     * @param b     The second array to compare.
+     * @param count How many bytes should be compared.
+     *
+     * @return <code>true</code> if <code>count</code> first bytes in arrays
+     *         <code>a</code> and <code>b</code> are equal.
+     */
+    public static boolean arrayequals(byte[] a,
+            byte[] b,
+            int count) {
+        for (int i = 0; i < count; i++) {
+            if (a[i] != b[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Searches for a byte of specified value in the <code>buffer</code>,
+     * starting at the specified <code>position</code>.
+     *
+     * @param value The value to find.
+     * @param pos   The starting position for searching.
+     *
+     * @return The position of byte found, counting from beginning of the
+     *         <code>buffer</code>, or <code>-1</code> if not found.
+     */
+    protected int findByte(byte value,
+            int pos) {
+        for (int i = pos; i < tail; i++) {
+            if (buffer[i] == value) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+
+    /**
+     * Searches for the <code>boundary</code> in the <code>buffer</code>
+     * region delimited by <code>head</code> and <code>tail</code>.
+     *
+     * @return The position of the boundary found, counting from the
+     *         beginning of the <code>buffer</code>, or <code>-1</code> if
+     *         not found.
+     */
+    protected int findSeparator() {
+        int first;
+        int match = 0;
+        int maxpos = tail - boundaryLength;
+        for (first = head;
+        (first <= maxpos) && (match != boundaryLength);
+        first++) {
+            first = findByte(boundary[0], first);
+            if (first == -1 || (first > maxpos)) {
+                return -1;
+            }
+            for (match = 1; match < boundaryLength; match++) {
+                if (buffer[first + match] != boundary[match]) {
+                    break;
+                }
+            }
+        }
+        if (match == boundaryLength) {
+            return first - 1;
+        }
+        return -1;
+    }
+
+    /**
+     * Thrown to indicate that the input stream fails to follow the
+     * required syntax.
+     */
+    public static class MalformedStreamException
+    extends IOException {
+        /**
+         * Constructs a <code>MalformedStreamException</code> with no
+         * detail message.
+         */
+        public MalformedStreamException() {
+            super();
+        }
+
+        /**
+         * Constructs an <code>MalformedStreamException</code> with
+         * the specified detail message.
+         *
+         * @param message The detail message.
+         */
+        public MalformedStreamException(String message) {
+            super(message);
+        }
+    }
+
+
+    /**
+     * Thrown upon attempt of setting an invalid boundary token.
+     */
+    public static class IllegalBoundaryException
+            extends IOException {
+        /**
+         * Constructs an <code>IllegalBoundaryException</code> with no
+         * detail message.
+         */
+        public IllegalBoundaryException() {
+            super();
+        }
+
+        /**
+         * Constructs an <code>IllegalBoundaryException</code> with
+         * the specified detail message.
+         *
+         * @param message The detail message.
+         */
+        public IllegalBoundaryException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * An {@link InputStream} for reading an items contents.
+     */
+    public class ItemInputStream extends InputStream {
+        /** The number of bytes, which have been read so far.
+         */
+        private long total;
+        /** The number of bytes, which must be hold, because
+         * they might be a part of the boundary.
+         */
+        private int pad;
+        /** The current offset in the buffer.
+         */
+        private int pos;
+        /** Whether the stream is already closed.
+         */
+        private boolean closed;
+
+        /**
+         * Creates a new instance.
+         */
+        ItemInputStream() {
+            findSeparator();
+        }
+
+        /**
+         * Called for finding the separator.
+         */
+        private void findSeparator() {
+            pos = MultipartStream.this.findSeparator();
+            if (pos == -1) {
+                if (tail - head > keepRegion) {
+                    pad = keepRegion;
+                } else {
+                    pad = tail - head;
+                }
+            }
+        }
+
+        /**
+         * Returns the number of bytes, which have been read
+         * by the stream.
+         * @return Number of bytes, which have been read so far.
+         */
+        public long getBytesRead() {
+            return total;
+        }
+
+        /**
+         * Returns the number of bytes, which are currently
+         * available, without blocking.
+         * @throws IOException An I/O error occurs.
+         * @return Number of bytes in the buffer.
+         */
+        public int available() throws IOException {
+            if (pos == -1) {
+                return tail - head - pad;
+            }
+            return pos - head;
+        }
+
+        /** Offset when converting negative bytes to integers.
+         */
+        private static final int BYTE_POSITIVE_OFFSET = 256;
+
+        /**
+         * Returns the next byte in the stream.
+         * @return The next byte in the stream, as a non-negative
+         *   integer, or -1 for EOF.
+         * @throws IOException An I/O error occurred.
+         */
+        public int read() throws IOException {
+            if (available() == 0) {
+                if (makeAvailable() == 0) {
+                    return -1;
+                }
+            }
+            ++total;
+            int b = buffer[head++];
+            if (b >= 0) {
+                return b;
+            }
+            return b + BYTE_POSITIVE_OFFSET;
+        }
+
+        /**
+         * Reads bytes into the given buffer.
+         * @param b The destination buffer, where to write to.
+         * @param off Offset of the first byte in the buffer.
+         * @param len Maximum number of bytes to read.
+         * @return Number of bytes, which have been actually read,
+         *   or -1 for EOF.
+         * @throws IOException An I/O error occurred.
+         */
+        public int read(byte[] b, int off, int len) throws IOException {
+            if (len == 0) {
+                return 0;
+            }
+            int res = available();
+            if (res == 0) {
+                res = makeAvailable();
+                if (res == 0) {
+                    return -1;
+                }
+            }
+            res = Math.min(res, len);
+            System.arraycopy(buffer, head, b, off, res);
+            head += res;
+            total += res;
+            return res;
+        }
+
+        /**
+         * Closes the input stream.
+         * @throws IOException An I/O error occurred.
+         */
+        public void close() throws IOException {
+            close(false);
+        }
+
+        /**
+         * Closes the input stream.
+         * @param pCloseUnderlying Whether to close the underlying stream
+         *   (hard close)
+         * @throws IOException An I/O error occurred.
+         */
+        public void close(boolean pCloseUnderlying) throws IOException {
+            if (closed) {
+                return;
+            }
+            if (pCloseUnderlying) {
+                closed = true;
+                input.close();
+            } else {
+                for (;;) {
+                    int av = available();
+                    if (av == 0) {
+                        av = makeAvailable();
+                        if (av == 0) {
+                            break;
+                        }
+                    }
+                    if (skip(av) != av && log.isLoggable(Level.WARNING)) {
+                        log.log(Level.WARNING, LogFacade.FAILED_SKIP_BYTES_MULTIPART_STREAM_CLOSE_EXCEPTION, av);
+                    }
+                }
+            }
+            closed = true;
+        }
+
+        /**
+         * Skips the given number of bytes.
+         * @param bytes Number of bytes to skip.
+         * @return The number of bytes, which have actually been
+         *   skipped.
+         * @throws IOException An I/O error occurred.
+         */
+        public long skip(long bytes) throws IOException {
+            int av = available();
+            if (av == 0) {
+                av = makeAvailable();
+                if (av == 0) {
+                    return 0;
+                }
+            }
+            long res = Math.min(av, bytes);
+            head += res;
+            return res;
+        }
+
+        /**
+         * Attempts to read more data.
+         * @return Number of available bytes
+         * @throws IOException An I/O error occurred.
+         */
+        private int makeAvailable() throws IOException {
+            if (pos != -1) {
+                return 0;
+            }
+
+            // Move the data to the beginning of the buffer.
+            total += tail - head - pad;
+            System.arraycopy(buffer, tail - pad, buffer, 0, pad);
+
+            // Refill buffer with new data.
+            head = 0;
+            tail = pad;
+
+            for (;;) {
+                int bytesRead = input.read(buffer, tail, bufSize - tail);
+                if (bytesRead == -1) {
+                    // The last pad amount is left in the buffer.
+                    // Boundary can't be in there so signal an error
+                    // condition.
+                    final String msg = "Stream ended unexpectedly";
+                    throw new MalformedStreamException(msg);
+                }
+                if (notifier != null) {
+                    notifier.noteBytesRead(bytesRead);
+                }
+                tail += bytesRead;
+
+                findSeparator();
+                int av = available();
+
+                if (av > 0 || pos != -1) {
+                    return av;
+                }
+            }
+        }
+
+        /**
+         * Returns, whether the stream is closed.
+         * @return True, if the stream is closed, otherwise false.
+         */
+        public boolean isClosed() {
+            return closed;
+        }
+    }
+
+    // ------------------------------------------------------ Debugging methods
+
+
+    // These are the methods that were used to debug this stuff.
+    /*
+
+    // Dump data.
+    protected void dump()
+    {
+        System.out.println("01234567890");
+        byte[] temp = new byte[buffer.length];
+        for(int i=0; i<buffer.length; i++)
+        {
+            if (buffer[i] == 0x0D || buffer[i] == 0x0A)
+            {
+                temp[i] = 0x21;
+            }
+            else
+            {
+                temp[i] = buffer[i];
+            }
+        }
+        System.out.println(new String(temp));
+        int i;
+        for (i=0; i<head; i++)
+            System.out.print(" ");
+        System.out.println("h");
+        for (i=0; i<tail; i++)
+            System.out.print(" ");
+        System.out.println("t");
+        System.out.flush();
+    }
+
+    // Main routine, for testing purposes only.
+    //
+    // @param args A String[] with the command line arguments.
+    // @throws Exception, a generic exception.
+    public static void main( String[] args )
+        throws Exception
+    {
+        File boundaryFile = new File("boundary.dat");
+        int boundarySize = (int)boundaryFile.length();
+        byte[] boundary = new byte[boundarySize];
+        FileInputStream input = new FileInputStream(boundaryFile);
+        input.read(boundary,0,boundarySize);
+
+        input = new FileInputStream("multipart.dat");
+        MultipartStream chunks = new MultipartStream(input, boundary);
+
+        int i = 0;
+        String header;
+        OutputStream output;
+        boolean nextChunk = chunks.skipPreamble();
+        while (nextChunk)
+        {
+            header = chunks.readHeaders();
+            System.out.println("!"+header+"!");
+            System.out.println("wrote part"+i+".dat");
+            output = new FileOutputStream("part"+(i++)+".dat");
+            chunks.readBodyData(output);
+            nextChunk = chunks.readBoundary();
+        }
+    }
+
+     */
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ParameterParser.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ParameterParser.java
new file mode 100644
index 0000000..eca4991
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ParameterParser.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * A simple parser intended to parse sequences of name/value pairs.
+ * Parameter values are exptected to be enclosed in quotes if they
+ * contain unsafe characters, such as '=' characters or separators.
+ * Parameter values are optional and can be omitted.
+ *
+ * <p>
+ *  <code>param1 = value; param2 = "anything goes; really"; param3</code>
+ * </p>
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ */
+
+class ParameterParser {
+    /**
+     * String to be parsed.
+     */
+    private char[] chars = null;
+
+    /**
+     * Current position in the string.
+     */
+    private int pos = 0;
+
+    /**
+     * Maximum position in the string.
+     */
+    private int len = 0;
+
+    /**
+     * Start of a token.
+     */
+    private int i1 = 0;
+
+    /**
+     * End of a token.
+     */
+    private int i2 = 0;
+
+    /**
+     * Whether names stored in the map should be converted to lower case.
+     */
+    private boolean lowerCaseNames = false;
+
+    /**
+     * Default ParameterParser constructor.
+     */
+    public ParameterParser() {
+        super();
+    }
+
+    /**
+     * Are there any characters left to parse?
+     *
+     * @return <tt>true</tt> if there are unparsed characters,
+     *         <tt>false</tt> otherwise.
+     */
+    private boolean hasChar() {
+        return this.pos < this.len;
+    }
+
+    /**
+     * A helper method to process the parsed token. This method removes
+     * leading and trailing blanks as well as enclosing quotation marks,
+     * when necessary.
+     *
+     * @param quoted <tt>true</tt> if quotation marks are expected,
+     *               <tt>false</tt> otherwise.
+     * @return the token
+     */
+    private String getToken(boolean quoted) {
+        // Trim leading white spaces
+        while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) {
+            i1++;
+        }
+        // Trim trailing white spaces
+        while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) {
+            i2--;
+        }
+        // Strip away quotation marks if necessary
+        if (quoted) {
+            if (((i2 - i1) >= 2)
+                && (chars[i1] == '"')
+                && (chars[i2 - 1] == '"')) {
+                i1++;
+                i2--;
+            }
+        }
+        String result = null;
+        if (i2 > i1) {
+            result = new String(chars, i1, i2 - i1);
+        }
+        return result;
+    }
+
+    /**
+     * Tests if the given character is present in the array of characters.
+     *
+     * @param ch the character to test for presense in the array of characters
+     * @param charray the array of characters to test against
+     *
+     * @return <tt>true</tt> if the character is present in the array of
+     *   characters, <tt>false</tt> otherwise.
+     */
+    private boolean isOneOf(char ch, final char[] charray) {
+        boolean result = false;
+        for (int i = 0; i < charray.length; i++) {
+            if (ch == charray[i]) {
+                result = true;
+                break;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Parses out a token until any of the given terminators
+     * is encountered.
+     *
+     * @param terminators the array of terminating characters. Any of these
+     * characters when encountered signify the end of the token
+     *
+     * @return the token
+     */
+    private String parseToken(final char[] terminators) {
+        char ch;
+        i1 = pos;
+        i2 = pos;
+        while (hasChar()) {
+            ch = chars[pos];
+            if (isOneOf(ch, terminators)) {
+                break;
+            }
+            i2++;
+            pos++;
+        }
+        return getToken(false);
+    }
+
+    /**
+     * Parses out a token until any of the given terminators
+     * is encountered outside the quotation marks.
+     *
+     * @param terminators the array of terminating characters. Any of these
+     * characters when encountered outside the quotation marks signify the end
+     * of the token
+     *
+     * @return the token
+     */
+    private String parseQuotedToken(final char[] terminators) {
+        char ch;
+        i1 = pos;
+        i2 = pos;
+        boolean quoted = false;
+        boolean charEscaped = false;
+        while (hasChar()) {
+            ch = chars[pos];
+            if (!quoted && isOneOf(ch, terminators)) {
+                break;
+            }
+            if (!charEscaped && ch == '"') {
+                quoted = !quoted;
+            }
+            charEscaped = (!charEscaped && ch == '\\');
+            i2++;
+            pos++;
+
+        }
+        return getToken(true);
+    }
+
+    /**
+     * Returns <tt>true</tt> if parameter names are to be converted to lower
+     * case when name/value pairs are parsed.
+     *
+     * @return <tt>true</tt> if parameter names are to be
+     * converted to lower case when name/value pairs are parsed.
+     * Otherwise returns <tt>false</tt>
+     */
+    public boolean isLowerCaseNames() {
+        return this.lowerCaseNames;
+    }
+
+    /**
+     * Sets the flag if parameter names are to be converted to lower case when
+     * name/value pairs are parsed.
+     *
+     * @param b <tt>true</tt> if parameter names are to be
+     * converted to lower case when name/value pairs are parsed.
+     * <tt>false</tt> otherwise.
+     */
+    public void setLowerCaseNames(boolean b) {
+        this.lowerCaseNames = b;
+    }
+
+    /**
+     * Extracts a map of name/value pairs from the given string. Names are
+     * expected to be unique. Multiple separators may be specified and
+     * the earliest found in the input string is used.
+     *
+     * @param str the string that contains a sequence of name/value pairs
+     * @param separators the name/value pairs separators
+     *
+     * @return a map of name/value pairs
+     */
+    public Map<String, String> parse(final String str, char[] separators) {
+        if (separators == null || separators.length == 0) {
+            return new HashMap<String, String>();
+        }
+        char separator = separators[0];
+        if (str != null) {
+            int idx = str.length();
+            for (int i = 0;  i < separators.length;  i++) {
+                int tmp = str.indexOf(separators[i]);
+                if (tmp != -1) {
+                    if (tmp < idx) {
+                        idx = tmp;
+                        separator = separators[i];
+                    }
+                }
+            }
+        }
+        return parse(str, separator);
+    }
+
+    /**
+     * Extracts a map of name/value pairs from the given string. Names are
+     * expected to be unique.
+     *
+     * @param str the string that contains a sequence of name/value pairs
+     * @param separator the name/value pairs separator
+     *
+     * @return a map of name/value pairs
+     */
+    public Map<String, String> parse(final String str, char separator) {
+        if (str == null) {
+            return new HashMap<String, String>();
+        }
+        return parse(str.toCharArray(), separator);
+    }
+
+    /**
+     * Extracts a map of name/value pairs from the given array of
+     * characters. Names are expected to be unique.
+     *
+     * @param chars the array of characters that contains a sequence of
+     * name/value pairs
+     * @param separator the name/value pairs separator
+     *
+     * @return a map of name/value pairs
+     */
+    public Map<String, String> parse(final char[] chars, char separator) {
+        if (chars == null) {
+            return new HashMap<String, String>();
+        }
+        return parse(chars, 0, chars.length, separator);
+    }
+
+    /**
+     * Extracts a map of name/value pairs from the given array of
+     * characters. Names are expected to be unique.
+     *
+     * @param chars the array of characters that contains a sequence of
+     * name/value pairs
+     * @param offset - the initial offset.
+     * @param length - the length.
+     * @param separator the name/value pairs separator
+     *
+     * @return a map of name/value pairs
+     */
+    public Map<String, String> parse(
+        final char[] chars,
+        int offset,
+        int length,
+        char separator) {
+
+        HashMap<String, String> params = new HashMap<String, String>();
+        if (chars == null) {
+            return params;
+        }
+
+        this.chars = chars;
+        this.pos = offset;
+        this.len = length;
+
+        String paramName = null;
+        String paramValue = null;
+        while (hasChar()) {
+            paramName = parseToken(new char[] {
+                    '=', separator });
+            paramValue = null;
+            if (hasChar() && (chars[pos] == '=')) {
+                pos++; // skip '='
+                paramValue = parseQuotedToken(new char[] {
+                        separator });
+            }
+            if (hasChar() && (chars[pos] == separator)) {
+                pos++; // skip separator
+            }
+            if ((paramName != null) && (paramName.length() > 0)) {
+                if (this.lowerCaseNames) {
+                    paramName = paramName.toLowerCase(Locale.ENGLISH);
+                }
+                params.put(paramName, paramValue);
+            }
+        }
+        return params;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/PartHeaders.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/PartHeaders.java
new file mode 100644
index 0000000..d1860fc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/PartHeaders.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * @author Michael C. Macaluso
+ * @author Kin-man Chung (Adopted for Glassfish)
+ */
+class PartHeaders implements Serializable {
+    private static final long serialVersionUID = -4455695752627032559L;
+
+    /**
+     * Map of <code>String</code> keys to a <code>List</code> of
+     * <code>String</code> instances.
+     */
+    private final Map<String, List<String>> headerMap =
+        new LinkedHashMap<String, List<String>>();
+
+    private Set<String> headerNames = null;
+
+    public String getHeader(String name) {
+        String nameLower = name.toLowerCase(Locale.ENGLISH);
+        List<String> headerValueList = headerMap.get(nameLower);
+        if (null == headerValueList) {
+            return null;
+        }
+        return headerValueList.get(0);
+    }
+
+    public synchronized Collection<String> getHeaderNames() {
+        if (null == headerNames) {
+            headerNames = Collections.unmodifiableSet(headerMap.keySet());
+        }
+        return headerNames;
+    }
+
+    public List<String> getHeaders(String name) {
+        String nameLower = name.toLowerCase(Locale.ENGLISH);
+        List<String> headerValueList = headerMap.get(nameLower);
+        if (null == headerValueList) {
+            return Collections.emptyList();
+        }
+        return headerValueList;
+    }
+
+    /**
+     * Method to add header values to this instance.
+     *
+     * @param name name of this header
+     * @param value value of this header
+     */
+    public synchronized void addHeader(String name, String value) {
+        String nameLower = name.toLowerCase(Locale.ENGLISH);
+        List<String> headerValueList = headerMap.get(nameLower);
+        if (null == headerValueList) {
+            headerValueList = new ArrayList<String>();
+            headerMap.put(nameLower, headerValueList);
+        }
+        headerValueList.add(value);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/PartItem.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/PartItem.java
new file mode 100644
index 0000000..e6b934d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/PartItem.java
@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.util.RequestUtil;
+
+import javax.servlet.http.Part;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * This class implements java.servlet.http.Part.
+ *
+ * Original authors:
+ * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @author Sean C. Sullivan
+ *
+ * Adopted for Glassfish:
+ * @author Kin-man Chung
+ *
+ * @version $Id: PartItem.java $
+ */
+class PartItem
+    implements Serializable, Part {
+
+    // ----------------------------------------------------- Manifest constants
+
+    /**
+     * The UID to use when serializing this instance.
+     */
+    private static final long serialVersionUID = 2237570099615271025L;
+
+    private static final Logger log = LogFacade.getLogger();
+    private final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------------- Data members
+
+    /**
+     * UID used in unique file name generation.
+     */
+    private static final String UID =
+            new java.rmi.server.UID().toString()
+                .replace(':', '_').replace('-', '_');
+
+    /**
+     * Counter used in unique identifier generation.
+     */
+    private static int counter = 0;
+
+
+    /**
+     * The name of the form field as provided by the browser.
+     */
+    private String fieldName;
+
+
+    /**
+     * The content type passed by the browser, or <code>null</code> if
+     * not defined.
+     */
+    private String contentType;
+
+
+    /**
+     * Whether or not this item is a simple form field.
+     */
+    private boolean isFormField;
+
+
+    /**
+     * The original filename in the user's filesystem.
+     */
+    private String fileName;
+
+
+    /**
+     * The size of the item, in bytes. This is used to cache the size when a
+     * file item is moved from its original location.
+     */
+    private long size = -1;
+
+
+    /**
+     * The threshold above which uploads will be stored on disk.
+     */
+    private int sizeThreshold;
+
+
+    /**
+     * The directory in which uploaded files will be stored, if stored on disk.
+     */
+    private File repository;
+
+
+    /**
+     * Cached contents of the file.
+     */
+    private byte[] cachedContent;
+
+
+    /**
+     * Output stream for this item.
+     */
+    private transient DeferredFileOutputStream dfos;
+
+    /**
+     * The temporary file to use.
+     */
+    private transient File tempFile;
+
+    /**
+     * File to allow for serialization of the content of this item.
+     */
+    private File dfosFile;
+
+    /**
+     * The items headers.
+     */
+    private PartHeaders headers;
+
+    /**
+     * The Multipart base.
+     */
+    private Multipart multipart;
+
+    /**
+     * The request character encoding;
+     */
+    private String requestCharEncoding;
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs a new <code>DiskFileItem</code> instance.
+     *
+     * @param multipart     The Multipart instance.
+     * @param headers       The PartHeaders instance.
+     * @param fieldName     The name of the form field.
+     * @param contentType   The content type passed by the browser or
+     *                      <code>null</code> if not specified.
+     * @param isFormField   Whether or not this item is a plain form field, as
+     *                      opposed to a file upload.
+     * @param fileName      The original filename in the user's filesystem, or
+     *                      <code>null</code> if not specified.
+     * @param requestCharEncoding
+     *                      The request character encoding.
+     */
+    public PartItem(Multipart multipart, PartHeaders headers,
+                    String fieldName, String contentType,
+                    boolean isFormField, String fileName,
+                    String requestCharEncoding) {
+
+        this.multipart = multipart;
+        this.headers = headers;
+        this.fieldName = fieldName;
+        this.contentType = contentType;
+        this.isFormField = isFormField;
+        this.fileName = fileName;
+        this.requestCharEncoding = requestCharEncoding;
+        this.sizeThreshold = multipart.getFileSizeThreshold();
+        this.repository = multipart.getRepository();
+    }
+
+
+    // ------------------------------- Methods from javax.activation.DataSource
+
+
+    /**
+     * Returns an {@link java.io.InputStream InputStream} that can be
+     * used to retrieve the contents of the file.
+     *
+     * @return An {@link java.io.InputStream InputStream} that can be
+     *         used to retrieve the contents of the file.
+     *
+     * @throws IOException if an error occurs.
+     */
+    public InputStream getInputStream()
+        throws IOException {
+        if (!isInMemory()) {
+            return new FileInputStream(dfos.getFile());
+        }
+
+        if (cachedContent == null) {
+            cachedContent = dfos.getData();
+        }
+        return new ByteArrayInputStream(cachedContent);
+    }
+
+
+    /**
+     * Returns the content type passed by the agent or <code>null</code> if
+     * not defined.
+     *
+     * @return The content type passed by the agent or <code>null</code> if
+     *         not defined.
+     */
+    public String getContentType() {
+        return contentType;
+    }
+
+
+    /**
+     * Returns the content charset passed by the agent or <code>null</code> if
+     * not defined.
+     *
+     * @return The content charset passed by the agent or <code>null</code> if
+     *         not defined.
+     */
+    public String getCharSet() {
+        ParameterParser parser = new ParameterParser();
+        parser.setLowerCaseNames(true);
+        // Parameter parser can handle null input
+        Map<String, String> params = parser.parse(getContentType(), ';');
+        return params.get("charset");
+    }
+
+
+    /**
+     * Returns the original filename in the client's filesystem.
+     *
+     * @return The original filename in the client's filesystem.
+     */
+    public String getSubmittedFileName() {
+        return fileName;
+    }
+
+
+    // ------------------------------------------------------- FileItem methods
+
+
+    /**
+     * Provides a hint as to whether or not the file contents will be read
+     * from memory.
+     *
+     * @return <code>true</code> if the file contents will be read
+     *         from memory; <code>false</code> otherwise.
+     */
+    public boolean isInMemory() {
+        if (cachedContent != null) {
+            return true;
+        }
+        return dfos.isInMemory();
+    }
+
+
+    /**
+     * Returns the size of the file.
+     *
+     * @return The size of the file, in bytes.
+     */
+    public long getSize() {
+        if (size >= 0) {
+            return size;
+        } else if (cachedContent != null) {
+            return cachedContent.length;
+        } else if (dfos.isInMemory()) {
+            return dfos.getData().length;
+        } else {
+            return dfos.getFile().length();
+        }
+    }
+
+
+    /**
+     * Returns the contents of the file as an array of bytes.  If the
+     * contents of the file were not yet cached in memory, they will be
+     * loaded from the disk storage and cached.
+     *
+     * @return The contents of the file as an array of bytes.
+     */
+    public byte[] get() {
+        if (isInMemory()) {
+            if (cachedContent == null) {
+                cachedContent = dfos.getData();
+            }
+            return cachedContent;
+        }
+
+        byte[] fileData = new byte[(int) getSize()];
+        FileInputStream fis = null;
+
+        try {
+            fis = new FileInputStream(dfos.getFile());
+            if (fis.read(fileData) != (int)getSize())
+                if (log.isLoggable(Level.INFO))
+                    log.log(Level.INFO, LogFacade.FILE_DATA_IS_EMPTY_INFO);
+        } catch (IOException e) {
+            fileData = null;
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+
+        return fileData;
+    }
+
+
+    /**
+     * Returns the contents of the file as a String, using the specified
+     * encoding.  This method uses {@link #get()} to retrieve the
+     * contents of the file.
+     *
+     * @param charset The charset to use.
+     *
+     * @return The contents of the file, as a string.
+     *
+     * @throws UnsupportedEncodingException if the requested character
+     *                                      encoding is not available.
+     */
+    public String getString(final String charset)
+        throws UnsupportedEncodingException {
+        return new String(get(), RequestUtil.lookupCharset(charset));
+    }
+
+
+    /**
+     * Returns the contents of the file as a String, using the default
+     * character encoding.  This method uses {@link #get()} to retrieve the
+     * contents of the file.
+     *
+     * @return The contents of the file, as a string.
+     *
+     * @todo Consider making this method throw UnsupportedEncodingException.
+     */
+    public String getString() {
+        byte[] rawdata = get();
+        String charset = getCharSet();
+        if (charset == null) {
+            // If content-type does not specify a charset, use the request
+            // character encoding if it is non-null;
+            if (requestCharEncoding != null) {
+                charset = requestCharEncoding;
+            } else {
+                // Media subtypes of type "text" are defined to have a default
+                // charset value of "ISO-8859-1" when received via HTTP
+                charset = Globals.ISO_8859_1_ENCODING;
+            }
+        }
+        try {
+            return new String(rawdata, RequestUtil.lookupCharset(charset));
+        } catch (UnsupportedEncodingException e) {
+            return new String(rawdata, Charset.defaultCharset());
+        }
+    }
+
+
+    /**
+     * A convenience method to write an uploaded item to disk. The client code
+     * is not concerned with whether or not the item is stored in memory, or on
+     * disk in a temporary location. They just want to write the uploaded item
+     * to a file.
+     * <p>
+     * This implementation first attempts to rename the uploaded item to the
+     * specified destination file, if the item was originally written to disk.
+     * Otherwise, the data will be copied to the specified file.
+     * <p>
+     * This method is only guaranteed to work <em>once</em>, the first time it
+     * is invoked for a particular item. This is because, in the event that the
+     * method renames a temporary file, that file will no longer be available
+     * to copy or rename again at a later time.
+     *
+     * @param file The file into which the uploaded item should be stored.
+     *
+     * @throws IOException if an error occurs.
+     */
+    public void write(File file) throws IOException {
+        if (isInMemory()) {
+            FileOutputStream fout = null;
+            try {
+                fout = new FileOutputStream(file);
+                fout.write(get());
+            } finally {
+                if (fout != null) {
+                    fout.close();
+                }
+            }
+        } else {
+            File outputFile = getStoreLocation();
+            if (outputFile != null) {
+                // Save the length of the file
+                size = outputFile.length();
+                /*
+                 * The uploaded file is being stored on disk
+                 * in a temporary location so move it to the
+                 * desired file.
+                 */
+                if (!outputFile.renameTo(file)) {
+                    BufferedInputStream in = null;
+                    BufferedOutputStream out = null;
+                    try {
+                        in = new BufferedInputStream(
+                            new FileInputStream(outputFile));
+                        out = new BufferedOutputStream(
+                                new FileOutputStream(file));
+                        Streams.copy(in, out, false);
+                    } finally {
+                        if (in != null) {
+                            try {
+                                in.close();
+                            } catch (IOException e) {
+                                // ignore
+                            }
+                        }
+                        if (out != null) {
+                            try {
+                                out.close();
+                            } catch (IOException e) {
+                                // ignore
+                            }
+                        }
+                    }
+                }
+            } else {
+                /*
+                 * For whatever reason we cannot write the
+                 * file to disk.
+                 */
+                throw new IOException(
+                    "Cannot write uploaded file to disk!");
+            }
+        }
+    }
+
+    public void write (String file) throws IOException {
+       write(new File(repository, file));
+    }
+
+
+    /**
+     * Deletes the underlying storage for a file item, including deleting any
+     * associated temporary disk file. Although this storage will be deleted
+     * automatically when the <code>FileItem</code> instance is garbage
+     * collected, this method can be used to ensure that this is done at an
+     * earlier time, thus preserving system resources.
+     */
+    public void delete() {
+        cachedContent = null;
+        File outputFile = getStoreLocation();
+        if (outputFile != null && outputFile.exists()) {
+            deleteFile(outputFile);
+        }
+    }
+
+
+    /**
+     * Returns the name of the field in the multipart form corresponding to
+     * this file item.
+     *
+     * @return The name of the form field.
+     *
+     * @see #setName(java.lang.String)
+     *
+     */
+    public String getName() {
+        return fieldName;
+    }
+
+
+    /**
+     * Sets the field name used to reference this file item.
+     *
+     * @param fieldName The name of the form field.
+     *
+     * @see #getName()
+     *
+     */
+    public void setName(String fieldName) {
+        this.fieldName = fieldName;
+    }
+
+
+    /**
+     * Determines whether or not a <code>FileItem</code> instance represents
+     * a simple form field.
+     *
+     * @return <code>true</code> if the instance represents a simple form
+     *         field; <code>false</code> if it represents an uploaded file.
+     *
+     * @see #setFormField(boolean)
+     *
+     */
+    public boolean isFormField() {
+        return isFormField;
+    }
+
+
+    /**
+     * Specifies whether or not a <code>FileItem</code> instance represents
+     * a simple form field.
+     *
+     * @param state <code>true</code> if the instance represents a simple form
+     *              field; <code>false</code> if it represents an uploaded file.
+     *
+     * @see #isFormField()
+     *
+     */
+    public void setFormField(boolean state) {
+        isFormField = state;
+    }
+
+
+    /**
+     * Returns an {@link java.io.OutputStream OutputStream} that can
+     * be used for storing the contents of the file.
+     *
+     * @return An {@link java.io.OutputStream OutputStream} that can be used
+     *         for storing the contensts of the file.
+     *
+     * @throws IOException if an error occurs.
+     */
+    public OutputStream getOutputStream()
+        throws IOException {
+        if (dfos == null) {
+            File outputFile = getTempFile();
+            dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
+        }
+        return dfos;
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Returns the {@link java.io.File} object for the <code>FileItem</code>'s
+     * data's temporary location on the disk. Note that for
+     * <code>FileItem</code>s that have their data stored in memory,
+     * this method will return <code>null</code>. When handling large
+     * files, you can use {@link java.io.File#renameTo(java.io.File)} to
+     * move the file to new location without copying the data, if the
+     * source and destination locations reside within the same logical
+     * volume.
+     *
+     * @return The data file, or <code>null</code> if the data is stored in
+     *         memory.
+     */
+    public File getStoreLocation() {
+        return dfos == null ? null : dfos.getFile();
+    }
+
+
+    // ------------------------------------------------------ Protected methods
+
+
+    /**
+     * Removes the file contents from the temporary storage.
+     */
+    protected void finalize() {
+        File outputFile = dfos.getFile();
+
+        if (outputFile != null && outputFile.exists()) {
+            deleteFile(outputFile);
+        }
+    }
+
+
+    /**
+     * Creates and returns a {@link java.io.File File} representing a uniquely
+     * named temporary file in the configured repository path. The lifetime of
+     * the file is tied to the lifetime of the <code>FileItem</code> instance;
+     * the file will be deleted when the instance is garbage collected.
+     *
+     * @return The {@link java.io.File File} to be used for temporary storage.
+     */
+    protected File getTempFile() {
+        if (tempFile == null) {
+            File tempDir = repository;
+            if (tempDir == null) {
+                tempDir = new File(System.getProperty("java.io.tmpdir"));
+            }
+
+            String tempFileName =
+                "upload_" + UID + "_" + getUniqueId() + ".tmp";
+
+            tempFile = new File(tempDir, tempFileName);
+        }
+        return tempFile;
+    }
+
+
+    // -------------------------------------------------------- Private methods
+
+
+    /**
+     * Returns an identifier that is unique within the class loader used to
+     * load this class, but does not have random-like apearance.
+     *
+     * @return A String with the non-random looking instance identifier.
+     */
+    private static String getUniqueId() {
+        final int limit = 100000000;
+        int current;
+        synchronized (PartItem.class) {
+            current = counter++;
+        }
+        String id = Integer.toString(current);
+
+        // If you manage to get more than 100 million of ids, you'll
+        // start getting ids longer than 8 characters.
+        if (current < limit) {
+            id = ("00000000" + id).substring(id.length());
+        }
+        return id;
+    }
+
+
+
+
+    /**
+     * Returns a string representation of this object.
+     *
+     * @return a string representation of this object.
+     */
+    public String toString() {
+        return "File name=" + this.getSubmittedFileName()
+            + ", StoreLocation="
+            + String.valueOf(this.getStoreLocation())
+            + ", size="
+            + this.getSize()
+            + "bytes, "
+            + "isFormField=" + isFormField()
+            + ", FieldName="
+            + this.getName();
+    }
+
+
+    // -------------------------------------------------- Serialization methods
+
+
+    /**
+     * Writes the state of this object during serialization.
+     *
+     * @param out The stream to which the state should be written.
+     *
+     * @throws IOException if an error occurs.
+     */
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        // Read the data
+        if (dfos.isInMemory()) {
+            cachedContent = get();
+        } else {
+            cachedContent = null;
+            dfosFile = dfos.getFile();
+        }
+
+        // write out values
+        out.defaultWriteObject();
+    }
+
+    /**
+     * Reads the state of this object during deserialization.
+     *
+     * @param in The stream from which the state should be read.
+     *
+     * @throws IOException if an error occurs.
+     * @throws ClassNotFoundException if class cannot be found.
+     */
+    private void readObject(ObjectInputStream in)
+            throws IOException, ClassNotFoundException {
+        // read values
+        in.defaultReadObject();
+
+        if (repository != null) {
+            if (repository.isDirectory()) {
+                                // Check path for nulls
+                if (repository.getPath() != null && repository.getPath().contains("\0")) {
+                    String msg = MessageFormat.format(
+                            rb.getString(LogFacade.REPOSITORY_PATH_CONTAIN_NULL_CHARACTER), repository.getPath());
+                    throw new IOException(msg);
+                }
+            } else {
+                String msg = MessageFormat.format(
+                        rb.getString(LogFacade.REPOSITORY_IS_NOT_A_DIRECTORY), repository.getAbsolutePath());
+                throw new IOException(msg);
+            }
+        }
+
+        try (OutputStream output = getOutputStream()) {
+            if (cachedContent != null) {
+                output.write(cachedContent);
+            } else {
+                if (dfosFile != null && (dfosFile.getPath() == null || dfosFile.getPath().contains("\0"))) {
+                    String msg = MessageFormat.format(
+                      rb.getString(LogFacade.REPOSITORY_PATH_CONTAIN_NULL_CHARACTER), dfosFile.getPath());
+                    throw new IOException(msg);
+                }
+                FileInputStream input = new FileInputStream(dfosFile);
+                Streams.copy(input, output, false);
+                deleteFile(dfosFile);
+                dfosFile = null;
+            }
+        }
+
+        cachedContent = null;
+    }
+
+    /**
+     * Returns the value of the specified mime header as a String.
+     * @param name a String specifying the header name
+     * @return a String containing the value of the requested header,
+     *     or null  if the part does not have a header of that name
+     */
+    public String getHeader(String name) {
+        return headers.getHeader(name);
+    }
+    
+
+    /**
+     * Returns all the values of the specified Part header
+     * @param name - a String specifying the header name
+     * @return a Collection of the values of the requested header.
+     *     If the Part does not have any headers of that name return an
+     *     empty Collection. If the container does not allow access to header
+     *     information, return null
+     */
+    public Collection<String> getHeaders(String name) {
+        List<String> values = headers.getHeaders(name);
+        if (values != Collections.EMPTY_LIST) {
+            values = Collections.unmodifiableList(values);
+        }
+        return values;
+    }
+
+
+    /**
+     * Returns a Collection of all the header names this part contains.
+     * @return a Collection of all the header names sent with this part;
+     *     if the part has no headers, an empty Collection;
+     *     if the servlet container does not allow servlets to use this
+     *     method, null
+     */
+    public Collection<String> getHeaderNames() {
+        return headers.getHeaderNames();
+    }
+
+    private void deleteFile(File file) {
+        if (!file.delete() && log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Cannot delete file: " + file);
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ProgressListener.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ProgressListener.java
new file mode 100644
index 0000000..8075996
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ProgressListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+
+/**
+ * The {@link ProgressListener} may be used to display a progress bar
+ * or do stuff like that.
+ */
+public interface ProgressListener {
+    /** Updates the listeners status information.
+     * @param pBytesRead The total number of bytes, which have been read
+     *   so far.
+     * @param pContentLength The total number of bytes, which are being
+     *   read. May be -1, if this number is unknown.
+     * @param pItems The number of the field, which is currently being
+     *   read. (0 = no item so far, 1 = first item is being read, ...)
+     */
+    void update(long pBytesRead, long pContentLength, int pItems);
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/RequestItem.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/RequestItem.java
new file mode 100644
index 0000000..ce44b3d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/RequestItem.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * <p> This interface provides access to a file or form item that was
+ * received within a <code>multipart/form-data</code> POST request.
+ * The items contents are retrieved by calling {@link #openStream()}.</p>
+ * <p>Instances of this class are created by accessing the
+ * iterator, returned by * {@link RequestItemIterator()}.</p>
+ *
+ * Original authors from org.apache.common.fileupload.FileItem:
+ * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ *
+ * Adopted for Glassfish:
+ * @author Kin-man Chung
+ */
+
+interface RequestItem {
+
+    /** Creates an {@link InputStream}, which allows to read the
+     * items contents.
+     * @return The input stream, from which the items data may
+     *   be read.
+     * @throws IllegalStateException The method was already invoked on
+     * this item. It is not possible to recreate the data stream.
+     * @throws IOException An I/O error occurred.
+     * @see ItemSkippedException
+     */
+    InputStream openStream() throws IOException;
+
+    /**
+     * Closes the file item.
+     * @throws IOException An I/O error occurred.
+     */
+    void close() throws IOException;
+
+    /**
+     * Returns the content type passed by the browser or <code>null</code> if
+     * not defined.
+     *
+     * @return The content type passed by the browser or <code>null</code> if
+     *         not defined.
+     */
+    String getContentType();
+
+    /**
+     * Returns the original filename in the client's filesystem, as provided by
+     * the browser (or other client software). In most cases, this will be the
+     * base file name, without path information. However, some clients, such as
+     * the Opera browser, do include path information.
+     *
+     * @return The original filename in the client's filesystem.
+     */
+    String getSubmittedFileName();
+
+    /**
+     * Returns the name of the field in the multipart form corresponding to
+     * this file item.
+     *
+     * @return The name of the form field.
+     */
+    String getFieldName();
+
+    /**
+     * Determines whether or not a <code>FileItem</code> instance represents
+     * a simple form field.
+     *
+     * @return <code>true</code> if the instance represents a simple form
+     *         field; <code>false</code> if it represents an uploaded file.
+     */
+    boolean isFormField();
+    
+    /**
+     * Returns the item headers.
+     * @return The items header object
+     */
+    public PartHeaders getHeaders();
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/RequestItemIterator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/RequestItemIterator.java
new file mode 100644
index 0000000..4e163ba
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/RequestItemIterator.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import org.apache.catalina.Globals;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Locale;
+import java.util.Map;
+
+import org.glassfish.grizzly.utils.Charsets;
+
+/**
+ * <p>High level API for processing file uploads.</p>
+ *
+ * <p>This class handles multiple forms/files per single HTML widget, sent
+ * using <code>multipart/mixed</code> encoding type, as specified by
+ * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>.
+ *
+ * Original authors from org.apache.common.fileupload:
+ * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @author Sean C. Sullivan
+ *
+ * Adopted for glassfish:
+ * @author Kin-man Chung
+ */
+class RequestItemIterator {
+
+    private static class RequestItemImpl implements RequestItem {
+
+        // The file items content type.
+        private final String contentType;
+
+        // The file items field name.
+        private final String fieldName;
+
+        // The file items file name.
+        private final String submittedFileName;
+
+        // Whether the file item is a form field.
+        private final boolean formField;
+
+        // The file items input stream.
+        private InputStream stream;
+
+        // The headers, if any.
+        private PartHeaders headers;
+
+        /**
+         * Creates a new instance.
+         * @param multipart The multipart instance for accessing global properties
+         * @param multiStream The multi part stream to process
+         * @param pHeaders The item headers
+         * @param pSubmittedFileName The items file name, or null.
+         * @param pFieldName The items field name.
+         * @param pContentType The items content type, or null.
+         * @param pFormField Whether the item is a form field.
+         * @param pContentLength The items content length, if known, or -1
+         * @throws ServletException Creating the file item failed.
+         */
+        RequestItemImpl(Multipart multipart, MultipartStream multiStream,
+                    PartHeaders pHeaders, String pSubmittedFileName, String pFieldName,
+                    String pContentType, boolean pFormField,
+                    long pContentLength) throws ServletException {
+
+            headers = pHeaders;
+            submittedFileName = pSubmittedFileName;
+            fieldName = pFieldName;
+            contentType = pContentType;
+            formField = pFormField;
+
+            stream = multiStream.newInputStream();
+            long fileSizeMax = multipart.getMaxFileSize();
+            if (fileSizeMax != -1) {
+                if (pContentLength != -1) {
+                    if (pContentLength > fileSizeMax) {
+                        throw new ServletException(
+                                "The field " + fieldName
+                                + " exceeds its maximum permitted "
+                                + " size of " + fileSizeMax
+                                + " characters.");
+                    }
+                } else {
+                    stream = new LimitedInputStream(stream, fileSizeMax) {
+                        protected void raiseError(long pSizeMax, long pCount)
+                                throws SizeException {
+                            throw new SizeException(
+                                    "The field " + fieldName
+                                    + " exceeds its maximum permitted "
+                                    + " size of " + pSizeMax
+                                    + " characters.");
+                        }
+                    };
+                }
+            }
+        }
+
+        /**
+         * Returns the items content type, or null.
+         * @return Content type, if known, or null.
+         */
+        public String getContentType() {
+            return contentType;
+        }
+
+        /**
+         * Returns the items field name.
+         * @return Field name.
+         */
+        public String getFieldName() {
+            return fieldName;
+        }
+
+        /**
+         * Returns the items file name.
+         * @return File name, if known, or null.
+         */
+        public String getSubmittedFileName() {
+            return submittedFileName;
+        }
+
+        /**
+         * Returns, whether this is a form field.
+         * @return True, if the item is a form field,
+         *   otherwise false.
+         */
+        public boolean isFormField() {
+            return formField;
+        }
+
+        /**
+         * Returns an input stream, which may be used to
+         * read the items contents.
+         * @return Opened input stream.
+         * @throws IOException An I/O error occurred.
+         */
+        public InputStream openStream() throws IOException {
+            return stream;
+        }
+
+        /**
+         * Closes the file item.
+         * @throws IOException An I/O error occurred.
+         */
+        public void close() throws IOException {
+            stream.close();
+        }
+
+        /**
+         * Returns the file item headers.
+         * @return The items header object
+         */
+        public PartHeaders getHeaders() {
+            return headers;
+        }
+
+        /**
+         * Sets the file item headers.
+         * @param pHeaders The items header object
+         */
+        public void setHeaders(PartHeaders pHeaders) {
+            headers = pHeaders;
+        }
+    }
+
+    private static final String CONTENT_TYPE = "Content-type";
+    private static final String CONTENT_LENGTH = "Content-length";
+    private static final String CONTENT_DISPOSITION = "Content-disposition";
+    private static final String FORM_DATA = "form-data";
+    private static final String ATTACHMENT = "attachment";
+    private static final String MULTIPART = "multipart/";
+    private static final String MULTIPART_FORM_DATA = "multipart/form-data";
+    private static final String MULTIPART_MIXED = "multipart/mixed";
+
+    private static final Charset ISO_8859_1_CHARSET = Charsets.DEFAULT_CHARSET;
+
+    // Multipart instance, for accessing global properties, e.g. maxFileSize
+    private Multipart multipart;
+
+    // The multi part stream to process.
+    private final MultipartStream multiStream;
+
+    // The notifier, which used for triggering the
+    private final MultipartStream.ProgressNotifier notifier;
+
+    // The boundary, which separates the various parts.
+    private final byte[] boundary;
+
+    // The item, which we currently process.
+    private RequestItem currentItem;
+
+    // The current items field name.
+    private String currentFieldName;
+
+    // Whether the current item may still be read.
+    private boolean itemValid;
+ 
+    // Whether we have seen the end of the file.
+    private boolean eof;
+
+    /**
+     * Creates a new instance.
+     * @param request The HttpServletRequest.
+     * @throws FileUploadException An error occurred while
+     *   parsing the request.
+     * @throws ServletException An I/O error occurred.
+     */
+    RequestItemIterator(Multipart multipart, HttpServletRequest request)
+                throws IOException, ServletException {
+
+        this.multipart = multipart;
+        String contentType = request.getContentType();
+        if ((null == contentType)
+                || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {
+            throw new ServletException(
+                    "the request doesn't contain a "
+                    + MULTIPART_FORM_DATA
+                    + " or "
+                    + MULTIPART_MIXED
+                    + " stream, content type header is "
+                    + contentType);
+        }
+
+        InputStream input = request.getInputStream();
+
+        long sizeMax = multipart.getMaxRequestSize();
+        if (sizeMax >= 0) {
+            int requestSize = request.getContentLength();
+            if (requestSize == -1) {
+                input = new LimitedInputStream(input, sizeMax) {
+                    protected void raiseError(long pSizeMax, long pCount)
+                                throws SizeException {
+                        throw new SizeException(
+                                    "the request was rejected because"
+                                    + " its size (" + pCount
+                                    + ") exceeds the configured maximum"
+                                    + " (" + pSizeMax + ")");
+                    }
+                };
+            } else if (requestSize > sizeMax) {
+                throw new ServletException(
+                                "the request was rejected because its size ("
+                                + requestSize
+                                + ") exceeds the configured maximum ("
+                                + sizeMax + ")");
+            }
+        }
+
+        boundary = getBoundary(contentType);
+        if (boundary == null) {
+            throw new ServletException(
+                        "the request was rejected because "
+                        + "no multipart boundary was found");
+        }
+
+        notifier = new MultipartStream.ProgressNotifier(
+                    multipart.getProgressListener(),
+                    request.getContentLength());
+        try {
+            multiStream = new MultipartStream(input, boundary, notifier);
+        } catch (IllegalArgumentException iae) {
+            throw new ServletException ("The boundary specified in the content-type header is too long", iae);
+        }
+        multiStream.setHeaderEncoding(request.getCharacterEncoding());
+
+        findNextItem();
+    }
+
+    /**
+     * Called for finding the nex item, if any.
+     * @return true, if an next item was found, otherwise false.
+     * @throws IOException An I/O error occurred.
+     */
+    private boolean findNextItem() throws IOException, ServletException {
+
+        if (eof) {
+            return false;
+        }
+
+        if (currentItem != null) {
+            currentItem.close();
+            currentItem = null;
+        }
+        for (;;) {
+            if (! multiStream.skipPreamble()) {
+                if (currentFieldName == null) {
+                    // Outer multipart terminated -> No more data
+                    eof = true;
+                    return false;
+                }
+                // Inner multipart terminated -> Return to parsing the outer
+                multiStream.setBoundary(boundary);
+                currentFieldName = null;
+                continue;
+            }
+            PartHeaders headers = getParsedHeaders(multiStream.readHeaders());
+            if (currentFieldName == null) {
+                // We're parsing the outer multipart
+                String fieldName = getFieldName(headers);
+                if (fieldName != null) {
+                    String subContentType = headers.getHeader(CONTENT_TYPE);
+                    if (subContentType != null
+                                &&  subContentType.toLowerCase(Locale.ENGLISH)
+                                        .startsWith(MULTIPART_MIXED)) {
+                        currentFieldName = fieldName;
+                        // Multiple files associated with this field name
+                        byte[] subBoundary = getBoundary(subContentType);
+                        multiStream.setBoundary(subBoundary);
+                        continue;
+                    }
+                    String fileName = getSubmittedFileName(headers);
+                    currentItem = new RequestItemImpl(
+                            multipart, multiStream, headers, fileName,
+                            fieldName, headers.getHeader(CONTENT_TYPE),
+                            fileName == null, getContentLength(headers));
+                    notifier.noteItem();
+                    itemValid = true;
+                    return true;
+                }
+            } else {
+                String fileName = getSubmittedFileName(headers);
+                if (fileName != null) {
+                    currentItem = new RequestItemImpl(
+                            multipart, multiStream, headers, fileName,
+                            currentFieldName,
+                            headers.getHeader(CONTENT_TYPE),
+                            false, getContentLength(headers));
+                    notifier.noteItem();
+                    itemValid = true;
+                    return true;
+                }
+            }
+            multiStream.discardBodyData();
+        }
+    }
+
+    private long getContentLength(PartHeaders pHeaders) {
+        try {
+            return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH));
+        } catch (Exception e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Retrieves the file name from the <code>Content-disposition</code>
+     * header.
+     *
+     * @param headers The HTTP headers object.
+     *
+     * @return The file name for the current <code>encapsulation</code>.
+     */
+    protected String getSubmittedFileName(PartHeaders headers) {
+        return getSubmittedFileName(headers.getHeader(CONTENT_DISPOSITION));
+    }
+
+    /**
+     * Returns the given content-disposition headers file name.
+     * @param pContentDisposition The content-disposition headers value.
+     * @return The file name
+     */
+    private String getSubmittedFileName(String pContentDisposition) {
+        String fileName = null;
+        if (pContentDisposition != null) {
+            String cdl = pContentDisposition.toLowerCase(Locale.ENGLISH);
+            if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) {
+                ParameterParser parser = new ParameterParser();
+                parser.setLowerCaseNames(true);
+                // Parameter parser can handle null input
+                Map<String, String> params =
+                        parser.parse(pContentDisposition, ';');
+                if (params.containsKey("filename")) {
+                    fileName = params.get("filename");
+                    if (fileName != null) {
+                        fileName = fileName.trim();
+                    } else {
+                        // Even if there is no value, the parameter is present,
+                        // so we return an empty file name rather than no file
+                        // name.
+                        fileName = "";
+                    }
+                }
+            }
+        }
+        return fileName;
+    }
+
+
+    /**
+     * Retrieves the field name from the <code>Content-disposition</code>
+     * header.
+     *
+     * @param headers A <code>Map</code> containing the HTTP request headers.
+     *
+     * @return The field name for the current <code>encapsulation</code>.
+     */
+    protected String getFieldName(PartHeaders headers) {
+        return getFieldName(headers.getHeader(CONTENT_DISPOSITION));
+    }
+
+    /**
+     * Returns the field name, which is given by the content-disposition
+     * header.
+     * @param pContentDisposition The content-dispositions header value.
+     * @return The field jake
+     */
+    private String getFieldName(String pContentDisposition) {
+        String fieldName = null;
+        if (pContentDisposition != null
+                && pContentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) {
+            ParameterParser parser = new ParameterParser();
+            parser.setLowerCaseNames(true);
+            // Parameter parser can handle null input
+            Map<String, String> params = parser.parse(pContentDisposition, ';');
+            fieldName = params.get("name");
+            if (fieldName != null) {
+                fieldName = fieldName.trim();
+            }
+        }
+        return fieldName;
+    }
+
+    /**
+     * <p> Parses the <code>header-part</code> and returns as key/value
+     * pairs.
+     *
+     * <p> If there are multiple headers of the same names, the name
+     * will map to a comma-separated list containing the values.
+     *
+     * @param headerPart The <code>header-part</code> of the current
+     *                   <code>encapsulation</code>.
+     *
+     * @return A PartHeaders which hass a <code>Map</code> containing
+     *     the parsed HTTP request headers.
+     */
+    protected PartHeaders getParsedHeaders(String headerPart) {
+
+        final int len = headerPart.length();
+        PartHeaders headers = new PartHeaders();
+        int start = 0;
+        for (;;) {
+            int end = parseEndOfLine(headerPart, start);
+            if (start == end) {
+                break;
+            }
+            StringBuilder header =
+                new StringBuilder(headerPart.substring(start, end));
+            start = end + 2;
+            while (start < len) {
+                int nonWs = start;
+                while (nonWs < len) {
+                    char c = headerPart.charAt(nonWs);
+                    if (c != ' '  &&  c != '\t') {
+                        break;
+                    }
+                    ++nonWs;
+                }
+                if (nonWs == start) {
+                    break;
+                }
+                // Continuation line found
+                end = parseEndOfLine(headerPart, nonWs);
+                header.append(" ").append(headerPart.substring(nonWs, end));
+                start = end + 2;
+            }
+            final int colonOffset = header.indexOf(":");
+            if (colonOffset == -1) {
+                // This header line is malformed, skip it.
+                continue;
+            }
+            String headerName = header.substring(0, colonOffset).trim();
+            String headerValue =
+                header.substring(header.indexOf(":") + 1).trim();
+            headers.addHeader(headerName, headerValue);
+        }
+        return headers;
+    }
+
+    /**
+     * Skips bytes until the end of the current line.
+     * @param headerPart The headers, which are being parsed.
+     * @param end Index of the last byte, which has yet been
+     *   processed.
+     * @return Index of the \r\n sequence, which indicates
+     *   end of line.
+     */
+    private int parseEndOfLine(String headerPart, int end) {
+        int index = end;
+        for (;;) {
+            int offset = headerPart.indexOf('\r', index);
+            if (offset == -1  ||  offset + 1 >= headerPart.length()) {
+                throw new IllegalStateException(
+                    "Expected headers to be terminated by an empty line.");
+            }
+            if (headerPart.charAt(offset + 1) == '\n') {
+                return offset;
+            }
+            index = offset + 1;
+        }
+    }
+
+    /**
+     * Returns, whether another instance of {@link FileItemStream}
+     * is available.
+     * @throws FileUploadException Parsing or processing the
+     *   file item failed.
+     * @throws IOException Reading the file item failed.
+     * @return true, if one or more additional file items
+     *   are available, otherwise false.
+     */
+    public boolean hasNext() throws ServletException, IOException {
+        if (eof) {
+            return false;
+        }
+        if (itemValid) {
+            return true;
+        }
+        return findNextItem();
+    }
+
+    /**
+     * Returns the next available {@link FileItemStream}.
+     * @throws ServletException Parsing or processing the
+     *   file item failed.
+     * @throws IOException Reading the file item failed.
+     * @return RequestItem instance
+     */
+    public RequestItem next() throws ServletException, IOException {
+
+        itemValid = false;
+        return currentItem;
+    }
+
+
+    /**
+     * Retrieves the boundary from the <code>Content-type</code> header.
+     *
+     * @param contentType The value of the content type header from which to
+     *                    extract the boundary value.
+     *
+     * @return The boundary, as a byte array.
+     */
+    protected byte[] getBoundary(String contentType) {
+        ParameterParser parser = new ParameterParser();
+        parser.setLowerCaseNames(true);
+        // Parameter parser can handle null input
+        Map<String, String> params =
+            parser.parse(contentType, new char[] {';', ','});
+        String boundaryStr = params.get("boundary");
+
+        if (boundaryStr == null) {
+            return null;
+        }
+        byte[] boundary;
+        try {
+            boundary = boundaryStr.getBytes(ISO_8859_1_CHARSET);
+        } catch (Exception e) {
+            boundary = boundaryStr.getBytes(Charset.defaultCharset());
+        }
+        return boundary;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/SizeException.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/SizeException.java
new file mode 100644
index 0000000..2fad61a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/SizeException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * This interface is used to throw an IOException when the request or parts
+ * of the multipart exceeds the allowable limited.
+ *
+ * @author: Kin-man Chung
+ */
+package org.apache.catalina.fileupload;
+
+import java.io.IOException;
+
+public class SizeException extends IOException {
+
+    public SizeException(String message) {
+        super(message);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/Streams.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/Streams.java
new file mode 100644
index 0000000..64e758d
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/Streams.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+
+/** Utility class for working with streams.
+ */
+final class Streams {
+    /**
+     * Private constructor, to prevent instantiation.
+     * This class has only static methods.
+     */
+    private Streams() {
+        // Does nothing
+    }
+
+    /**
+     * Default buffer size for use in
+     * {@link #copy(InputStream, OutputStream, boolean)}.
+     */
+    private static final int DEFAULT_BUFFER_SIZE = 8192;
+
+    /**
+     * Copies the contents of the given {@link InputStream}
+     * to the given {@link OutputStream}. Shortcut for
+     * <pre>
+     *   copy(pInputStream, pOutputStream, new byte[8192]);
+     * </pre>
+     * @param pInputStream The input stream, which is being read.
+     * It is guaranteed, that {@link InputStream#close()} is called
+     * on the stream.
+     * @param pOutputStream The output stream, to which data should
+     * be written. May be null, in which case the input streams
+     * contents are simply discarded.
+     * @param pClose True guarantees, that {@link OutputStream#close()}
+     * is called on the stream. False indicates, that only
+     * {@link OutputStream#flush()} should be called finally.
+     *
+     * @return Number of bytes, which have been copied.
+     * @throws IOException An I/O error occurred.
+     */
+    public static long copy(InputStream pInputStream,
+            OutputStream pOutputStream, boolean pClose)
+            throws IOException {
+        return copy(pInputStream, pOutputStream, pClose,
+                new byte[DEFAULT_BUFFER_SIZE]);
+    }
+
+    /**
+     * Copies the contents of the given {@link InputStream}
+     * to the given {@link OutputStream}.
+     * @param pIn The input stream, which is being read.
+     *   It is guaranteed, that {@link InputStream#close()} is called
+     *   on the stream.
+     * @param pOut The output stream, to which data should
+     *   be written. May be null, in which case the input streams
+     *   contents are simply discarded.
+     * @param pClose True guarantees, that {@link OutputStream#close()}
+     *   is called on the stream. False indicates, that only
+     *   {@link OutputStream#flush()} should be called finally.
+     * @param pBuffer Temporary buffer, which is to be used for
+     *   copying data.
+     * @return Number of bytes, which have been copied.
+     * @throws IOException An I/O error occurred.
+     */
+    public static long copy(InputStream pIn,
+            OutputStream pOut, boolean pClose,
+            byte[] pBuffer)
+    throws IOException {
+        OutputStream out = pOut;
+        InputStream in = pIn;
+        try {
+            long total = 0;
+            for (;;) {
+                int res = in.read(pBuffer);
+                if (res == -1) {
+                    break;
+                }
+                if (res > 0) {
+                    total += res;
+                    if (out != null) {
+                        out.write(pBuffer, 0, res);
+                    }
+                }
+            }
+            if (out != null) {
+                if (pClose) {
+                    out.close();
+                } else {
+                    out.flush();
+                }
+                out = null;
+            }
+            in.close();
+            in = null;
+            return total;
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Throwable t) {
+                    /* Ignore me */
+                }
+            }
+            if (pClose  &&  out != null) {
+                try {
+                    out.close();
+                } catch (Throwable t) {
+                    /* Ignore me */
+                }
+            }
+        }
+    }
+
+    /**
+     * This convenience method allows to read a Stream
+     * content into a string. The platform's default character encoding
+     * is used for converting bytes into characters.
+     * @param pStream The input stream to read.
+     * @see #asString(InputStream, String)
+     * @return The streams contents, as a string.
+     * @throws IOException An I/O error occurred.
+     */
+    public static String asString(InputStream pStream) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        copy(pStream, baos, true);
+        return baos.toString();
+    }
+
+    /**
+     * This convenience method allows to read a Stream
+     * content into a string, using the given character encoding.
+     * @param pStream The input stream to read.
+     * @param pEncoding The character encoding, typically "UTF-8".
+     * @see #asString(InputStream)
+     * @return The streams contents, as a string.
+     * @throws IOException An I/O error occurred.
+     */
+    public static String asString(InputStream pStream, String pEncoding)
+            throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        copy(pStream, baos, true);
+        return baos.toString(pEncoding);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ThresholdingOutputStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ThresholdingOutputStream.java
new file mode 100644
index 0000000..4472701
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/fileupload/ThresholdingOutputStream.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * An output stream which triggers an event when a specified number of bytes of
+ * data have been written to it. The event can be used, for example, to throw
+ * an exception if a maximum has been reached, or to switch the underlying
+ * stream type when the threshold is exceeded.
+ * <p>
+ * This class overrides all <code>OutputStream</code> methods. However, these
+ * overrides ultimately call the corresponding methods in the underlying output
+ * stream implementation.
+ * <p>
+ * NOTE: This implementation may trigger the event <em>before</em> the threshold
+ * is actually reached, since it triggers when a pending write operation would
+ * cause the threshold to be exceeded.
+ *
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ *
+ * @version $Id: ThresholdingOutputStream.java 540714 2007-05-22 19:39:44Z niallp $
+ */
+abstract class ThresholdingOutputStream
+    extends OutputStream
+{
+
+    // ----------------------------------------------------------- Data members
+
+
+    /**
+     * The threshold at which the event will be triggered.
+     */
+    private int threshold;
+
+
+    /**
+     * The number of bytes written to the output stream.
+     */
+    private long written;
+
+
+    /**
+     * Whether or not the configured threshold has been exceeded.
+     */
+    private boolean thresholdExceeded;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs an instance of this class which will trigger an event at the
+     * specified threshold.
+     *
+     * @param threshold The number of bytes at which to trigger an event.
+     */
+    public ThresholdingOutputStream(int threshold)
+    {
+        this.threshold = threshold;
+    }
+
+
+    // --------------------------------------------------- OutputStream methods
+
+
+    /**
+     * Writes the specified byte to this output stream.
+     *
+     * @param b The byte to be written.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(int b) throws IOException
+    {
+        checkThreshold(1);
+        getStream().write(b);
+        written++;
+    }
+
+
+    /**
+     * Writes <code>b.length</code> bytes from the specified byte array to this
+     * output stream.
+     *
+     * @param b The array of bytes to be written.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(byte b[]) throws IOException
+    {
+        checkThreshold(b.length);
+        getStream().write(b);
+        written += b.length;
+    }
+
+
+    /**
+     * Writes <code>len</code> bytes from the specified byte array starting at
+     * offset <code>off</code> to this output stream.
+     *
+     * @param b   The byte array from which the data will be written.
+     * @param off The start offset in the byte array.
+     * @param len The number of bytes to write.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(byte b[], int off, int len) throws IOException
+    {
+        checkThreshold(len);
+        getStream().write(b, off, len);
+        written += len;
+    }
+
+
+    /**
+     * Flushes this output stream and forces any buffered output bytes to be
+     * written out.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void flush() throws IOException
+    {
+        getStream().flush();
+    }
+
+
+    /**
+     * Closes this output stream and releases any system resources associated
+     * with this stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void close() throws IOException
+    {
+        try
+        {
+            flush();
+        }
+        catch (IOException ignored)
+        {
+            // ignore
+        }
+        getStream().close();
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Returns the threshold, in bytes, at which an event will be triggered.
+     *
+     * @return The threshold point, in bytes.
+     */
+    public int getThreshold()
+    {
+        return threshold;
+    }
+
+
+    /**
+     * Returns the number of bytes that have been written to this output stream.
+     *
+     * @return The number of bytes written.
+     */
+    public long getByteCount()
+    {
+        return written;
+    }
+
+
+    /**
+     * Determines whether or not the configured threshold has been exceeded for
+     * this output stream.
+     *
+     * @return <code>true</code> if the threshold has been reached;
+     *         <code>false</code> otherwise.
+     */
+    public boolean isThresholdExceeded()
+    {
+        return (written > threshold);
+    }
+
+
+    // ------------------------------------------------------ Protected methods
+
+
+    /**
+     * Checks to see if writing the specified number of bytes would cause the
+     * configured threshold to be exceeded. If so, triggers an event to allow
+     * a concrete implementation to take action on this.
+     *
+     * @param count The number of bytes about to be written to the underlying
+     *              output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected void checkThreshold(int count) throws IOException
+    {
+        if (!thresholdExceeded && (written + count > threshold))
+        {
+            thresholdExceeded = true;
+            thresholdReached();
+        }
+    }
+
+    /**
+     * Resets the byteCount to zero.  You can call this from 
+     * {@link #thresholdReached()} if you want the event to be triggered again. 
+     */
+    protected void resetByteCount() 
+    {
+        this.thresholdExceeded = false;
+        this.written = 0;
+    }
+
+    // ------------------------------------------------------- Abstract methods
+
+
+    /**
+     * Returns the underlying output stream, to which the corresponding
+     * <code>OutputStream</code> methods in this class will ultimately delegate.
+     *
+     * @return The underlying output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected abstract OutputStream getStream() throws IOException;
+
+
+    /**
+     * Indicates that the configured threshold has been reached, and that a
+     * subclass should take whatever action necessary on this event. This may
+     * include changing the underlying output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected abstract void thresholdReached() throws IOException;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/filters/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/filters/Constants.java
new file mode 100644
index 0000000..b0c732b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/filters/Constants.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.filters;
+
+
+/**
+ * Manifest constants for this Java package.
+ *
+ *
+ * @author Craig R. McClanahan
+ * @version $Id: Constants.java 956385 2010-06-20 18:59:51Z markt $
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.filters";
+    
+    public static final String CSRF_NONCE_SESSION_ATTR_NAME =
+        "org.apache.catalina.filters.CSRF_NONCE";
+    
+    public static final String CSRF_NONCE_REQUEST_PARAM =
+        "org.apache.catalina.filters.CSRF_NONCE";
+
+    public static final String METHOD_GET = "GET";
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/filters/CsrfPreventionFilter.java b/appserver/web/web-core/src/main/java/org/apache/catalina/filters/CsrfPreventionFilter.java
new file mode 100644
index 0000000..4cb7fcc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/filters/CsrfPreventionFilter.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2011-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.filters;
+
+import org.apache.catalina.LogFacade;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.security.SecureRandom;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.Logger;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.servlet.http.HttpSession;
+
+/**
+ * Provides basic CSRF protection for a web application. The filter assumes
+ * that:
+ * <ul>
+ * <li>The filter is mapped to /*</li>
+ * <li>{@link HttpServletResponse#encodeRedirectURL(String)} and
+ * {@link HttpServletResponse#encodeURL(String)} are used to encode all URLs
+ * returned to the client
+ * </ul>
+ */
+public class CsrfPreventionFilter extends FilterBase {
+
+    protected static final Logger log = LogFacade.getLogger();
+
+    private String randomClass = SecureRandom.class.getName();
+
+    private Random randomSource;
+
+    private final Set<String> entryPoints = new HashSet<String>();
+
+    private int nonceCacheSize = 5;
+
+    @Override
+    protected Logger getLogger() {
+        return log;
+    }
+
+    /**
+     * Entry points are URLs that will not be tested for the presence of a valid
+     * nonce. They are used to provide a way to navigate back to a protected
+     * application after navigating away from it. Entry points will be limited
+     * to HTTP GET requests and should not trigger any security sensitive
+     * actions.
+     *
+     * @param entryPoints   Comma separated list of URLs to be configured as
+     *                      entry points.
+     */
+    public void setEntryPoints(String entryPoints) {
+        String values[] = entryPoints.split(",");
+        for (String value : values) {
+            this.entryPoints.add(value.trim());
+        }
+    }
+
+    /**
+     * Sets the number of previously issued nonces that will be cached on a LRU
+     * basis to support parallel requests, limited use of the refresh and back
+     * in the browser and similar behaviors that may result in the submission
+     * of a previous nonce rather than the current one. If not set, the default
+     * value of 5 will be used.
+     *
+     * @param nonceCacheSize    The number of nonces to cache
+     */
+    public void setNonceCacheSize(int nonceCacheSize) {
+        this.nonceCacheSize = nonceCacheSize;
+    }
+
+    /**
+     * Specify the class to use to generate the nonces. Must be in instance of
+     * {@link Random}.
+     *
+     * @param randomClass   The name of the class to use
+     */
+    public void setRandomClass(String randomClass) {
+        this.randomClass = randomClass;
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        // Set the parameters
+        super.init(filterConfig);
+
+        String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_CREATE_RANDOM_SOURCE_EXCEPTION), randomClass);
+
+        try {
+            Class<?> clazz = Class.forName(randomClass);
+            randomSource = (Random) clazz.newInstance();
+        } catch (ClassNotFoundException e) {
+
+            ServletException se = new ServletException(msg, e);
+            throw se;
+        } catch (InstantiationException e) {
+            ServletException se = new ServletException(msg, e);
+            throw se;
+        } catch (IllegalAccessException e) {
+            ServletException se = new ServletException(msg, e);
+            throw se;
+        }
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain chain) throws IOException, ServletException {
+
+        ServletResponse wResponse = null;
+
+        if (request instanceof HttpServletRequest &&
+                response instanceof HttpServletResponse) {
+
+            HttpServletRequest req = (HttpServletRequest) request;
+            HttpServletResponse res = (HttpServletResponse) response;
+
+            boolean skipNonceCheck = false;
+
+            if (Constants.METHOD_GET.equals(req.getMethod())) {
+                String path = req.getServletPath();
+                if (req.getPathInfo() != null) {
+                    path = path + req.getPathInfo();
+                }
+
+                if (entryPoints.contains(path)) {
+                    skipNonceCheck = true;
+                }
+            }
+
+            HttpSession session = req.getSession(false);
+
+            @SuppressWarnings("unchecked")
+            LruCache<String> nonceCache = (session == null) ? null
+                    : (LruCache<String>) session.getAttribute(
+                            Constants.CSRF_NONCE_SESSION_ATTR_NAME);
+
+            if (!skipNonceCheck) {
+                String previousNonce =
+                    req.getParameter(Constants.CSRF_NONCE_REQUEST_PARAM);
+
+                if (nonceCache == null || previousNonce == null ||
+                        !nonceCache.contains(previousNonce)) {
+                    res.sendError(HttpServletResponse.SC_FORBIDDEN);
+                    return;
+                }
+            }
+
+            if (nonceCache == null) {
+                nonceCache = new LruCache<String>(nonceCacheSize);
+                if (session == null) {
+                    session = req.getSession(true);
+                }
+                session.setAttribute(
+                        Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
+            }
+
+            String newNonce = generateNonce();
+
+            nonceCache.add(newNonce);
+
+            wResponse = new CsrfResponseWrapper(res, newNonce);
+        } else {
+            wResponse = response;
+        }
+
+        chain.doFilter(request, wResponse);
+    }
+
+
+    @Override
+    protected boolean isConfigProblemFatal() {
+        return true;
+    }
+
+
+    /**
+     * Generate a once time token (nonce) for authenticating subsequent
+     * requests. This will also add the token to the session. The nonce
+     * generation is a simplified version of ManagerBase.generateSessionId().
+     *
+     */
+    protected String generateNonce() {
+        byte random[] = new byte[16];
+
+        // Render the result as a String of hexadecimal digits
+        StringBuilder buffer = new StringBuilder();
+
+        randomSource.nextBytes(random);
+
+        for (int j = 0; j < random.length; j++) {
+            byte b1 = (byte) ((random[j] & 0xf0) >> 4);
+            byte b2 = (byte) (random[j] & 0x0f);
+            if (b1 < 10) {
+                buffer.append((char) ('0' + b1));
+            } else {
+                buffer.append((char) ('A' + (b1 - 10)));
+            }
+            if (b2 < 10) {
+                buffer.append((char) ('0' + b2));
+            } else {
+                buffer.append((char) ('A' + (b2 - 10)));
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    protected static class CsrfResponseWrapper
+            extends HttpServletResponseWrapper {
+
+        private final String nonce;
+
+        public CsrfResponseWrapper(HttpServletResponse response, String nonce) {
+            super(response);
+            this.nonce = nonce;
+        }
+
+        @Override
+        @Deprecated
+        public String encodeRedirectUrl(String url) {
+            return encodeRedirectURL(url);
+        }
+
+        @Override
+        public String encodeRedirectURL(String url) {
+            return addNonce(super.encodeRedirectURL(url));
+        }
+
+        @Override
+        @Deprecated
+        public String encodeUrl(String url) {
+            return encodeURL(url);
+        }
+
+        @Override
+        public String encodeURL(String url) {
+            return addNonce(super.encodeURL(url));
+        }
+
+        /**
+         * Return the specified URL with the nonce added to the query string.
+         *
+         * @param url URL to be modified
+         */
+        private String addNonce(String url) {
+
+            if ((url == null) || (nonce == null)) {
+                return (url);
+            }
+
+            String path = url;
+            String query = "";
+            String anchor = "";
+            int pound = path.indexOf('#');
+            if (pound >= 0) {
+                anchor = path.substring(pound);
+                path = path.substring(0, pound);
+            }
+            int question = path.indexOf('?');
+            if (question >= 0) {
+                query = path.substring(question);
+                path = path.substring(0, question);
+            }
+            StringBuilder sb = new StringBuilder(path);
+            if (query.length() >0) {
+                sb.append(query);
+                sb.append('&');
+            } else {
+                sb.append('?');
+            }
+            sb.append(Constants.CSRF_NONCE_REQUEST_PARAM);
+            sb.append('=');
+            sb.append(nonce);
+            sb.append(anchor);
+            return (sb.toString());
+        }
+    }
+
+    protected static class LruCache<T> implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        // Although the internal implementation uses a Map, this cache
+        // implementation is only concerned with the keys.
+        private final Map<T,T> cache;
+
+        public LruCache(final int cacheSize) {
+            cache = new FixSizeLinkedHashMap<T,T>(cacheSize);
+        }
+
+        public void add(T key) {
+            synchronized (cache) {
+                cache.put(key, null);
+            }
+        }
+
+        public boolean contains(T key) {
+            synchronized (cache) {
+                return cache.containsKey(key);
+            }
+        }
+    }
+
+    private static class FixSizeLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
+        private static final long serialVersionUID = 1L;
+        private int cacheSize;
+
+        private FixSizeLinkedHashMap(int cs) {
+            super();
+            cacheSize = cs;
+        }
+
+        @Override
+        protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
+            if (size() > cacheSize) {
+                return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/filters/FilterBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/filters/FilterBase.java
new file mode 100644
index 0000000..ece7421
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/filters/FilterBase.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.filters;
+
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.LogFacade;
+import org.glassfish.web.util.IntrospectionUtils;
+
+/**
+ * Base class for filters that provides generic initialisation and a simple
+ * no-op destruction.
+ *
+ * @author xxd
+ *
+ */
+public abstract class FilterBase implements Filter {
+
+    protected  static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    protected abstract Logger getLogger();
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        Enumeration<String> paramNames = filterConfig.getInitParameterNames();
+        while (paramNames.hasMoreElements()) {
+            String paramName = paramNames.nextElement();
+            if (!IntrospectionUtils.setProperty(this, paramName,
+                    filterConfig.getInitParameter(paramName))) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.PROPERTY_NOT_DEFINED_EXCEPTION),
+                                                  new Object[] {paramName, this.getClass().getName()});
+                if (isConfigProblemFatal()) {
+                    throw new ServletException(msg);
+                } else {
+                    getLogger().log(Level.WARNING, msg);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void destroy() {
+        // NOOP
+    }
+
+    /**
+     * Determines if an exception when calling a setter or an unknown
+     * configuration attribute triggers the failure of the this filter which in
+     * turn will prevent the web application from starting.
+     *
+     * @return <code>true</code> if a problem should trigger the failure of this
+     *         filter, else <code>false</code>
+     */
+    protected boolean isConfigProblemFatal() {
+        return false;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/loader/StandardClassLoader.java b/appserver/web/web-core/src/main/java/org/apache/catalina/loader/StandardClassLoader.java
new file mode 100644
index 0000000..c3ca026
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/loader/StandardClassLoader.java
@@ -0,0 +1,1037 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.loader;
+
+import org.apache.catalina.LogFacade;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.naming.JndiPermission;
+import org.glassfish.web.loader.Reloader;
+
+import java.io.File;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.*;
+import java.security.*;
+import java.util.*;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Subclass implementation of <b>java.net.URLClassLoader</b> that knows how
+ * to load classes from disk directories, as well as local and remote JAR
+ * files.  It also implements the <code>Reloader</code> interface, to provide
+ * automatic reloading support to the associated loader.
+ * <p>
+ * In all cases, URLs must conform to the contract specified by
+ * <code>URLClassLoader</code> - any URL that ends with a "/" character is
+ * assumed to represent a directory; all other URLs are assumed to be the
+ * address of a JAR file.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
+ * the order they are added via the initial constructor and/or any subsequent
+ * calls to <code>addRepository()</code>.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - At present, there are no dependencies
+ * from this class to any other Catalina class, so that it could be used
+ * independently.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:02 $
+ */
+
+public class StandardClassLoader
+    extends URLClassLoader
+    implements Reloader {
+
+    private static final Logger log = LogFacade.getLogger();
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new ClassLoader with no defined repositories and no
+     * parent ClassLoader.
+     */
+    public StandardClassLoader() {
+
+        super(new URL[0]);
+        this.parent = getParent();
+        this.system = getSystemClassLoader();
+        securityManager = System.getSecurityManager();
+
+    }
+
+
+    /**
+     * Construct a new ClassLoader with no defined repositories and no
+     * parent ClassLoader, but with a stream handler factory.
+     *
+     * @param factory the URLStreamHandlerFactory to use when creating URLs
+     */
+    public StandardClassLoader(URLStreamHandlerFactory factory) {
+
+        super(new URL[0], null, factory);
+        this.factory = factory;
+
+    }
+
+
+    /**
+     * Construct a new ClassLoader with no defined repositories and the
+     * specified parent ClassLoader.
+     *
+     * @param parent The parent ClassLoader
+     */
+    public StandardClassLoader(ClassLoader parent) {
+
+        super((new URL[0]), parent);
+        this.parent = parent;
+        this.system = getSystemClassLoader();
+        securityManager = System.getSecurityManager();
+
+    }
+
+
+    /**
+     * Construct a new ClassLoader with no defined repositories and the
+     * specified parent ClassLoader.
+     *
+     * @param parent The parent ClassLoader
+     * @param factory the URLStreamHandlerFactory to use when creating URLs
+     */
+    public StandardClassLoader(ClassLoader parent,
+                               URLStreamHandlerFactory factory) {
+
+        super((new URL[0]), parent, factory);
+        this.factory = factory;
+
+    }
+
+
+    /**
+     * Construct a new ClassLoader with the specified repositories and
+     * no parent ClassLoader.
+     *
+     * @param repositories The initial set of repositories
+     */
+    public StandardClassLoader(String repositories[]) {
+        this(convert(repositories));
+    }
+
+
+    /**
+     * Construct a new ClassLoader with the specified repositories and
+     * parent ClassLoader.
+     *
+     * @param repositories The initial set of repositories
+     * @param parent The parent ClassLoader
+     */
+    public StandardClassLoader(String repositories[], ClassLoader parent) {
+        this(convert(repositories), parent);
+    }
+
+
+    /**
+     * Construct a new ClassLoader with the specified repositories and
+     * no parent ClassLoader.
+     *
+     * @param repositories The initial set of repositories
+     */
+    public StandardClassLoader(URL repositories[]) {
+
+        super(repositories);
+        this.parent = getParent();
+        this.system = getSystemClassLoader();
+        securityManager = System.getSecurityManager();
+        if (repositories != null) {
+            for (int i = 0; i < repositories.length; i++) {
+                addRepositoryInternal(repositories[i].toString());
+            }
+        }
+    }
+
+
+    /**
+     * Construct a new ClassLoader with the specified repositories and
+     * parent ClassLoader.
+     *
+     * @param repositories The initial set of repositories
+     * @param parent The parent ClassLoader
+     */
+    public StandardClassLoader(URL repositories[], ClassLoader parent) {
+
+        super(repositories, parent);
+        this.parent = parent;
+        this.system = getSystemClassLoader();
+        securityManager = System.getSecurityManager();
+        if (repositories != null) {
+            for (int i = 0; i < repositories.length; i++) {
+                addRepositoryInternal(repositories[i].toString());
+            }
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The debugging detail level of this component.
+     */
+    protected int debug = 0;
+
+
+    /**
+     * 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;
+
+
+    /**
+     * The list of local repositories, in the order they should be searched
+     * for locally loaded classes or resources.
+     */
+    protected ArrayList<String> repositories = new ArrayList<String>();
+
+
+    /**
+     * A list of read File and Jndi Permission's required if this loader
+     * is for a web application context.
+     */
+    private ArrayList<Permission> permissionList = new ArrayList<Permission>();
+
+
+    /**
+     * The PermissionCollection for each CodeSource for a web
+     * application context.
+     */
+    private HashMap<String, PermissionCollection> loaderPC =
+        new HashMap<String, PermissionCollection>();
+
+
+    /**
+     * Instance of the SecurityManager installed.
+     */
+    private SecurityManager securityManager = null;
+
+
+    /**
+     * Flag that the security policy has been refreshed from file.
+     */
+    private boolean policy_refresh = false;
+
+    /**
+     * The parent class loader.
+     */
+    private ClassLoader parent = null;
+
+
+    /**
+     * The system class loader.
+     */
+    private ClassLoader system = null;
+
+
+    /**
+     * URL stream handler for additional protocols.
+     */
+    protected URLStreamHandlerFactory factory = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (this.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 "delegate first" flag for this class loader.
+     */
+    public boolean getDelegate() {
+
+        return (this.delegate);
+
+    }
+
+
+    /**
+     * Set the "delegate first" flag for this class loader.
+     *
+     * @param delegate The new "delegate first" flag
+     */
+    public void setDelegate(boolean delegate) {
+
+        this.delegate = delegate;
+
+    }
+
+
+    /**
+     * If there is a Java SecurityManager create a read FilePermission
+     * or JndiPermission for the file directory path.
+     *
+     * @param path file directory path
+     */
+    protected void setPermissions(String path) {
+        if( securityManager != null ) {
+            if( path.startsWith("jndi:") || path.startsWith("jar:jndi:") ) {
+                permissionList.add(new JndiPermission(path + "*"));
+            } else {
+                permissionList.add(new FilePermission(path + "-","read"));
+            }
+        }
+    }
+
+
+    /**
+     * If there is a Java SecurityManager add a read FilePermission
+     * or JndiPermission for URL.
+     *
+     * @param url URL for a file or directory on local system
+     */
+    protected void setPermissions(URL url) {
+        setPermissions(url.toString());
+    }
+
+
+    // ------------------------------------------------------- Reloader Methods
+
+
+    /**
+     * 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
+     */
+    public void addRepository(String repository) {
+
+        if (debug >= 1)
+            log("addRepository(" + repository + ")");
+
+        // Add this repository to our underlying class loader
+        try {
+            URLStreamHandler streamHandler = null;
+            String protocol = parseProtocol(repository);
+            if (factory != null)
+                streamHandler = factory.createURLStreamHandler(protocol);
+            URL url = new URL(null, repository, streamHandler);
+            super.addURL(url);
+        } catch (MalformedURLException e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Invalid repository: " + repository);
+            iae.initCause(e);
+            throw iae;
+        }
+
+        // Add this repository to our internal list
+        addRepositoryInternal(repository);
+
+    }
+
+
+    /**
+     * This class loader doesn't check for reloading.
+     */
+    public boolean modified() {
+
+        return (false);
+
+    }
+
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("StandardClassLoader\r\n");
+        sb.append("  delegate: ");
+        sb.append(delegate);
+        sb.append("\r\n");
+        sb.append("  repositories:\r\n");
+        Iterator<String> iter = repositories.iterator();
+        while (iter.hasNext()) {
+            sb.append("    ");
+            sb.append(iter.next());
+            sb.append("\r\n");
+        }
+        if (this.parent != null) {
+            sb.append("----------> Parent Classloader:\r\n");
+            sb.append(this.parent.toString());
+            sb.append("\r\n");
+        }
+        return (sb.toString());
+
+    }
+
+
+    // ---------------------------------------------------- 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 {
+
+        if (debug >= 3)
+            log("    findClass(" + name + ")");
+
+        // (1) Permission to define this class when using a SecurityManager
+        if (securityManager != null) {
+            int i = name.lastIndexOf('.');
+            if (i >= 0) {
+                try {
+                    if (debug >= 4)
+                        log("      securityManager.checkPackageDefinition");
+                    securityManager.checkPackageDefinition(name.substring(0,i));
+                } catch (Exception se) {
+                    if (debug >= 4)
+                        log("      -->Exception-->ClassNotFoundException", se);
+                    throw new ClassNotFoundException(name, se);
+                }
+            }
+        }
+
+        // Ask our superclass to locate this class, if possible
+        // (throws ClassNotFoundException if it is not found)
+        Class clazz = null;
+        try {
+            if (debug >= 4)
+                log("      super.findClass(" + name + ")");
+            try {
+                synchronized (this) {
+                    clazz = findLoadedClass(name);
+                    if (clazz != null)
+                        return clazz;
+                    clazz = super.findClass(name);
+                }
+            } catch(AccessControlException ace) {
+                throw new ClassNotFoundException(name, ace);
+            } catch (RuntimeException e) {
+                if (debug >= 4)
+                    log("      -->RuntimeException Rethrown", e);
+                throw e;
+            }
+            if (clazz == null) {
+                if (debug >= 3)
+                    log("    --> Returning ClassNotFoundException");
+                throw new ClassNotFoundException(name);
+            }
+        } catch (ClassNotFoundException e) {
+            if (debug >= 3)
+                log("    --> Passing on ClassNotFoundException", e);
+            throw e;
+        }
+
+        // Return the class we have located
+        if (debug >= 4) {
+            log("      Returning class " + clazz);
+            log("      Loaded by " + clazz.getClassLoader());
+        }
+        return (clazz);
+
+    }
+
+
+    /**
+     * 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(String name) {
+
+        if (debug >= 3)
+            log("    findResource(" + name + ")");
+
+        URL url = super.findResource(name);
+        if (debug >= 3) {
+            if (url != null)
+                log("    --> Returning '" + url.toString() + "'");
+            else
+                log("    --> Resource not found, returning null");
+        }
+        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<URL> findResources(String name) throws IOException {
+
+        if (debug >= 3)
+            log("    findResources(" + name + ")");
+        return (super.findResources(name));
+
+    }
+
+
+    /**
+     * Find the resource with the given name.  A resource is some data
+     * (images, audio, text, etc.) that can be accessed by class code in a
+     * way that is independent of the location of the code.  The name of a
+     * resource is a "/"-separated path name that identifies the resource.
+     * If the resource cannot be found, return <code>null</code>.
+     * <p>
+     * This method searches according to the following algorithm, returning
+     * as soon as it finds the appropriate URL.  If the resource cannot be
+     * found, returns <code>null</code>.
+     * <ul>
+     * <li>If the <code>delegate</code> property is set to <code>true</code>,
+     *     call the <code>getResource()</code> method of the parent class
+     *     loader, if any.</li>
+     * <li>Call <code>findResource()</code> to find this resource in our
+     *     locally defined repositories.</li>
+     * <li>Call the <code>getResource()</code> method of the parent class
+     *     loader, if any.</li>
+     * </ul>
+     *
+     * @param name Name of the resource to return a URL for
+     */
+    public URL getResource(String name) {
+
+        if (debug >= 2)
+            log("getResource(" + name + ")");
+        URL url = null;
+
+        // (1) Delegate to parent if requested
+        if (delegate) {
+            if (debug >= 3)
+                log("  Delegating to parent classloader");
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            url = loader.getResource(name);
+            if (url != null) {
+                if (debug >= 2)
+                    log("  --> Returning '" + url.toString() + "'");
+                return (url);
+            }
+        }
+
+        // (2) Search local repositories
+        if (debug >= 3)
+            log("  Searching local repositories");
+        url = findResource(name);
+        if (url != null) {
+            if (debug >= 2)
+                log("  --> Returning '" + url.toString() + "'");
+            return (url);
+        }
+
+        // (3) Delegate to parent unconditionally if not already attempted
+        if( !delegate ) {
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            url = loader.getResource(name);
+            if (url != null) {
+                if (debug >= 2)
+                    log("  --> Returning '" + url.toString() + "'");
+                return (url);
+            }
+        }
+
+        // (4) Resource was not found
+        if (debug >= 2)
+            log("  --> Resource not found, returning null");
+        return (null);
+
+    }
+
+
+    /**
+     * Find the resource with the given name, and return an input stream
+     * that can be used for reading it.  The search order is as described
+     * for <code>getResource()</code>, after checking to see if the resource
+     * data has been previously cached.  If the resource cannot be found,
+     * return <code>null</code>.
+     *
+     * @param name Name of the resource to return an input stream for
+     */
+    public InputStream getResourceAsStream(String name) {
+
+        if (debug >= 2)
+            log("getResourceAsStream(" + name + ")");
+        InputStream stream = null;
+
+        // (0) Check for a cached copy of this resource
+        stream = findLoadedResource(name);
+        if (stream != null) {
+            if (debug >= 2)
+                log("  --> Returning stream from cache");
+            return (stream);
+        }
+
+        // (1) Delegate to parent if requested
+        if (delegate) {
+            if (debug >= 3)
+                log("  Delegating to parent classloader");
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            stream = loader.getResourceAsStream(name);
+            if (stream != null) {
+                // FIXME - cache???
+                if (debug >= 2)
+                    log("  --> Returning stream from parent");
+                return (stream);
+            }
+        }
+
+        // (2) Search local repositories
+        if (debug >= 3)
+            log("  Searching local repositories");
+        URL url = findResource(name);
+        if (url != null) {
+            // FIXME - cache???
+            if (debug >= 2)
+                log("  --> Returning stream from local");
+            try {
+               return (url.openStream());
+            } catch (IOException e) {
+               log("url.openStream(" + url.toString() + ")", e);
+               return (null);
+            }
+        }
+
+        // (3) Delegate to parent unconditionally
+        if (!delegate) {
+            if (debug >= 3)
+                log("  Delegating to parent classloader");
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            stream = loader.getResourceAsStream(name);
+            if (stream != null) {
+                // FIXME - cache???
+                if (debug >= 2)
+                    log("  --> Returning stream from parent");
+                return (stream);
+            }
+        }
+
+        // (4) Resource was not found
+        if (debug >= 2)
+            log("  --> Resource not found, returning null");
+        return (null);
+
+    }
+
+
+    /**
+     * Load the class with the specified name.  This method searches for
+     * classes in the same manner as <code>loadClass(String, boolean)</code>
+     * with <code>false</code> as the second argument.
+     *
+     * @param name Name of the class to be loaded
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class loadClass(String name) throws ClassNotFoundException {
+
+        return (loadClass(name, false));
+
+    }
+
+
+    /**
+     * 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 {
+
+        if (debug >= 2)
+            log("loadClass(" + name + ", " + resolve + ")");
+        Class clazz = null;
+
+        // (0) Check our previously loaded class cache
+        clazz = findLoadedClass(name);
+        if (clazz != null) {
+            if (debug >= 3)
+                log("  Returning class from cache");
+            if (resolve)
+                resolveClass(clazz);
+            return (clazz);
+        }
+
+        // If a system class, use system class loader
+        if( name.startsWith("java.") ) {
+            ClassLoader loader = system;
+            clazz = loader.loadClass(name);
+            if (clazz != null) {
+                if (resolve)
+                    resolveClass(clazz);
+                return (clazz);
+            }
+            throw new ClassNotFoundException(name);
+        }
+
+        // (.5) Permission to access this class when using a SecurityManager
+        if (securityManager != null) {
+            int i = name.lastIndexOf('.');
+            if (i >= 0) {
+                try {
+                    securityManager.checkPackageAccess(name.substring(0,i));
+                } catch (SecurityException se) {
+                    String error = "Security Violation, attempt to use " +
+                        "Restricted Class: " + name;
+                    log(error);
+                    throw new ClassNotFoundException(error, se);
+                }
+            }
+        }
+
+        // (1) Delegate to our parent if requested
+        if (delegate) {
+            if (debug >= 3)
+                log("  Delegating to parent classloader");
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            try {
+                clazz = loader.loadClass(name);
+                if (clazz != null) {
+                    if (debug >= 3)
+                        log("  Loading class from parent");
+                    if (resolve)
+                        resolveClass(clazz);
+                    return (clazz);
+                }
+            } catch (ClassNotFoundException e) {
+                ;
+            }
+        }
+
+        // (2) Search local repositories
+        if (debug >= 3)
+            log("  Searching local repositories");
+        try {
+            clazz = findClass(name);
+            if (clazz != null) {
+                if (debug >= 3)
+                    log("  Loading class from local repository");
+                if (resolve)
+                    resolveClass(clazz);
+                return (clazz);
+            }
+        } catch (ClassNotFoundException e) {
+            ;
+        }
+
+        // (3) Delegate to parent unconditionally
+        if (!delegate) {
+            if (debug >= 3)
+                log("  Delegating to parent classloader");
+            ClassLoader loader = parent;
+            if (loader == null)
+                loader = system;
+            try {
+                clazz = loader.loadClass(name);
+                if (clazz != null) {
+                    if (debug >= 3)
+                        log("  Loading class from parent");
+                    if (resolve)
+                        resolveClass(clazz);
+                    return (clazz);
+                }
+            } catch (ClassNotFoundException e) {
+                ;
+            }
+        }
+
+        // This class was not found
+        throw new ClassNotFoundException(name);
+
+    }
+
+
+    /**
+     * Get the Permissions for a CodeSource.  If this instance
+     * of StandardClassLoader is for a web application context,
+     * add read FilePermissions for the base directory (if unpacked),
+     * the context URL, and jar file resources.
+     *
+     * @param codeSource where the code was loaded from
+     * @return PermissionCollection for CodeSource
+     */
+    protected final PermissionCollection getPermissions(CodeSource codeSource) {
+        if (!policy_refresh) {
+            // Refresh the security policies
+            Policy policy = Policy.getPolicy();
+            policy.refresh();
+            policy_refresh = true;
+        }
+        String codeUrl = codeSource.getLocation().toString();
+        PermissionCollection pc;
+        if ((pc = loaderPC.get(codeUrl)) == null) {
+            pc = super.getPermissions(codeSource);
+            if (pc != null) {
+                Iterator<Permission> perms = permissionList.iterator();
+                while (perms.hasNext()) {
+                    Permission p = perms.next();
+                    pc.add(p);
+                }
+                loaderPC.put(codeUrl,pc);
+            }
+        }
+        return (pc);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parse URL protocol.
+     *
+     * @return String protocol
+     */
+    protected static String parseProtocol(String spec) {
+        if (spec == null)
+            return "";
+        int pos = spec.indexOf(':');
+        if (pos <= 0)
+            return "";
+        return spec.substring(0, pos).trim();
+    }
+
+
+    /**
+     * Add a repository to our internal array only.
+     *
+     * @param repository The new repository
+     *
+     * @exception IllegalArgumentException if the manifest of a JAR file
+     *  cannot be processed correctly
+     */
+    protected void addRepositoryInternal(String repository) {
+
+        URLStreamHandler streamHandler = null;
+        String protocol = parseProtocol(repository);
+        if (factory != null)
+            streamHandler = factory.createURLStreamHandler(protocol);
+
+        // Validate the manifest of a JAR file repository
+        if (!repository.endsWith(File.separator) &&
+            !repository.endsWith("/")) {
+            JarFile jarFile = null;
+            try {
+                if (repository.startsWith("jar:")) {
+                    URL url = new URL(null, repository, streamHandler);
+                    JarURLConnection conn =
+                        (JarURLConnection) url.openConnection();
+                    conn.setAllowUserInteraction(false);
+                    conn.setDoInput(true);
+                    conn.setDoOutput(false);
+                    conn.connect();
+                    jarFile = conn.getJarFile();
+                } else if (repository.startsWith("file://")) {
+                    jarFile = new JarFile(repository.substring(7));
+                } else if (repository.startsWith("file:")) {
+                    jarFile = new JarFile(repository.substring(5));
+                } else if (repository.endsWith(".jar")) {
+                    URL url = new URL(null, repository, streamHandler);
+                    URLConnection conn = url.openConnection();
+                    JarInputStream jis =
+                        new JarInputStream(conn.getInputStream());
+                    try {
+                        jis.getManifest();
+                    } finally {
+                        try {
+                            jis.close();
+                        } catch(Throwable t) {
+                        }
+                    }
+                } else {
+                    throw new IllegalArgumentException
+                        ("addRepositoryInternal:  Invalid URL '" +
+                         repository + "'");
+                }
+            } catch (Throwable t) {
+                IllegalArgumentException iae = new IllegalArgumentException
+                    ("addRepositoryInternal");
+                iae.initCause(t);
+                throw iae;
+            } finally {
+                if (jarFile != null) {
+                    try {
+                        jarFile.close();
+                    } catch (Throwable t) {}
+                }
+            }
+        }
+
+        // Add this repository to our internal list
+        repositories.add(repository);
+    }
+
+
+    /**
+     * Convert an array of String to an array of URL and return it.
+     *
+     * @param input The array of String to be converted
+     */
+    protected static URL[] convert(String input[]) {
+        return convert(input, null);
+    }
+
+
+    /**
+     * Convert an array of String to an array of URL and return it.
+     *
+     * @param input The array of String to be converted
+     * @param factory Handler factory to use to generate the URLs
+     */
+    protected static URL[] convert(String input[],
+                                   URLStreamHandlerFactory factory) {
+
+        URLStreamHandler streamHandler = null;
+
+        URL url[] = new URL[input.length];
+        for (int i = 0; i < url.length; i++) {
+            try {
+                String protocol = parseProtocol(input[i]);
+                if (factory != null)
+                    streamHandler = factory.createURLStreamHandler(protocol);
+                else
+                    streamHandler = null;
+                url[i] = new URL(null, input[i], streamHandler);
+            } catch (MalformedURLException e) {
+                url[i] = null;
+            }
+        }
+        return (url);
+
+    }
+
+
+    /**
+     * Finds the resource with the given name if it has previously been
+     * loaded and cached by this class loader, and return an input stream
+     * to the resource data.  If this resource has not been cached, return
+     * <code>null</code>.
+     *
+     * @param name Name of the resource to return
+     */
+    protected InputStream findLoadedResource(String name) {
+
+        return (null);  // FIXME - findLoadedResource()
+
+    }
+
+
+    /**
+     * Log a debugging output message.
+     *
+     * @param message Message to be logged
+     */
+    private void log(String message) {
+        message = neutralizeForLog(message);
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "StandardClassLoader: " + message);
+
+    }
+
+
+    /**
+     * Log a debugging output message with an exception.
+     *
+     * @param message Message to be logged
+     * @param throwable Exception to be logged
+     */
+    private void log(String message, Throwable throwable) {
+        message = neutralizeForLog(message);
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "StandardClassLoader: " + message, throwable);
+
+    }
+
+
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/loader/WebappLoader.java b/appserver/web/web-core/src/main/java/org/apache/catalina/loader/WebappLoader.java
new file mode 100644
index 0000000..96fc624
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/loader/WebappLoader.java
@@ -0,0 +1,1291 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.loader;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.util.LifecycleSupport;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.util.StringManager;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
+import org.apache.naming.resources.Resource;
+import org.glassfish.web.loader.WebappClassLoader;
+
+import javax.management.ObjectName;
+import javax.naming.Binding;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletContext;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLStreamHandlerFactory;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Classloader implementation which is specialized for handling web
+ * applications in the most efficient way, while being Catalina aware (all
+ * accesses to resources are made through the DirContext interface).
+ * This class loader supports detection of modified
+ * Java classes, which can be used to implement auto-reload support.
+ * <p>
+ * This class loader is configured by adding the pathnames of directories,
+ * JAR files, and ZIP files with the <code>addRepository()</code> method,
+ * prior to calling <code>start()</code>.  When a new class is required,
+ * these repositories will be consulted first to locate the class.  If it
+ * is not present, the system class loader will be used instead.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.10 $ $Date: 2007/05/05 05:32:09 $
+ */
+
+public class WebappLoader
+    implements Lifecycle, Loader, PropertyChangeListener  {
+
+    /**
+     * First load of the class.
+     */
+    private static boolean first = true;
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+    // --------------------------------------------------------- Constructors
+
+    /**
+     * Construct a new WebappLoader with no defined parent class loader
+     * (so that the actual parent will be the system class loader).
+     */
+    public WebappLoader() {
+
+        this(null);
+
+    }
+
+
+    /**
+     * Construct a new WebappLoader with the specified class loader
+     * to be defined as the parent of the ClassLoader we ultimately create.
+     *
+     * @param parent The parent class loader
+     */
+    public WebappLoader(ClassLoader parent) {
+        super();
+        this.parentClassLoader = parent;
+    }
+
+
+    // --------------------------------------------------- Instance Variables
+
+    private ObjectName oname;
+    private ObjectName controller;
+
+    /**
+     * The class loader being managed by this Loader component.
+     */
+    private WebappClassLoader classLoader = null;
+
+
+    /**
+     * The Container with which this Loader has been associated.
+     */
+    private Container container = null;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The "follow standard delegation model" flag that will be used to
+     * configure our ClassLoader.
+     */
+    private boolean delegate = false;
+
+
+    /**
+     * The descriptive information about this Loader implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.loader.WebappLoader/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The Java class name of the ClassLoader implementation to be used.
+     * This class should extend WebappClassLoader, otherwise, a different 
+     * loader implementation must be used.
+     */
+    private String loaderClass =
+        "org.glassfish.web.loader.WebappClassLoader";
+
+
+    /**
+     * The parent class loader of the class loader we will create.
+     */
+    private ClassLoader parentClassLoader = null;
+
+
+    /**
+     * The reloadable flag for this Loader.
+     */
+    private boolean reloadable = false;
+
+
+    /**
+     * The set of repositories associated with this class loader.
+     */
+    private String repositories[] = new String[0];
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(WebappLoader.class.getPackage().getName());
+
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * Classpath set in the loader.
+     */
+    private String classpath = null;
+
+
+    // START PE 4985680`
+    /**
+     * List of packages that may always be overridden, regardless of whether
+     * they belong to a protected namespace (i.e., a namespace that may never
+     * be overridden by a webapp)  
+     */
+    private ArrayList<String> overridablePackages;
+    // END PE 4985680
+
+
+    // START PWC 1.1 6314481
+    private boolean ignoreHiddenJarFiles;
+    // END PWC 1.1 6314481
+    
+    private boolean useMyFaces;
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Java class loader to be used by this Container.
+     */
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+
+    /**
+     * Return the Container with which this Logger has been associated.
+     */
+    public Container getContainer() {
+        return (container);
+    }
+
+
+    /**
+     * Set the Container with which this Logger has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container) {
+
+        // Deregister from the old Container (if any)
+        if ((this.container != null) && (this.container instanceof Context))
+            ((Context) this.container).removePropertyChangeListener(this);
+
+        // Process this property change
+        Container oldContainer = this.container;
+        this.container = container;
+        support.firePropertyChange("container", oldContainer, this.container);
+
+        // Register with the new Container (if any)
+        if (this.container instanceof Context) {
+            setReloadable( ((Context) this.container).getReloadable() );
+            ((Context) this.container).addPropertyChangeListener(this);
+        }
+    }
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+        return (this.debug);
+    }
+
+
+    /**
+     * Set the debugging detail level for this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+        int oldDebug = this.debug;
+        this.debug = debug;
+        support.firePropertyChange("debug", Integer.valueOf(oldDebug),
+                                   Integer.valueOf(this.debug));
+    }
+
+
+    /**
+     * 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;
+        support.firePropertyChange("delegate", Boolean.valueOf(oldDelegate),
+                                   Boolean.valueOf(this.delegate));
+    }
+
+
+    /**
+     * Return descriptive information about this Loader implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+
+    /**
+     * Return the ClassLoader class name.
+     */
+    public String getLoaderClass() {
+        return (this.loaderClass);
+    }
+
+
+    /**
+     * Set the ClassLoader class name.
+     *
+     * @param loaderClass The new ClassLoader class name
+     */
+    public void setLoaderClass(String loaderClass) {
+        this.loaderClass = loaderClass;
+    }
+
+
+    /**
+     * Return the reloadable flag for this Loader.
+     */
+    public boolean getReloadable() {
+        return (this.reloadable);
+    }
+
+
+    /**
+     * Set the reloadable flag for this Loader.
+     *
+     * @param reloadable The new reloadable flag
+     */
+    public void setReloadable(boolean reloadable) {
+
+        // Process this property change
+        boolean oldReloadable = this.reloadable;
+        this.reloadable = reloadable;
+        support.firePropertyChange("reloadable",
+                                   Boolean.valueOf(oldReloadable),
+                                   Boolean.valueOf(this.reloadable));
+    }
+
+
+    public void setUseMyFaces(boolean useMyFaces) {
+        this.useMyFaces = useMyFaces;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        support.addPropertyChangeListener(listener);
+    }
+
+
+    /**
+     * Add a new repository to the set of repositories for this class loader.
+     *
+     * @param repository Repository to be added
+     */
+    public void addRepository(String repository) {
+
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Adding repository " + repository);
+
+        for (int i = 0; i < repositories.length; i++) {
+            if (repository.equals(repositories[i]))
+                return;
+        }
+        String results[] = new String[repositories.length + 1];
+        for (int i = 0; i < repositories.length; i++)
+            results[i] = repositories[i];
+        results[repositories.length] = repository;
+        repositories = results;
+
+        if (started && (classLoader != null)) {
+            classLoader.addRepository(repository);
+            setClassPath();
+        }
+
+    }
+
+
+    /**
+     * Return the set of repositories defined for this class loader.
+     * If none are defined, a zero-length array is returned.
+     * For security reason, returns a clone of the Array (since 
+     * String are immutable).
+     */
+    public String[] findRepositories() {
+        return repositories.clone();
+    }
+
+    public String[] getRepositories() {
+        return repositories.clone();
+    }
+
+
+    /** 
+     * Classpath, as set in org.apache.catalina.jsp_classpath context
+     * property
+     *
+     * @return The classpath
+     */
+    public String getClasspath() {
+        return classpath;
+    }
+
+
+    /**
+     * Has the internal repository associated with this Loader been modified,
+     * such that the loaded classes should be reloaded?
+     */
+    public boolean modified() {
+        return (classLoader.modified());
+    }
+
+
+    /**
+     * Used to periodically signal to the classloader to release JAR resources.
+     */
+    public void closeJARs(boolean force) {
+        if (classLoader !=null){
+            classLoader.closeJARs(force);
+        }
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        support.removePropertyChangeListener(listener);
+    }
+
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+        StringBuilder sb = new StringBuilder("WebappLoader[");
+        if (container != null)
+            sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this WebappLoader.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+    private boolean initialized=false;
+
+    public void init() {
+        initialized=true;
+
+        if( oname==null ) {
+            // not registered yet - standalone or API
+            if( container instanceof StandardContext) {
+                // Register ourself. The container must be a webapp
+                try {
+                    StandardContext ctx=(StandardContext)container;
+                    String path = ctx.getEncodedPath();
+                    if (path.equals("")) {
+                        path = "/";
+                    }   
+                    oname = new ObjectName(ctx.getEngineName() +
+                                           ":type=Loader,path=" +
+                                           path + ",host=" +
+                                           ctx.getParent().getName());
+                    controller = oname;
+                } catch (Exception e) {
+                    log.log(Level.SEVERE, LogFacade.REGISTERING_LOADER_EXCEPTION, e);
+                }
+            }
+        }
+
+        /*
+        if( container == null ) {
+            // JMX created the loader
+            // TODO
+        }
+        */
+    }
+
+    public void destroy() {
+        if( controller==oname ) {
+            oname = null;
+        }
+        initialized = false;
+
+    }
+
+    private static synchronized void initStreamHandlerFactory() {
+        // Register a stream handler factory for the JNDI protocol
+        URLStreamHandlerFactory streamHandlerFactory =
+            new DirContextURLStreamHandlerFactory();
+
+        synchronized (WebappLoader.class) {
+            if (first) {
+                first = false;
+                try {
+                    URL.setURLStreamHandlerFactory(streamHandlerFactory);
+                } catch (Exception e) {
+                    // Log and continue anyway, this is not critical
+                    log.log(Level.SEVERE, LogFacade.REGISTERING_JNDI_STREAM_HANDLER_EXCEPTION, e);
+                } catch (Throwable t) {
+                    // This is likely a dual registration
+                    if (log.isLoggable(Level.FINE)) {
+                        log.log(Level.FINE, "Dual registration of jndi stream handler: " +
+                                t.getMessage());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Start this component, initializing our associated class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    public void start() throws LifecycleException {
+        // Validate and update our current component state
+        if( ! initialized ) init();
+        if (started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.LOADER_ALREADY_STARTED_EXCEPTION));
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Starting this Loader");
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        if (container.getResources() == null) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.NO_RESOURCE_INFO, container);
+            }
+            return;
+        }
+        // Register a stream handler factory for the JNDI protocol
+        initStreamHandlerFactory();
+
+        // Construct a class loader based on our current repositories list
+        try {
+
+            final ClassLoader cl = createClassLoader();
+            if (cl instanceof WebappClassLoader) {
+                classLoader = (WebappClassLoader) cl;
+            } else {
+                classLoader = AccessController.doPrivileged(new PrivilegedAction<WebappClassLoader>() {
+                    @Override
+                    public WebappClassLoader run() {
+                        return new WebappClassLoader(cl);
+                    }
+                });
+            }
+            classLoader.setResources(container.getResources());
+            classLoader.setDebug(this.debug);
+            classLoader.setDelegate(this.delegate);
+
+            for (int i = 0; i < repositories.length; i++) {
+                classLoader.addRepository(repositories[i]);
+            }
+            
+            // START OF PE 4985680
+            if (overridablePackages != null){
+                for (int i = 0; i < overridablePackages.size(); i++) {
+                    classLoader.addOverridablePackage(
+                                            overridablePackages.get(i));
+                }
+                overridablePackages = null;
+            }
+            // END OF PE 4985680
+
+            // Configure our repositories
+            setRepositories();
+            setClassPath();
+
+            setPermissions();
+
+            // Binding the Webapp class loader to the directory context
+            DirContextURLStreamHandler.bind(classLoader,
+                    this.container.getResources());
+
+        } catch (Throwable t) {
+            log.log(Level.SEVERE, LogFacade.LIFECYCLE_EXCEPTION, t);
+            throw new LifecycleException("start: ", t);
+        }
+
+    }
+
+
+    /**
+     * Stop this component, finalizing our associated class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.LOADER_NOT_STARTED_EXCEPTION));
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Stopping this Loader");
+
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Remove context attributes as appropriate
+        if (container instanceof Context) {
+            ServletContext servletContext =
+                ((Context) container).getServletContext();
+            servletContext.removeAttribute(Globals.CLASS_PATH_ATTR);
+        }
+
+        // Throw away our current class loader
+        stopNestedClassLoader();
+        DirContextURLStreamHandler.unbind(classLoader);
+
+        classLoader = null;
+
+        destroy();
+    }
+
+
+    /**
+     * Stops the nested classloader
+     */
+    public void stopNestedClassLoader() throws LifecycleException {
+        try {
+            classLoader.stop();
+        } catch (Exception e) {
+            throw new LifecycleException(e);
+        }
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Process property change events from our associated Context.
+     *
+     * @param event The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        // Validate the source of this event
+        if (!(event.getSource() instanceof Context))
+            return;
+
+        // Process a relevant property change
+        String propName = event.getPropertyName();
+        if ("reloadable".equals(propName)) {
+            try {
+                setReloadable
+                    ( ((Boolean) event.getNewValue()).booleanValue() );
+            } catch (NumberFormatException e) {
+                log.log(Level.SEVERE, LogFacade.SET_RELOADABLE_PROPERTY_EXCEPTION, neutralizeForLog(event.getNewValue().toString()));
+            }
+        } else if ("antiJARLocking".equals(propName)) {
+            ClassLoader cloader = Thread.currentThread().getContextClassLoader();
+            if (cloader instanceof WebappClassLoader) {
+                ((WebappClassLoader)cloader).setAntiJARLocking(
+                        ((Boolean)event.getNewValue()).booleanValue());
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------- Private Methods
+
+
+    /**
+     * Create associated classLoader.
+     */
+    protected ClassLoader createClassLoader()
+        throws Exception {
+
+        Class<?> clazz = Class.forName(loaderClass);
+        WebappClassLoader classLoader = null;
+
+        if (parentClassLoader == null) {
+            parentClassLoader = Thread.currentThread().getContextClassLoader();
+        }
+        Class<?>[] argTypes = { ClassLoader.class };
+        Object[] args = { parentClassLoader };
+        Constructor<?> constr = clazz.getConstructor(argTypes);
+        classLoader = (WebappClassLoader) constr.newInstance(args);
+
+        classLoader.setUseMyFaces(useMyFaces);
+
+        /*
+         * Start the WebappClassLoader here as opposed to in the course of
+         * WebappLoader#start, in order to prevent it from being started
+         * twice (during normal deployment, the WebappClassLoader is created
+         * by the deployment backend without calling
+         * WebappLoader#createClassLoader, and will have been started
+         * by the time WebappLoader#start is called)
+         */
+        try {
+            classLoader.start();
+        } catch (Exception e) {
+            throw new LifecycleException(e);
+        }
+
+        return classLoader;
+
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     *
+    private void log(String message) {
+        org.apache.catalina.Logger logger = null;
+        String containerName = null;
+        if (container != null) {
+            logger = container.getLogger();
+            containerName = container.getName();
+        }
+        if (logger != null) {
+            logger.log("WebappLoader[" + containerName + "]: " +
+                message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.info("WebappLoader[" + containerName + "]: " + message);
+            }
+        }
+    }
+    */
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    private void log(String message, Throwable t) {
+        org.apache.catalina.Logger logger = null;
+        String containerName = null;
+
+
+        if (container != null) {
+            logger = container.getLogger();
+            containerName = container.getName();
+        }
+        if (logger != null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.WEB_APP_LOADER_EXCEPTION),
+                    new Object[] {containerName, message});
+            logger.log(neutralizeForLog(msg), t);
+        } else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.WEB_APP_LOADER_EXCEPTION),
+                    new Object[] {containerName, message});
+            log.log(Level.WARNING, neutralizeForLog(msg), t);
+        }
+    }
+
+
+    /**
+     * Configure associated class loader permissions.
+     */
+    private void setPermissions() {
+        
+        if (!Globals.IS_SECURITY_ENABLED)
+            return;
+        if (!(container instanceof Context))
+            return;
+
+        try {
+            AccessController.doPrivileged(
+                  new PrivilegedExceptionAction<Object>() {
+                    public Object run() throws SecurityException {
+                        setPermissions_priv();
+                        return null;
+                    }
+                });
+            } catch (PrivilegedActionException e) {
+                throw (SecurityException ) e.getException();
+            }        
+    }
+    
+    
+    private void setPermissions_priv() {
+
+
+        // Tell the class loader the root of the context
+        ServletContext servletContext =
+            ((Context) container).getServletContext();
+
+        // Assigning permissions for the work directory
+        File workDir =
+            (File) servletContext.getAttribute(ServletContext.TEMPDIR);
+        if (workDir != null) {
+            try {
+                String workDirPath = workDir.getCanonicalPath();
+                classLoader.addPermission
+                    (new FilePermission(workDirPath, "read,write"));
+                classLoader.addPermission
+                    (new FilePermission(workDirPath + File.separator + "-", 
+                                        "read,write,delete"));
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+
+        try {
+
+            URL rootURL = servletContext.getResource("/");
+            classLoader.addPermission(rootURL);
+
+            String contextRoot = servletContext.getRealPath("/");
+            if (contextRoot != null) {
+                try {
+                    contextRoot = (new File(contextRoot)).getCanonicalPath();
+                    classLoader.addPermission(contextRoot);
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+
+            URL classesURL = servletContext.getResource("/WEB-INF/classes/");
+            classLoader.addPermission(classesURL);
+            URL libURL = servletContext.getResource("/WEB-INF/lib/");
+            classLoader.addPermission(libURL);
+
+            if (contextRoot != null) {
+
+                if (libURL != null) {
+                    File rootDir = new File(contextRoot);
+                    File libDir = new File(rootDir, "WEB-INF/lib/");
+                    try {
+                        String path = libDir.getCanonicalPath();
+                        classLoader.addPermission(path);
+                    } catch (IOException e) {
+                    }
+                }
+
+            } else {
+
+                if (workDir != null) {
+                    if (libURL != null) {
+                        File libDir = new File(workDir, "WEB-INF/lib/");
+                        try {
+                            String path = libDir.getCanonicalPath();
+                            classLoader.addPermission(path);
+                        } catch (IOException e) {
+                        }
+                    }
+                    if (classesURL != null) {
+                        File classesDir = new File(workDir, "WEB-INF/classes/");
+                        try {
+                            String path = classesDir.getCanonicalPath();
+                            classLoader.addPermission(path);
+                        } catch (IOException e) {
+                        }
+                    }
+                }
+
+            }
+
+        } catch (MalformedURLException e) {
+        }
+
+    }
+
+
+    /**
+     * Configure the repositories for our class loader, based on the
+     * associated Context.
+     */
+    private void setRepositories() throws IOException {
+
+        if (!(container instanceof Context))
+            return;
+        ServletContext servletContext =
+            ((Context) container).getServletContext();
+        if (servletContext == null)
+            return;
+
+        // Loading the work directory
+        File workDir =
+            (File) servletContext.getAttribute(ServletContext.TEMPDIR);
+        if (workDir == null) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.NO_WORK_DIR_INFO, servletContext);
+            }
+        }
+
+        if (log.isLoggable(Level.FINEST) && workDir != null)
+            log.log(Level.FINEST, "Deploying class repositories to work directory"
+                    + workDir.getAbsolutePath());
+
+        DirContext resources = container.getResources();
+
+        // Setting up the class repository (/WEB-INF/classes), if it exists
+
+        String classesPath = "/WEB-INF/classes";
+        DirContext classes = null;
+
+        try {
+            Object object = resources.lookup(classesPath);
+            if (object instanceof DirContext) {
+                classes = (DirContext) object;
+            }
+        } catch(NamingException e) {
+            // Silent catch: it's valid that no /WEB-INF/classes collection
+            // exists
+        }
+
+        if (classes != null) {
+
+            File classRepository = null;
+
+            String absoluteClassesPath =
+                servletContext.getRealPath(classesPath);
+
+            if (absoluteClassesPath != null) {
+
+                classRepository = new File(absoluteClassesPath);
+
+            } else {
+
+                classRepository = new File(workDir, classesPath);
+                if (!classRepository.mkdirs() &&
+                        !classRepository.isDirectory()) {
+                    throw new IOException(rb.getString(LogFacade.FAILED_CREATE_DEST_DIR));
+                }
+                if (!copyDir(classes, classRepository)) {
+                    throw new IOException(rb.getString(LogFacade.FAILED_COPY_RESOURCE));
+                }
+
+            }
+
+            if (log.isLoggable(Level.FINEST))
+                log.log(Level.FINEST, "Deploy class files "
+                        +classesPath+" to "
+                        + classRepository.getAbsolutePath());
+        }
+
+        // Setting up the JAR repository (/WEB-INF/lib), if it exists
+
+        String libPath = "/WEB-INF/lib";
+
+        classLoader.setJarPath(libPath);
+
+        DirContext libDir = null;
+        // Looking up directory /WEB-INF/lib in the context
+        try {
+            Object object = resources.lookup(libPath);
+            if (object instanceof DirContext)
+                libDir = (DirContext) object;
+        } catch(NamingException e) {
+            // Silent catch: it's valid that no /WEB-INF/lib collection
+            // exists
+        }
+
+        if (libDir != null) {
+
+            boolean copyJars = false;
+            String absoluteLibPath = servletContext.getRealPath(libPath);
+
+            File destDir = null;
+
+            if (absoluteLibPath != null) {
+                destDir = new File(absoluteLibPath);
+            } else {
+                copyJars = true;
+                destDir = new File(workDir, libPath);
+                if (!destDir.mkdirs() && !destDir.isDirectory()) {
+                    log.log(Level.SEVERE, LogFacade.FAILED_CREATE_WORK_DIR_EXCEPTION, destDir.getAbsolutePath());
+                }
+            }
+
+            if (!copyJars) {
+                return;
+            }
+
+            // Looking up directory /WEB-INF/lib in the context
+            try {
+                NamingEnumeration<Binding> enumeration =
+                    resources.listBindings(libPath);
+                while (enumeration.hasMoreElements()) {
+
+                    Binding binding = enumeration.nextElement();
+                    String filename = libPath + "/" + binding.getName();
+                    // START OF IASRI 4657979
+                    if (!filename.endsWith(".jar") &&
+                        !filename.endsWith(".zip"))
+                    // END OF IASRI 4657979
+                        continue;
+
+                    // START PWC 1.1 6314481
+                    if (binding.getName() != null
+                            && binding.getName().startsWith(".")
+                            && ignoreHiddenJarFiles) {
+                        continue;
+                    }
+                    // END PWC 1.1 6314481
+
+                    File destFile = new File(destDir, binding.getName());
+
+                    if (log.isLoggable(Level.FINEST)) {
+                        log.log(Level.FINEST, "Deploy JAR "+filename+" to " + destFile.getAbsolutePath());
+                    }
+
+                    Object obj = binding.getObject();
+
+                    if (!(obj instanceof Resource))
+                        continue;
+
+                    Resource jarResource = (Resource) obj;
+
+                    try (FileOutputStream fos = new FileOutputStream(destFile)) {
+                        if (!copy(jarResource.streamContent(), fos)) {
+                            continue;
+                        }
+                    }
+                }
+            } catch (NamingException e) {
+                // Silent catch: it's valid that no /WEB-INF/lib directory
+                // exists
+            } catch (IOException e) {
+                log("Unable to configure repositories", e);
+            }
+        }
+    }
+
+
+    /**
+     * Set the appropriate context attribute for our class path.  This
+     * is required only because Jasper depends on it.
+     */
+    private void setClassPath() {
+
+        // Validate our current state information
+        if (!(container instanceof Context))
+            return;
+        ServletContext servletContext =
+            ((Context) container).getServletContext();
+        if (servletContext == null)
+            return;
+
+        if (container instanceof StandardContext) {
+            String baseClasspath = 
+                ((StandardContext) container).getCompilerClasspath();
+            if (baseClasspath != null) {
+                servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+                                            baseClasspath);
+                return;
+            }
+        }
+
+        StringBuilder classpath = new StringBuilder();
+
+        // Assemble the class path information from our class loader chain
+        ClassLoader loader = getClassLoader();
+        boolean first = true;
+        while (loader != null) {
+            if (!(loader instanceof URLClassLoader)) {
+                String cp = getClasspath(loader);
+                if (cp != null) {
+                    if (!first) {
+                        classpath.append(File.pathSeparator);
+                    } else {
+                        first = false;
+                    }
+                    classpath.append(cp);
+                }
+            } else {
+                URL[] repositories = ((URLClassLoader) loader).getURLs();
+                for (int i = 0; i < repositories.length; i++) {
+                    if (repositories[i] == null) {
+                        continue;
+                    }
+                    String repository = repositories[i].toString();
+                    if (repository.startsWith("file://")) {
+                        repository = repository.substring(7);
+                    } else if (repository.startsWith("file:")) {
+                        repository = repository.substring(5);
+                    } else if (repository.startsWith("jndi:")) {
+                        repository = servletContext.getRealPath(
+                            repository.substring(5));
+                    } else {
+                        continue;
+                    }
+                    if (!repository.isEmpty()) {
+                        if (!first) {
+                            classpath.append(File.pathSeparator);
+                        } else {
+                            first = false;
+                        }
+                        classpath.append(repository);
+                    }
+                }
+            }
+
+            loader = loader.getParent();
+        }
+
+        this.classpath = classpath.toString();
+
+        // Store the assembled class path as a servlet context attribute
+        servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+                                    classpath.toString());
+
+    }
+
+    // try to extract the classpath from a loader that is not URLClassLoader
+    private String getClasspath( ClassLoader loader ) {
+        try {
+            Method m=loader.getClass().getMethod("getClasspath", new Class[] {});
+            if (log.isLoggable(Level.FINEST))
+                log.log(Level.FINEST, "getClasspath " + m);
+            Object o=m.invoke( loader, new Object[] {} );
+            if (log.isLoggable(Level.FINEST))
+                log.log(Level.FINEST, "gotClasspath " + o);
+            if (o instanceof String )
+                return (String)o;
+            return null;
+        } catch (Exception ex) {
+            if (log.isLoggable(Level.FINEST))
+                log.log(Level.FINEST, "getClasspath ", ex);
+        }
+        return null;
+    }
+
+    /**
+     * Copy directory.
+     */
+    private boolean copyDir(DirContext srcDir, File destDir) {
+
+        try {
+
+            NamingEnumeration<NameClassPair> enumeration = srcDir.list("");
+            while (enumeration.hasMoreElements()) {
+                NameClassPair ncPair = enumeration.nextElement();
+                String name = ncPair.getName();
+                Object object = srcDir.lookup(name);
+                File currentFile = new File(destDir, name);
+                if (object instanceof Resource) {
+                    InputStream is = ((Resource) object).streamContent();
+                    OutputStream os = new FileOutputStream(currentFile);
+                    if (!copy(is, os))
+                        return false;
+                } else if (object instanceof InputStream) {
+                    OutputStream os = new FileOutputStream(currentFile);
+                    if (!copy((InputStream) object, os))
+                        return false;
+                } else if (object instanceof DirContext) {
+                    if (!currentFile.isDirectory() && !currentFile.mkdir())
+                        return false;
+                    if (!copyDir((DirContext) object, currentFile))
+                        return false;
+                }
+            }
+
+        } catch (NamingException e) {
+            return false;
+        } catch (IOException e) {
+            return false;
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Copy a file to the specified temp directory. This is required only
+     * because Jasper depends on it.
+     */
+    private boolean copy(InputStream is, OutputStream os) {
+
+        try {
+            byte[] buf = new byte[4096];
+            while (true) {
+                int len = is.read(buf);
+                if (len < 0)
+                    break;
+                os.write(buf, 0, len);
+            }
+        } catch (IOException e) {
+            return false;
+        } finally {
+            try {
+                is.close();
+            } catch (Exception e) {
+                // do nothing
+            }
+            try {
+                os.close();
+            } catch (Exception e) {
+                // do nothing
+            }
+        }
+
+        return true;
+
+    }
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+    // START OF PE 4985680
+    /**
+     * Adds the given package name to the list of packages that may always be
+     * overriden, regardless of whether they belong to a protected namespace
+     */
+    public void addOverridablePackage(String packageName){
+       if ( overridablePackages == null){
+           overridablePackages = new ArrayList<String>();
+       }
+        
+       overridablePackages.add( packageName ); 
+    }
+    // END OF PE 4985680
+
+
+    // START PWC 1.1 6314481
+    public void setIgnoreHiddenJarFiles(boolean ignoreHiddenJarFiles) {
+        this.ignoreHiddenJarFiles = ignoreHiddenJarFiles;
+    }
+    // END PWC 1.1 6314481
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/logger/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/Constants.java
new file mode 100644
index 0000000..8b8b912
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.logger;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.logger</code>
+ * package.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.logger";
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/logger/FileLogger.java b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/FileLogger.java
new file mode 100644
index 0000000..7c10215
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/FileLogger.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.logger;
+
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LogFacade;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.sql.Timestamp;
+
+
+/**
+ * Implementation of <b>Logger</b> that appends log messages to a file
+ * named {prefix}.{date}.{suffix} in a configured directory, with an
+ * optional preceding timestamp.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2006/10/03 20:19:12 $
+ */
+
+public class FileLogger
+    extends LoggerBase {
+
+    // ----------------------------------------------------- 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 = "logs";
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.logger.FileLogger/1.0";
+
+
+    /**
+     * The prefix that is added to log file filenames.
+     */
+    private String prefix = "catalina.";
+
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The suffix that is added to log file filenames.
+     */
+    private String suffix = ".log";
+
+
+    /**
+     * Should logged messages be date/time stamped?
+     */
+    private boolean timestamp = false;
+
+
+    /**
+     * The PrintWriter to which we are currently logging, if any.
+     */
+    private PrintWriter writer = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the directory in which we create log files.
+     */
+    public String getDirectory() {
+
+        return (directory);
+
+    }
+
+
+    /**
+     * Set the directory in which we create log files.
+     *
+     * @param directory The new log file directory
+     */
+    public void setDirectory(String directory) {
+
+        String oldDirectory = this.directory;
+        this.directory = directory;
+        support.firePropertyChange("directory", oldDirectory, this.directory);
+
+    }
+
+
+    /**
+     * Return the log file prefix.
+     */
+    public String getPrefix() {
+
+        return (prefix);
+
+    }
+
+
+    /**
+     * Set the log file prefix.
+     *
+     * @param prefix The new log file prefix
+     */
+    public void setPrefix(String prefix) {
+
+        String oldPrefix = this.prefix;
+        this.prefix = prefix;
+        support.firePropertyChange("prefix", oldPrefix, this.prefix);
+
+    }
+
+
+    /**
+     * Return the log file suffix.
+     */
+    public String getSuffix() {
+
+        return (suffix);
+
+    }
+
+
+    /**
+     * Set the log file suffix.
+     *
+     * @param suffix The new log file suffix
+     */
+    public void setSuffix(String suffix) {
+
+        String oldSuffix = this.suffix;
+        this.suffix = suffix;
+        support.firePropertyChange("suffix", oldSuffix, this.suffix);
+
+    }
+
+
+    /**
+     * Return the timestamp flag.
+     */
+    public boolean getTimestamp() {
+
+        return (timestamp);
+
+    }
+
+
+    /**
+     * Set the timestamp flag.
+     *
+     * @param timestamp The new timestamp flag
+     */
+    public void setTimestamp(boolean timestamp) {
+
+        boolean oldTimestamp = this.timestamp;
+        this.timestamp = timestamp;
+        support.firePropertyChange("timestamp", Boolean.valueOf(oldTimestamp),
+                                   Boolean.valueOf(this.timestamp));
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Writes the specified message to a servlet log file, usually an event
+     * log.  The name and type of the servlet log is specific to the
+     * servlet container.
+     *
+     * @param msg A <code>String</code> specifying the message to be written
+     *  to the log file
+     */
+    public void log(String msg) {
+
+        // 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();
+                }
+            }
+        }
+
+        // Log this message, timestamped if necessary
+        if (writer != null) {
+            if (timestamp) {
+                writer.println(tsString + " " + msg);
+            } else {
+                writer.println(msg);
+            }
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Close the currently open log file (if any)
+     */
+    private void close() {
+
+        if (writer == null)
+            return;
+        writer.flush();
+        writer.close();
+        writer = null;
+        date = "";
+
+    }
+
+
+    /**
+     * 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);
+        if (!dir.isAbsolute())
+            dir = new File(System.getProperty("catalina.base"), directory);
+        if (!dir.mkdirs() && !dir.exists()) {
+            writer = null;
+            return;
+        }
+
+        // Open the current log file
+        try {
+            String pathname = dir.getAbsolutePath() + File.separator +
+                prefix + date + suffix;
+            writer = new PrintWriter(new FileWriter(pathname, true), true);
+        } catch (IOException e) {
+            writer = null;
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.FILE_LOGGER_STARTED));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+        
+        super.start();
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.FILE_LOGGER_NOT_STARTED));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        close();
+        
+        super.stop();
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/logger/LoggerBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/LoggerBase.java
new file mode 100644
index 0000000..8a2fa5b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/LoggerBase.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.logger;
+
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.util.LifecycleSupport;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Convenience base class for <b>Logger</b> implementations.  The only
+ * method that must be implemented is <code>log(String msg)</code>, plus
+ * any property setting and lifecycle methods required for configuration.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.4 $ $Date: 2006/10/07 01:14:23 $
+ */
+
+public class LoggerBase
+    implements Lifecycle, org.apache.catalina.Logger
+ {
+     protected static final Logger log = LogFacade.getLogger();
+     protected static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Container with which this Logger has been associated.
+     */
+    protected Container container = null;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    protected int debug = 0;
+
+    
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.logger.LoggerBase/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * The verbosity level for above which log messages may be filtered.
+     */
+    protected int verbosity = ERROR;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container with which this Logger has been associated.
+     */
+    public Container getContainer() {
+
+        return (container);
+
+    }
+
+
+    /**
+     * Set the Container with which this Logger has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container) {
+
+        Container oldContainer = this.container;
+        this.container = container;
+        support.firePropertyChange("container", oldContainer, this.container);
+
+    }
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (this.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 descriptive information about this Logger implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the verbosity level of this logger.  Messages logged with a
+     * higher verbosity than this level will be silently ignored.
+     */
+    public int getVerbosity() {
+
+        return (this.verbosity);
+
+    }
+
+
+    /**
+     * Set the verbosity level of this logger.  Messages logged with a
+     * higher verbosity than this level will be silently ignored.
+     *
+     * @param verbosity The new verbosity level
+     */
+    public void setVerbosity(int verbosity) {
+
+        this.verbosity = verbosity;
+
+    }
+
+
+    /**
+     * Set the verbosity level of this logger.  Messages logged with a
+     * higher verbosity than this level will be silently ignored.
+     *
+     * @param verbosityLevel The new verbosity level, as a string
+     */
+    public void setVerbosityLevel(String verbosity) {
+
+        if ("FATAL".equalsIgnoreCase(verbosity))
+            this.verbosity = FATAL;
+        else if ("ERROR".equalsIgnoreCase(verbosity))
+            this.verbosity = ERROR;
+        else if ("WARNING".equalsIgnoreCase(verbosity))
+            this.verbosity = WARNING;
+        else if ("INFORMATION".equalsIgnoreCase(verbosity))
+            this.verbosity = INFORMATION;
+        else if ("DEBUG".equalsIgnoreCase(verbosity))
+            this.verbosity = DEBUG;
+
+    }
+
+
+    /**
+     * Set the verbosity level of this logger.  Messages logged with a
+     * higher verbosity than this level will be silently ignored.
+     *
+     * @param verbosityLevel The new verbosity level, as a string
+     */
+    public void setLevel(String logLevel) {
+            
+        if ("SEVERE".equalsIgnoreCase(logLevel)) {
+            log.setLevel(Level.SEVERE);
+        } else if ("WARNING".equalsIgnoreCase(logLevel)) {
+            log.setLevel(Level.WARNING);
+        } else if ("INFO".equalsIgnoreCase(logLevel)) {
+           log.setLevel(Level.INFO);
+        } else if ("CONFIG".equalsIgnoreCase(logLevel)) {
+           log.setLevel(Level.CONFIG);
+        } else if ("FINE".equalsIgnoreCase(logLevel)) {
+            log.setLevel(Level.FINE);
+        } else if ("FINER".equalsIgnoreCase(logLevel)) {
+            log.setLevel(Level.FINER);
+        } else if ("FINEST".equalsIgnoreCase(logLevel)) {
+            log.setLevel(Level.FINEST);
+        } else {
+            log.setLevel(Level.INFO);
+        }
+        
+    }
+
+
+    public void addHandler(Handler handler) {
+        log.setUseParentHandlers(false);
+        handler.setLevel(log.getLevel());
+        log.addHandler(handler);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Writes the specified message to a servlet log file, usually an event
+     * log.  The name and type of the servlet log is specific to the
+     * servlet container.  This message will be logged unconditionally.
+     *
+     * @param msg A <code>String</code> specifying the message to be
+     *  written to the log file
+     */
+    public void log(String msg) {
+        log.log(Level.FINE, msg);
+    }
+
+
+    /**
+     * Writes the specified exception, and message, to a servlet log file.
+     * The implementation of this method should call
+     * <code>log(msg, exception)</code> instead.  This method is deprecated
+     * in the ServletContext interface, but not deprecated here to avoid
+     * many useless compiler warnings.  This message will be logged
+     * unconditionally.
+     *
+     * @param exception An <code>Exception</code> to be reported
+     * @param msg The associated message string
+     */
+    public void log(Exception exception, String msg) {
+
+        log(msg, exception);
+
+    }
+
+
+    /**
+     * Writes an explanatory message and a stack trace for a given
+     * <code>Throwable</code> exception to the servlet log file.  The name
+     * and type of the servlet log file is specific to the servlet container,
+     * usually an event log.  This message will be logged unconditionally.
+     *
+     * @param msg A <code>String</code> that describes the error or
+     *  exception
+     * @param throwable The <code>Throwable</code> error or exception
+     */
+    public void log(String msg, Throwable throwable) {
+        msg = neutralizeForLog(msg);
+        CharArrayWriter buf = new CharArrayWriter();
+        PrintWriter writer = new PrintWriter(buf);
+        writer.println(msg);
+        throwable.printStackTrace(writer);
+        Throwable rootCause = null;
+        if (throwable instanceof LifecycleException)
+            rootCause = ((LifecycleException) throwable).getCause();
+        else if (throwable instanceof ServletException)
+            rootCause = ((ServletException) throwable).getRootCause();
+        if (rootCause != null) {
+            writer.println("----- Root Cause -----");
+            rootCause.printStackTrace(writer);
+        }
+        log(buf.toString());
+
+    }
+
+
+    /**
+     * Writes the specified message to the servlet log file, usually an event
+     * log, if the logger is set to a verbosity level equal to or higher than
+     * the specified value for this message.
+     *
+     * @param message A <code>String</code> specifying the message to be
+     *  written to the log file
+     * @param verbosity Verbosity level of this message
+     */
+    public void log(String message, int verbosity) {
+        message = neutralizeForLog(message);
+        if (this.verbosity >= verbosity)
+            log(message);
+
+    }
+
+
+    /**
+     * Writes the specified message and exception to the servlet log file,
+     * usually an event log, if the logger is set to a verbosity level equal
+     * to or higher than the specified value for this message.
+     *
+     * @param message A <code>String</code> that describes the error or
+     *  exception
+     * @param throwable The <code>Throwable</code> error or exception
+     * @param verbosity Verbosity level of this message
+     */
+    public void log(String message, Throwable throwable, int verbosity) {
+        message = neutralizeForLog(message);
+        if (this.verbosity >= verbosity)
+            log(message, throwable);
+
+    }
+
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+    protected ObjectName oname;
+    protected ObjectName controller;
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public void init() {
+        
+    }
+    
+    public void destroy() {
+        
+    }
+    
+    public ObjectName createObjectName() {
+        if(log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "createObjectName with " + container);
+        }
+        // register
+        try {
+            StandardEngine engine=null;            
+            String suffix="";
+            if( container instanceof StandardEngine ) {
+                engine=(StandardEngine)container;                
+            } else if( container instanceof StandardHost ) {
+                engine=(StandardEngine)container.getParent();
+                suffix=",host=" + container.getName();
+            } else if( container instanceof StandardContext ) {
+                String path = ((StandardContext)container).getPath();
+                // add "/" to avoid MalformedObjectName Exception
+                if (path.equals("")) {
+                    path = "/";
+                }
+                engine=(StandardEngine)container.getParent().getParent();
+                suffix= ",path=" + path + ",host=" + 
+                        container.getParent().getName();
+            } else {
+                log.log(Level.SEVERE, LogFacade.UNKNOWN_CONTAINER_EXCEPTION);
+            }
+            if( engine != null ) {
+                oname=new ObjectName(engine.getDomain()+ ":type=Logger" + suffix);
+            } else {
+                log.log(Level.SEVERE, LogFacade.NULL_ENGINE_EXCEPTION, container);
+            }
+        } catch (Throwable e) {
+            log.log(Level.WARNING,LogFacade.UNABLE_CREATE_OBJECT_NAME_FOR_LOGGER_EXCEPTION, e);
+        }
+        return oname;
+    }
+
+
+   // ------------------------------------------------------ Lifecycle Methods
+    
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners
+     * associated with this LoggerBase.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.     
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+                                                                
+        // register this logger
+        if ( getObjectName()==null ) {   
+            ObjectName oname = createObjectName();   
+            try {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, neutralizeForLog("Registering logger " + oname));
+                }
+            } catch( Exception ex ) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_REGISTER_LOGGER_EXCEPTION),
+                                                  oname);
+                log.log(Level.SEVERE, neutralizeForLog(msg), ex);
+            }      
+        }     
+
+    }                         
+                              
+                              
+    /**                       
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *                        
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // unregister this logger
+        if ( getObjectName()!=null ) {   
+            ObjectName oname = createObjectName();   
+            try {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, neutralizeForLog("Unregistering logger " + oname));
+                }
+            } catch( Exception ex ) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_REGISTER_LOGGER_EXCEPTION),
+                        oname);
+                log.log(Level.SEVERE, neutralizeForLog(msg), ex);
+            }      
+        }  
+    }
+  
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/logger/SystemErrLogger.java b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/SystemErrLogger.java
new file mode 100644
index 0000000..3f01143
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/SystemErrLogger.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.logger;
+
+
+/**
+ * Simple implementation of <b>Logger</b> that writes to System.err.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:46 $
+ */
+
+public class SystemErrLogger
+    extends LoggerBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.logger.SystemErrLogger/1.0";
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Writes the specified message to a servlet log file, usually an event
+     * log.  The name and type of the servlet log is specific to the
+     * servlet container.
+     *
+     * @param msg A <code>String</code> specifying the message to be written
+     *  to the log file
+     */
+    public void log(String msg) {
+
+        System.err.println(msg);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/logger/SystemOutLogger.java b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/SystemOutLogger.java
new file mode 100644
index 0000000..e8a510e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/logger/SystemOutLogger.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.logger;
+
+
+/**
+ * Simple implementation of <b>Logger</b> that writes to System.out.
+ * Because this component is so simple, no configuration is required.
+ * Therefore, Lifecycle is not implemented.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:46 $
+ */
+
+public class SystemOutLogger
+    extends LoggerBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.logger.SystemOutLogger/1.0";
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Writes the specified message to a servlet log file, usually an event
+     * log.  The name and type of the servlet log is specific to the
+     * servlet container.
+     *
+     * @param msg A <code>String</code> specifying the message to be written
+     *  to the log file
+     */
+    public void log(String msg) {
+
+        System.out.println(msg);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/mbeans/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/catalina/mbeans/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/mbeans/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/net/DefaultServerSocketFactory.java b/appserver/web/web-core/src/main/java/org/apache/catalina/net/DefaultServerSocketFactory.java
new file mode 100644
index 0000000..a00c4ef
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/net/DefaultServerSocketFactory.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.net;
+
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+
+/**
+ * Default server socket factory, which returns unadorned server sockets.
+ *
+ * @author db@eng.sun.com
+ * @author Harish Prabandham
+ * @author Craig R. McClanahan
+ */
+
+public final class DefaultServerSocketFactory implements ServerSocketFactory {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * 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                input/output or network error
+     * @exception KeyStoreException          error instantiating the
+     *                                       KeyStore from file (SSL only)
+     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
+     *                                       by current provider (SSL only)
+     * @exception CertificateException       general certificate error (SSL only)
+     * @exception UnrecoverableKeyException  internal KeyStore problem with
+     *                                       the certificate (SSL only)
+     * @exception KeyManagementException     problem in the key management
+     *                                       layer (SSL only)
+     */
+    public ServerSocket createSocket (int port)
+    throws IOException, KeyStoreException, NoSuchAlgorithmException,
+           CertificateException, UnrecoverableKeyException,
+           KeyManagementException {
+
+        return (new ServerSocket(port));
+
+    }
+
+
+    /**
+     * 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                input/output or network error
+     * @exception KeyStoreException          error instantiating the
+     *                                       KeyStore from file (SSL only)
+     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
+     *                                       by current provider (SSL only)
+     * @exception CertificateException       general certificate error (SSL only)
+     * @exception UnrecoverableKeyException  internal KeyStore problem with
+     *                                       the certificate (SSL only)
+     * @exception KeyManagementException     problem in the key management
+     *                                       layer (SSL only)
+     */
+    public ServerSocket createSocket (int port, int backlog)
+    throws IOException, KeyStoreException, NoSuchAlgorithmException,
+           CertificateException, UnrecoverableKeyException,
+           KeyManagementException {
+
+        return (new ServerSocket(port, backlog));
+
+    }
+
+
+    /**
+     * 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                input/output or network error
+     * @exception KeyStoreException          error instantiating the
+     *                                       KeyStore from file (SSL only)
+     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
+     *                                       by current provider (SSL only)
+     * @exception CertificateException       general certificate error (SSL only)
+     * @exception UnrecoverableKeyException  internal KeyStore problem with
+     *                                       the certificate (SSL only)
+     * @exception KeyManagementException     problem in the key management
+     *                                       layer (SSL only)
+     */
+    public ServerSocket createSocket (int port, int backlog,
+                                      InetAddress ifAddress)
+    throws IOException, KeyStoreException, NoSuchAlgorithmException,
+           CertificateException, UnrecoverableKeyException,
+           KeyManagementException {
+
+        return (new ServerSocket(port, backlog, ifAddress));
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/net/ServerSocketFactory.java b/appserver/web/web-core/src/main/java/org/apache/catalina/net/ServerSocketFactory.java
new file mode 100644
index 0000000..be7619f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/net/ServerSocketFactory.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.net;
+
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+
+/**
+ * Interface that describes the common characteristics of factory classes
+ * that create server sockets which may be required by a Connector.  A concrete
+ * implementation of this interface will be assigned to a Connector
+ * via the <code>setFactory()</code> method.
+ *
+ * @author db@eng.sun.com
+ * @author Harish Prabandham
+ * @author Craig R. McClanahan
+ */
+public interface ServerSocketFactory {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * 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                input/output or network error
+     * @exception KeyStoreException          error instantiating the
+     *                                       KeyStore from file (SSL only)
+     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
+     *                                       by current provider (SSL only)
+     * @exception CertificateException       general certificate error (SSL only)
+     * @exception UnrecoverableKeyException  internal KeyStore problem with
+     *                                       the certificate (SSL only)
+     * @exception KeyManagementException     problem in the key management
+     *                                       layer (SSL only)
+     */
+    public ServerSocket createSocket (int port)
+    throws IOException, KeyStoreException, NoSuchAlgorithmException,
+           CertificateException, UnrecoverableKeyException,
+           KeyManagementException;
+
+
+    /**
+     * 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                input/output or network error
+     * @exception KeyStoreException          error instantiating the
+     *                                       KeyStore from file (SSL only)
+     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
+     *                                       by current provider (SSL only)
+     * @exception CertificateException       general certificate error (SSL only)
+     * @exception UnrecoverableKeyException  internal KeyStore problem with
+     *                                       the certificate (SSL only)
+     * @exception KeyManagementException     problem in the key management
+     *                                       layer (SSL only)
+     */
+    public ServerSocket createSocket (int port, int backlog)
+    throws IOException, KeyStoreException, NoSuchAlgorithmException,
+           CertificateException, UnrecoverableKeyException,
+           KeyManagementException;
+
+
+    /**
+     * 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                input/output or network error
+     * @exception KeyStoreException          error instantiating the
+     *                                       KeyStore from file (SSL only)
+     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
+     *                                       by current provider (SSL only)
+     * @exception CertificateException       general certificate error (SSL only)
+     * @exception UnrecoverableKeyException  internal KeyStore problem with
+     *                                       the certificate (SSL only)
+     * @exception KeyManagementException     problem in the key management
+     *                                       layer (SSL only)
+     */
+    public ServerSocket createSocket (int port, int backlog,
+                                      InetAddress ifAddress)
+    throws IOException, KeyStoreException, NoSuchAlgorithmException,
+           CertificateException, UnrecoverableKeyException,
+           KeyManagementException;
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/realm/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/Constants.java
new file mode 100644
index 0000000..01ea697
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/Constants.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.realm;
+
+
+/**
+ * Manifest constants for this Java package.
+ *
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:52 $
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.realm";
+    
+        // Authentication methods for login configuration
+    public static final String FORM_METHOD = "FORM";
+
+    // Form based authentication constants
+    public static final String FORM_ACTION = "/j_security_check";
+
+    // User data constraints for transport guarantee
+    public static final String NONE_TRANSPORT = "NONE";
+    public static final String INTEGRAL_TRANSPORT = "INTEGRAL";
+    public static final String CONFIDENTIAL_TRANSPORT = "CONFIDENTIAL";
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/realm/GenericPrincipal.java b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/GenericPrincipal.java
new file mode 100644
index 0000000..33bfa55
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/GenericPrincipal.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.realm;
+
+
+import org.apache.catalina.Realm;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * Generic implementation of <strong>java.security.Principal</strong> that
+ * is available for use by <code>Realm</code> implementations.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:52 $
+ */
+
+public class GenericPrincipal implements Principal {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new Principal, associated with the specified Realm, for the
+     * specified username and password.
+     *
+     * @param realm The Realm that owns this Principal
+     * @param name The username of the user represented by this Principal
+     * @param password Credentials used to authenticate this user
+     */
+    public GenericPrincipal(Realm realm, String name, char[] password) {
+
+        this(realm, name, password, null);
+
+    }
+
+
+    /**
+     * Construct a new Principal, associated with the specified Realm, for the
+     * specified username and password, with the specified role names
+     * (as Strings).
+     *
+     * @param realm The Realm that owns this principal
+     * @param name The username of the user represented by this Principal
+     * @param password Credentials used to authenticate this user
+     * @param roles List of roles (must be Strings) possessed by this user
+     */
+    public GenericPrincipal(Realm realm, String name, char[] password,
+                            List<String> roles) {
+
+        super();
+        this.realm = realm;
+        this.name = name;
+        this.password = ((password != null)? ((char[])password.clone()) : null);
+        if (roles != null) {
+            this.roles = new String[roles.size()];
+            this.roles = roles.toArray(this.roles);
+            if (this.roles.length > 0)
+                Arrays.sort(this.roles);
+        }
+    }
+
+    public GenericPrincipal(String name, char[] password,
+                            List<String> roles) {
+
+        super();
+        this.name = name;
+        this.password = ((password != null)? ((char[])password.clone()) : null);
+        if (roles != null) {
+            this.roles = new String[roles.size()];
+            this.roles = roles.toArray(this.roles);
+            if (this.roles.length > 0)
+                Arrays.sort(this.roles);
+        }
+    }
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The username of the user represented by this Principal.
+     */
+    protected String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+
+    /**
+     * The authentication credentials for the user represented by
+     * this Principal.
+     */
+    protected char[] password = null;
+
+    public char[] getPassword() {
+        return ((this.password != null)? ((char[])this.password.clone()) : null);
+    }
+
+
+    /**
+     * The Realm with which this Principal is associated.
+     */
+    protected Realm realm = null;
+
+    public Realm getRealm() {
+        return (this.realm);
+    }
+
+    void setRealm( Realm realm ) {
+        this.realm=realm;
+    }
+
+
+    /**
+     * The set of roles associated with this user.
+     */
+    protected String roles[] = new String[0];
+
+    public String[] getRoles() {
+        return (this.roles);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Does the user represented by this Principal possess the specified role?
+     *
+     * @param role Role to be tested
+     */
+    public boolean hasRole(String role) {
+
+        if("*".equals(role)) // Special 2.4 role meaning everyone
+            return true;
+        if (role == null)
+            return (false);
+        return (Arrays.binarySearch(roles, role) >= 0);
+
+    }
+
+
+    /**
+     * Return a String representation of this object, which exposes only
+     * information that should be public.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("GenericPrincipal[");
+        sb.append(this.name);
+        sb.append("(");
+        for( int i=0;i<roles.length; i++ ) {
+            sb.append( roles[i]).append(",");
+        }
+        sb.append(")]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/realm/JAASCallbackHandler.java b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/JAASCallbackHandler.java
new file mode 100644
index 0000000..24dec04
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/JAASCallbackHandler.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.realm;
+
+
+import javax.security.auth.callback.*;
+import java.io.IOException;
+
+
+/**
+ * <p>Implementation of the JAAS <strong>CallbackHandler</code> interface,
+ * used to negotiate delivery of the username and credentials that were
+ * specified to our constructor.  No interaction with the user is required
+ * (or possible).</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:52 $
+ */
+
+public class JAASCallbackHandler implements CallbackHandler {
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct a callback handler configured with the specified values.
+     *
+     * @param realm Our associated JAASRealm instance
+     * @param username Username to be authenticated with
+     * @param password Password to be authenticated with
+     */
+    public JAASCallbackHandler(JAASRealm realm, String username,
+                               char[] password) {
+
+        super();
+        this.realm = realm;
+        this.username = username;
+        this.password = ((password != null) ? ((char[])password.clone()) : null);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The password to be authenticated with.
+     */
+    protected char[] password = null;
+
+
+    /**
+     * The associated <code>JAASRealm</code> instance.
+     */
+    protected JAASRealm realm = null;
+
+
+    /**
+     * The username to be authenticated with.
+     */
+    protected String username = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieve the information requested in the provided Callbacks.  This
+     * implementation only recognizes <code>NameCallback</code> and
+     * <code>PasswordCallback</code> instances.
+     *
+     * @param callbacks The set of callbacks to be processed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception UnsupportedCallbackException if the login method requests
+     *  an unsupported callback type
+     */
+    public void handle(Callback callbacks[])
+        throws IOException, UnsupportedCallbackException {
+
+        for (int i = 0; i < callbacks.length; i++) {
+
+            if (callbacks[i] instanceof NameCallback) {
+                if (realm.getDebug() >= 3)
+                    realm.log("Returning username " + username);
+                ((NameCallback) callbacks[i]).setName(username);
+            } else if (callbacks[i] instanceof PasswordCallback) {
+                  final char[] passwordcontents;
+                  if (password != null) {
+                      passwordcontents = (char[])password.clone();
+                  } else {
+                      passwordcontents = new char[0];
+                  }
+                  ((PasswordCallback) callbacks[i]).setPassword
+                      (passwordcontents);
+            } else {
+                throw new UnsupportedCallbackException(callbacks[i]);
+            }
+
+
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/realm/JAASRealm.java b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/JAASRealm.java
new file mode 100644
index 0000000..09828b5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/JAASRealm.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.realm;
+
+
+import org.apache.catalina.Container;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LogFacade;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.*;
+import java.security.Principal;
+import java.security.acl.Group;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.logging.Level;
+
+/**
+ * <p>Implementation of <b>Realm</b> that authenticates users via the <em>Java
+ * Authentication and Authorization Service</em> (JAAS).  JAAS support requires
+ * either JDK 1.4 (which includes it as part of the standard platform) or
+ * JDK 1.3 (with the plug-in <code>jaas.jar</code> file).</p>
+ *
+ * <p>The value configured for the <code>appName</code> property is passed to
+ * the <code>javax.security.auth.login.LoginContext</code> constructor, to
+ * specify the <em>application name</em> used to select the set of relevant
+ * <code>LoginModules</code> required.</p>
+ *
+ * <p>The JAAS Specification describes the result of a successful login as a
+ * <code>javax.security.auth.Subject</code> instance, which can contain zero
+ * or more <code>java.security.Principal</code> objects in the return value
+ * of the <code>Subject.getPrincipals()</code> method.  However, it provides
+ * no guidance on how to distinguish Principals that describe the individual
+ * user (and are thus appropriate to return as the value of
+ * request.getUserPrincipal() in a web application) from the Principal(s)
+ * that describe the authorized roles for this user.  To maintain as much
+ * independence as possible from the underlying <code>LoginMethod</code>
+ * implementation executed by JAAS, the following policy is implemented by
+ * this Realm:</p>
+ * <ul>
+ * <li>The JAAS <code>LoginModule</code> is assumed to return a
+ *     <code>Subject with at least one <code>Principal</code> instance
+ *     representing the user himself or herself, and zero or more separate
+ *     <code>Principals</code> representing the security roles authorized
+ *     for this user.</li>
+ * <li>On the <code>Principal</code> representing the user, the Principal
+ *     name is an appropriate value to return via the Servlet API method
+ *     <code>HttpServletRequest.getRemoteUser()</code>.</li>
+ * <li>On the <code>Principals</code> representing the security roles, the
+ *     name is the name of the authorized security role.</li>
+ * <li>This Realm will be configured with two lists of fully qualified Java
+ *     class names of classes that implement
+ *     <code>java.security.Principal</code> - one that identifies class(es)
+ *     representing a user, and one that identifies class(es) representing
+ *     a security role.</li>
+ * <li>As this Realm iterates over the <code>Principals</code> returned by
+ *     <code>Subject.getPrincipals()</code>, it will identify the first
+ *     <code>Principal</code> that matches the "user classes" list as the
+ *     <code>Principal</code> for this user.</li>
+ * <li>As this Realm iterates over the <code>Principals</code> returned by
+ *     <code>Subject.getPrincipals()</code>, it will accumulate the set of
+ *     all <code>Principals</code> matching the "role classes" list as
+ *     identifying the security roles for this user.</li>
+ * <li>It is a configuration error for the JAAS login method to return a
+ *     validated <code>Subject</code> without a <code>Principal</code> that
+ *     matches the "user classes" list.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:04 $
+ */
+
+public class JAASRealm
+    extends RealmBase
+ {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The application name passed to the JAAS <code>LoginContext</code>,
+     * which uses it to select the set of relevant <code>LoginModules</code>.
+     */
+    protected String appName = null;
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.realm.JAASRealm/1.0";
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String name = "JAASRealm";
+
+
+    /**
+     * The list of role class names, split out for easy processing.
+     */
+    protected ArrayList<String> roleClasses = new ArrayList<String>();
+
+
+    /**
+     * The set of user class names, split out for easy processing.
+     */
+    protected ArrayList<String> userClasses = new ArrayList<String>();
+
+
+    // ------------------------------------------------------------- Properties
+
+    
+    /**
+     * setter for the appName member variable
+     * @deprecated JAAS should use the Engine ( domain ) name and webpp/host overrides
+     */
+    public void setAppName(String name) {
+        appName = name;
+    }
+    
+    /**
+     * getter for the appName member variable
+     */
+    public String getAppName() {
+        return appName;
+    }
+
+    public void setContainer(Container container) {
+        super.setContainer(container);
+        String name=container.getName();
+        if( appName==null  ) {
+            appName=name;
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.SETTING_JAAS_INFO, appName);
+            }
+        }
+    }
+
+    /**
+     * Comma-delimited list of <code>javax.security.Principal</code> classes
+     * that represent security roles.
+     */
+    protected String roleClassNames = null;
+
+    public String getRoleClassNames() {
+        return (this.roleClassNames);
+    }
+
+    public void setRoleClassNames(String roleClassNames) {
+        this.roleClassNames = roleClassNames;
+        roleClasses.clear();
+        String temp = this.roleClassNames;
+        if (temp == null) {
+            return;
+        }
+        while (true) {
+            int comma = temp.indexOf(',');
+            if (comma < 0) {
+                break;
+            }
+            roleClasses.add(temp.substring(0, comma).trim());
+            temp = temp.substring(comma + 1);
+        }
+        temp = temp.trim();
+        if (temp.length() > 0) {
+            roleClasses.add(temp);
+        }
+    }
+
+
+    /**
+     * Comma-delimited list of <code>javax.security.Principal</code> classes
+     * that represent individual users.
+     */
+    protected String userClassNames = null;
+
+    public String getUserClassNames() {
+        return (this.userClassNames);
+    }
+
+    public void setUserClassNames(String userClassNames) {
+        this.userClassNames = userClassNames;
+        userClasses.clear();
+        String temp = this.userClassNames;
+        if (temp == null) {
+            return;
+        }
+        while (true) {
+            int comma = temp.indexOf(',');
+            if (comma < 0) {
+                break;
+            }
+            userClasses.add(temp.substring(0, comma).trim());
+            temp = temp.substring(comma + 1);
+        }
+        temp = temp.trim();
+        if (temp.length() > 0) {
+            userClasses.add(temp);
+        }
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * If there are any errors with the JDBC connection, executing
+     * the query or anything we return null (don't authenticate). This
+     * event is also logged, and the connection will be closed so that
+     * a subsequent request will automatically re-open it.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, char[] credentials) {
+
+        // Establish a LoginContext to use for authentication
+        try {
+        LoginContext loginContext = null;
+        if( appName==null ) appName="Tomcat";
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, neutralizeForLog("Authenticating " + appName + " " +  username));
+
+        // What if the LoginModule is in the container class loader ?
+        //
+        ClassLoader ocl=Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+        try {
+            loginContext = new LoginContext
+                (appName, new JAASCallbackHandler(this, username,
+                                                  credentials));
+        } catch (Throwable e) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Error initializing JAAS: " +  e.toString());
+
+                String msg = MessageFormat.format(rb.getString(LogFacade.LOGIN_EXCEPTION_AUTHENTICATING_USERNAME), neutralizeForLog(username));
+                log.log(Level.FINE, msg, e);
+            }
+            return (null);
+        } finally {
+            Thread.currentThread().setContextClassLoader(ocl);
+        }
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Login context created " + neutralizeForLog(username));
+
+        // Negotiate a login via this LoginContext
+        Subject subject = null;
+        try {
+            loginContext.login();
+            subject = loginContext.getSubject();
+            if (subject == null) {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, LogFacade.USERNAME_NOT_AUTHENTICATED_FAILED_LOGIN, neutralizeForLog(username));
+                }
+                return (null);
+            }
+        } catch (AccountExpiredException e) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, LogFacade.USERNAME_NOT_AUTHENTICATED_EXPIRED_ACCOUNT, neutralizeForLog(username));
+            }
+            return (null);
+        } catch (CredentialExpiredException e) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, LogFacade.USERNAME_NOT_AUTHENTICATED_EXPIRED_CREDENTIAL, neutralizeForLog(username));
+            }
+            return (null);
+        } catch (FailedLoginException e) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, LogFacade.USERNAME_NOT_AUTHENTICATED_FAILED_LOGIN, neutralizeForLog(username));
+            }
+            return (null);
+        } catch (LoginException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.LOGIN_EXCEPTION_AUTHENTICATING_USERNAME),
+                    neutralizeForLog(username));
+            log.log(Level.FINE, msg, e);
+            return (null);
+        } catch (Throwable e) {
+            log.log(Level.FINE, "Unexpected error", e);
+            return (null);
+        }
+
+        if( log.isLoggable(Level.FINE))
+            log.log(Level.FINE, neutralizeForLog("Getting principal " + subject));
+
+        // Return the appropriate Principal for this authenticated Subject
+        Principal principal = createPrincipal(username, subject);
+        if (principal == null) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Failed to authenticate username " + neutralizeForLog(username));
+            }
+            return (null);
+        }
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Successful to authenticate username " + neutralizeForLog(username));
+        }
+
+        return (principal);
+        } catch( Throwable t) {
+            log.log(Level.SEVERE, LogFacade.AUTHENTICATION_ERROR, t);
+            return null;
+        }
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a short name for this Realm implementation.
+     */
+    protected String getName() {
+
+        return (this.name);
+
+    }
+
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected char[] getPassword(String username) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the Principal associated with the given user name.
+     */
+    protected Principal getPrincipal(String username) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Construct and return a <code>java.security.Principal</code> instance
+     * representing the authenticated user for the specified Subject.  If no
+     * such Principal can be constructed, return <code>null</code>.
+     *
+     * @param subject The Subject representing the logged in user
+     */
+    protected Principal createPrincipal(String username, Subject subject) {
+        // Prepare to scan the Principals for this Subject
+        ArrayList<String> roles = new ArrayList<String>();
+
+        // Scan the Principals for this Subject
+        Iterator principals = subject.getPrincipals().iterator();
+        while (principals.hasNext()) {
+            Principal principal = (Principal) principals.next();
+            // No need to look further - that's our own stuff
+            if( principal instanceof GenericPrincipal ) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Found old GenericPrincipal " + principal);
+                return principal;
+            }
+            String principalClass = principal.getClass().getName();
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "Principal: " + principalClass + " " + principal);
+
+            if (userClasses.contains(principalClass)) {
+                // Override the default - which is the original user, accepted by
+                // the friendly LoginManager
+                username = principal.getName();
+            }
+            if (roleClasses.contains(principalClass)) {
+                roles.add(principal.getName());
+            }
+            // Same as Jboss - that's a pretty clean solution
+            if( (principal instanceof Group) &&
+                 "Roles".equals( principal.getName())) {
+                Group grp=(Group)principal;
+                Enumeration en=grp.members();
+                while( en.hasMoreElements() ) {
+                    Principal roleP=(Principal)en.nextElement();
+                    roles.add( roleP.getName());
+                }
+
+            }
+        }
+
+        // Create the resulting Principal for our authenticated user
+        if (username != null) {
+            // Set password null as it will not be carried forward
+            return (new GenericPrincipal(this, username, null, roles));
+        } else {
+            return (null);
+        }
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     *
+     * Prepare for active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents it from being started
+     */
+    public void start() throws LifecycleException {
+
+        // Perform normal superclass initialization
+        super.start();
+
+    }
+
+
+    /**
+     * Gracefully shut down active use of the public methods of this Component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Perform normal superclass finalization
+        super.stop();
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/realm/RealmBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/RealmBase.java
new file mode 100644
index 0000000..c59ef8e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/RealmBase.java
@@ -0,0 +1,1642 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.realm;
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.*;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.SecurityCollection;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.util.*;
+
+import javax.management.ObjectName;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.nio.charset.CharacterCodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.sun.enterprise.util.Utility;
+import javax.servlet.ServletContext;
+// END SJSWS 6324431
+
+/**
+ * Simple implementation of <b>Realm</b> that reads an XML file to configure
+ * the valid users, passwords, and roles.  The file format (and default file
+ * location) are identical to those currently supported by Tomcat 3.X.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.14 $ $Date: 2007/04/18 17:27:23 $
+ */
+
+public abstract class RealmBase
+    implements Lifecycle, Realm {
+
+    protected static final Logger log = LogFacade.getLogger();
+    protected static final ResourceBundle rb = log.getResourceBundle();
+
+
+    //START SJSAS 6202703
+    /**
+     * "Expires" header always set to Date(1), so generate once only
+     */
+    private static final String DATE_ONE =
+            (new SimpleDateFormat(Response.HTTP_RESPONSE_DATE_HEADER,
+            Locale.US)).format(new Date(1));
+    //END SJSAS 6202703
+
+    // ----------------------------------------------------- Instance Variables
+
+     /**
+     * The debugging detail level for this component.
+     */
+    protected int debug = 0;
+
+    /**
+     * The Container with which this Realm is associated.
+     */
+    protected Container container = null;
+
+
+    /**
+     * Flag indicating whether a check to see if the request is secure is
+     * required before adding Pragma and Cache-Control headers when proxy 
+     * caching has been disabled
+     */
+    protected boolean checkIfRequestIsSecure = false;
+
+
+    /**
+     * Digest algorithm used in storing passwords in a non-plaintext format.
+     * Valid values are those accepted for the algorithm name by the
+     * MessageDigest class, or <code>null</code> if no digesting should
+     * be performed.
+     */
+    protected String digest = null;
+
+    /**
+     * The encoding charset for the digest.
+     */
+    protected String digestEncoding = null;
+
+
+    /**
+     * Descriptive information about this Realm implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.realm.RealmBase/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The MessageDigest object for digesting user credentials (passwords).
+     */
+    protected volatile MessageDigest md = null;
+
+
+    /**
+     * SHA-256 message digest provider.
+     */
+    protected static volatile MessageDigest sha256Helper;
+
+
+    /**
+     * Has this component been started?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+    /**
+     * Should we validate client certificate chains when they are presented?
+     */
+    protected boolean validate = true;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Container with which this Realm has been associated.
+     */
+    public Container getContainer() {
+
+        return (container);
+
+    }
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (this.debug);
+
+    }
+
+
+    /**
+     * Set the debugging detail level for this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+
+        this.debug = debug;
+
+    }
+
+    /**
+     * Set the Container with which this Realm has been associated.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container) {
+
+        Container oldContainer = this.container;
+        this.container = container;
+        this.checkIfRequestIsSecure = container.isCheckIfRequestIsSecure();
+        support.firePropertyChange("container", oldContainer, this.container);
+
+    }
+
+    /**
+     * Return the digest algorithm  used for storing credentials.
+     */
+    public String getDigest() {
+
+        return digest;
+
+    }
+
+
+    /**
+     * Set the digest algorithm used for storing credentials.
+     *
+     * @param digest The new digest algorithm
+     */
+    public void setDigest(String digest) {
+
+        this.digest = digest;
+
+    }
+
+    /**
+     * Returns the digest encoding charset.
+     *
+     * @return The charset (may be null) for platform default
+     */
+    public String getDigestEncoding() {
+        return digestEncoding;
+    }
+
+    /**
+     * Sets the digest encoding charset.
+     *
+     * @param charset The charset (null for platform default)
+     */
+    public void setDigestEncoding(String charset) {
+        digestEncoding = charset;
+    }
+
+    /**
+     * Return descriptive information about this Realm implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return info;
+
+    }
+
+
+    /**
+     * Return the "validate certificate chains" flag.
+     */
+    public boolean getValidate() {
+
+        return (this.validate);
+
+    }
+
+
+    /**
+     * Set the "validate certificate chains" flag.
+     *
+     * @param validate The new validate certificate chains flag
+     */
+    public void setValidate(boolean validate) {
+
+        this.validate = validate;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+        support.addPropertyChangeListener(listener);
+
+    }
+
+
+    /**
+     * Return the Principal associated with the specified username and
+     * credentials, if there is one; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    public Principal authenticate(String username, char[] credentials) {
+
+        char[] serverCredentials = getPassword(username);
+
+        boolean validated ;
+        if ( serverCredentials == null ) {
+            validated = false;
+        } else if(hasMessageDigest()) {
+            validated = equalsIgnoreCase(serverCredentials, digest(credentials));
+        } else {
+            validated = Arrays.equals(serverCredentials, credentials);
+        }
+        if(! validated ) {
+            return null;
+        }
+        return getPrincipal(username);
+    }
+
+
+    /**
+     * Return the Principal associated with the specified username, which
+     * matches the digest calculated using the given parameters using the
+     * method described in RFC 2069; otherwise return <code>null</code>.
+     *
+     * @param username Username of the Principal to look up
+     * @param clientDigest Digest which has been submitted by the client
+     * @param nOnce Unique (or supposedly unique) token which has been used
+     * for this request
+     * @param realm Realm name
+     * @param md5a2 Second MD5 digest used to calculate the digest :
+     * MD5(Method + ":" + uri)
+     */
+    public Principal authenticate(String username, char[] clientDigest,
+                                  String nOnce, String nc, String cnonce,
+                                  String qop, String realm,
+                                  char[] md5a2) {
+
+        char[] md5a1 = getDigest(username, realm);
+        if (md5a1 == null)
+            return null;
+
+        int nOnceLength = ((nOnce != null) ? nOnce.length() : 0);
+        int ncLength = ((nc != null) ? nc.length() : 0);
+        int cnonceLength = ((cnonce != null) ? cnonce.length() : 0);
+        int qopLength = ((qop != null) ? qop.length() : 0);
+        int md5a2Length = ((md5a2 != null) ? md5a2.length : 0);
+
+        // serverDigestValue = md5a1:nOnce:nc:cnonce:qop:md5a2
+        char[] serverDigestValue = new char[md5a1.length + 1 + 
+            nOnceLength + 1 + ncLength + 1 + cnonceLength + 1 +
+            qopLength + 1 + md5a2Length];
+
+        System.arraycopy(md5a1, 0, serverDigestValue, 0, md5a1.length);
+        int ind = md5a1.length;
+        serverDigestValue[ind++] = ':';
+        if (nOnce != null) {
+            System.arraycopy(nOnce.toCharArray(), 0, serverDigestValue, ind, nOnceLength);
+            ind += nOnceLength;
+        }
+        serverDigestValue[ind++] = ':';
+        if (nc != null) {
+            System.arraycopy(nc.toCharArray(), 0, serverDigestValue, ind, ncLength);
+            ind += ncLength;
+        }
+        serverDigestValue[ind++] = ':';
+        if (cnonce != null) {
+            System.arraycopy(cnonce.toCharArray(), 0, serverDigestValue, ind, cnonceLength);
+            ind += cnonceLength;
+        }
+        serverDigestValue[ind++] = ':';
+        if (qop != null) {
+            System.arraycopy(qop.toCharArray(), 0, serverDigestValue, ind, qopLength);
+            ind += qopLength;
+        }
+        serverDigestValue[ind++] = ':';
+        if (md5a2 != null) {
+            System.arraycopy(md5a2, 0, serverDigestValue, ind, md5a2Length);
+        }
+
+        byte[] valueBytes = null;
+
+        try {
+            valueBytes = Utility.convertCharArrayToByteArray(
+                    serverDigestValue, getDigestEncoding());
+        } catch (CharacterCodingException cce) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ILLEGAL_DIGEST_ENCODING_EXCEPTION),
+                                              getDigestEncoding());
+            log.log(Level.SEVERE, msg, cce);
+            throw new IllegalArgumentException(cce.getMessage());
+        }
+
+        char[] serverDigest = null;
+        // Bugzilla 32137
+        synchronized(sha256Helper) {
+            serverDigest = new String(sha256Helper.digest(valueBytes)).toCharArray();
+        }
+
+        if (log.isLoggable(Level.FINE)) {
+            String msg = "Username:" + username
+                         + " ClientSigest:" + Arrays.toString(clientDigest) + " nOnce:" + nOnce
+                         + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop
+                         + " realm:" + realm + "md5a2:" + Arrays.toString(md5a2)
+                         + " Server digest:" + String.valueOf(serverDigest);
+            log.log(Level.FINE, msg);
+        }
+        
+        if (Arrays.equals(serverDigest, clientDigest)) {
+            return getPrincipal(username);
+        } else {
+            return null;
+        }
+    }
+
+
+
+    /**
+     * Return the Principal associated with the specified chain of X509
+     * client certificates.  If there is none, return <code>null</code>.
+     *
+     * @param certs Array of client certificates, with the first one in
+     *  the array being the certificate of the client itself.
+     */
+    public Principal authenticate(X509Certificate certs[]) {
+
+        if ((certs == null) || (certs.length < 1))
+            return (null);
+
+        // Check the validity of each certificate in the chain
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Authenticating client certificate chain");
+        if (validate) {
+            for (int i = 0; i < certs.length; i++) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Checking validity for '" +
+                            certs[i].getSubjectDN().getName() + "'");
+                try {
+                    certs[i].checkValidity();
+                } catch (Exception e) {
+                    if (log.isLoggable(Level.FINE))
+                        log.log(Level.FINE, "Validity exception", e);
+                    return (null);
+                }
+            }
+        }
+
+        // Check the existence of the client Principal in our database
+        return (getPrincipal(certs[0].getSubjectDN().getName()));
+
+    }
+
+    
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess() {
+    }
+
+
+    /**
+     * Return the SecurityConstraints configured to guard the request URI for
+     * this request, or <code>null</code> if there is no such constraint.
+     *
+     * @param request Request we are processing
+     * @param context Context the Request is mapped to
+     */
+    public SecurityConstraint[] findSecurityConstraints(
+            HttpRequest request, Context context) {
+        return findSecurityConstraints(
+            request.getRequestPathMB().toString(),
+            ((HttpServletRequest) request.getRequest()).getMethod(),
+            context);
+    }
+
+    /**
+     * Gets the security constraints configured by the given context
+     * for the given request URI and method.
+     *
+     * @param uri the request URI (minus the context Path)
+     * @param method the request method
+     * @param context the context
+     *
+     * @return the security constraints configured by the given context
+     * for the given request URI and method, or null
+     */
+    public SecurityConstraint[] findSecurityConstraints(
+            String uri, String method, Context context) {
+
+        ArrayList<SecurityConstraint> results = null;
+
+        // Are there any defined security constraints?
+        if (!context.hasConstraints()) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "  No applicable constraints defined");
+            return (null);
+        }
+        
+        // START SJSWS 6324431
+        String origUri = uri;
+        boolean caseSensitiveMapping = 
+            ((StandardContext)context).isCaseSensitiveMapping();
+        if (uri != null && !caseSensitiveMapping) {
+            uri = uri.toLowerCase(Locale.ENGLISH);
+        }
+        // END SJSWS 6324431
+
+        boolean found = false;
+
+        List<SecurityConstraint> constraints = context.getConstraints();
+        Iterator<SecurityConstraint> i = constraints.iterator(); 
+        while (i.hasNext()) {
+            SecurityConstraint constraint = i.next();
+            SecurityCollection[] collection = constraint.findCollections();
+                     
+            // If collection is null, continue to avoid an NPE
+            // See Bugzilla 30624
+            if (collection == null) {
+                continue;
+            }
+
+            if (log.isLoggable(Level.FINEST)) {
+                /* SJSWS 6324431
+                log.trace("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+                */
+                // START SJSWS 6324431
+                String msg = "Checking constraint '" + constraint +
+                             "' against " + method + " " + origUri +
+                             " --> " +
+                             constraint.included(uri, method,
+                                                 caseSensitiveMapping);
+                log.log(Level.FINEST, msg);
+                // END SJSWS 6324431
+            }
+            /* SJSWS 6324431
+            if (log.isDebugEnabled() && constraints[i].included(
+                    uri, method)) {
+                log.debug("  Matched constraint '" + constraints[i] +
+                    "' against " + method + " " + uri);
+            }
+            */
+            // START SJSWS 6324431
+            if (log.isLoggable(Level.FINE)
+                    && constraint.included(uri, method,
+                                           caseSensitiveMapping)) {
+
+                log.log(Level.FINE, "  Matched constraint '" + constraint +
+                        "' against " + method + " " + origUri);
+
+            }
+            // END SJSWS 6324431
+
+            for (int j=0; j < collection.length; j++){
+                String[] patterns = collection[j].findPatterns();
+                // If patterns is null, continue to avoid an NPE
+                // See Bugzilla 30624
+                if ( patterns == null) {
+                    continue;
+                }
+
+                for(int k=0; k < patterns.length; k++) {
+                    /* SJSWS 6324431
+                    if(uri.equals(patterns[k])) {
+                    */
+                    // START SJSWS 6324431
+                    String pattern = caseSensitiveMapping ? patterns[k] :
+                        patterns[k].toLowerCase(Locale.ENGLISH);
+                    if (uri != null && uri.equals(pattern)) {
+                    // END SJSWS 6324431
+                        found = true;
+                        if(collection[j].findMethod(method)) {
+                            if(results == null) {
+                                results = new ArrayList<SecurityConstraint>();
+                            }
+                            results.add(constraint);
+                        }
+                    }
+                }
+            }
+        } // while
+
+        if (found) {
+            return resultsToArray(results);
+        }
+
+        int longest = -1;
+
+        i = constraints.iterator(); 
+        while (i.hasNext()) {
+            SecurityConstraint constraint = i.next();
+            SecurityCollection [] collection =
+                constraint.findCollections();
+            
+            // If collection is null, continue to avoid an NPE
+            // See Bugzilla 30624
+            if ( collection == null) {
+                continue;
+            }
+
+            if (log.isLoggable(Level.FINEST)) {
+                /* SJSWS 6324431
+                log.trace("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+                */
+                // START SJSWS 6324431
+                String msg = "  Checking constraint '" + constraint +
+                             "' against " + method + " " + origUri +
+                             " --> " +
+                             constraint.included(uri, method,
+                                                 caseSensitiveMapping);
+                log.log(Level.FINE, msg);
+                // END SJSWS 6324431
+            }
+            /* SJSWS 6324431
+            if (log.isDebugEnabled() && constraints[i].included(
+                    uri, method)) {
+                log.debug("  Matched constraint '" + constraints[i] +
+                    "' against " + method + " " + uri);
+            }
+            */
+            // START SJSWS 6324431
+            if (log.isLoggable(Level.FINE) &&
+                    constraint.included(uri, method,
+                                        caseSensitiveMapping)) {
+                log.log(Level.FINE, "  Matched constraint '" + constraint +
+                        "' against " + method + " " + origUri);
+            }
+            // END SJSWS 6324431
+
+            for (int j=0; j < collection.length; j++){
+                String[] patterns = collection[j].findPatterns();
+                // If patterns is null, continue to avoid an NPE
+                // See Bugzilla 30624
+                if (patterns == null) {
+                    continue;
+                }
+
+                boolean matched = false;
+                int length = -1;
+                for (int k=0; k < patterns.length; k++) {
+                    /* SJSWS 6324431
+                    String pattern = patterns[k];
+                    */
+                    // START SJSWS 6324431
+                    String pattern = caseSensitiveMapping ?
+                        patterns[k]:patterns[k].toLowerCase(Locale.ENGLISH);
+                    // END SJSWS 6324431
+                    if (pattern.startsWith("/") &&
+                            pattern.endsWith("/*") && 
+                            pattern.length() >= longest) {
+                            
+                        if (pattern.length() == 2) {
+                            matched = true;
+                            length = pattern.length();
+                        } else if (uri != null
+                                && (pattern.regionMatches(
+                                        0,uri,0,pattern.length()-1)
+                                    || (pattern.length()-2 == uri.length()
+                                        && pattern.regionMatches(
+                                            0,uri,0,pattern.length()-2)))) {
+                            matched = true;
+                            length = pattern.length();
+                        }
+                    }
+                }
+                if (matched) {
+                    found = true;
+                    if (length > longest) {
+                        if (results != null) {
+                            results.clear();
+                        }
+                        longest = length;
+                    }
+                    if (collection[j].findMethod(method)) {
+                        if (results == null) {
+                            results = new ArrayList<SecurityConstraint>();
+                        }
+                        results.add(constraint);
+                    }
+                }
+            }
+        } // while
+
+        if (found) {
+            return resultsToArray(results);
+        }
+
+        i = constraints.iterator(); 
+        while (i.hasNext()) {
+            SecurityConstraint constraint = i.next();
+            SecurityCollection[] collection = constraint.findCollections();
+
+            // If collection is null, continue to avoid an NPE
+            // See Bugzilla 30624
+            if ( collection == null) {
+                continue;
+            }
+            
+            if (log.isLoggable(Level.FINEST)) {
+                /* SJSWS 6324431
+                log.trace("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+                */
+                // START SJSWS 6324431
+                String msg = "  Checking constraint '" + constraint +
+                        "' against " + method + " " + origUri +
+                        " --> " +
+                        constraint.included(uri, method,
+                                caseSensitiveMapping);
+                log.log(Level.FINEST, msg);
+                // END SJSWS 6324431
+            }
+            /* SJSWS 6324431
+            if (log.isDebugEnabled() && constraints[i].included(
+                    uri, method)) {
+                log.debug("  Matched constraint '" + constraints[i] +
+                    "' against " + method + " " + uri);
+            }
+            */
+            // START SJSWS 6324431
+            if (log.isLoggable(Level.FINE) &&
+                    constraint.included(uri, method,
+                                        caseSensitiveMapping)) {
+
+                log.log(Level.FINE, "  Matched constraint '" + constraint +
+                        "' against " + method + " " + origUri);
+            }
+            // END SJSWS 6324431
+
+            boolean matched = false;
+            int pos = -1;
+            for (int j=0; j < collection.length; j++){
+                String [] patterns = collection[j].findPatterns();
+                // If patterns is null, continue to avoid an NPE
+                // See Bugzilla 30624
+                if (patterns == null) {
+                    continue;
+                }
+
+                for(int k=0; k < patterns.length && !matched; k++) {
+                    /* SJSWS 6324431
+                    String pattern = patterns[k];
+                    */
+                    // START SJSWS 6324431
+                    String pattern = caseSensitiveMapping ? 
+                        patterns[k]:patterns[k].toLowerCase(Locale.ENGLISH);
+                    // END SJSWS 6324431
+                    if (uri != null && pattern.startsWith("*.")){
+                        int slash = uri.lastIndexOf("/");
+                        int dot = uri.lastIndexOf(".");
+                        if (slash >= 0 && dot > slash &&
+                                dot != uri.length()-1 &&
+                                uri.length()-dot == pattern.length()-1) {
+                            if (pattern.regionMatches(
+                                    1,uri,dot,uri.length()-dot)) {
+                                matched = true;
+                                pos = j;
+                            }
+                        }
+                    }
+                }
+            }
+    
+            if (matched) {
+                found = true;
+                if (collection[pos].findMethod(method)) {
+                    if(results == null) {
+                        results = new ArrayList<SecurityConstraint>();
+                    }
+                    results.add(constraint);
+                }
+            }
+        } // while
+
+        if (found) {
+            return resultsToArray(results);
+        }
+
+        i = constraints.iterator(); 
+        while (i.hasNext()) {
+            SecurityConstraint constraint = i.next();
+            SecurityCollection[] collection = constraint.findCollections();
+            
+            // If collection is null, continue to avoid an NPE
+            // See Bugzilla 30624
+            if (collection == null) {
+                continue;
+            }
+
+            if (log.isLoggable(Level.FINEST)) {
+                /* SJSWS 6324431
+                log.trace("  Checking constraint '" + constraints[i] +
+                    "' against " + method + " " + uri + " --> " +
+                    constraints[i].included(uri, method));
+                */
+                // START SJSWS 6324431
+                String msg = "  Checking constraint '" + constraint +
+                        "' against " + method + " " + origUri +
+                        " --> " +
+                        constraint.included(uri, method,
+                                caseSensitiveMapping);
+                log.log(Level.FINEST, msg);
+                // END SJSWS 6324431
+            }
+            /* SJSWS 6324431
+            if (log.isDebugEnabled() && constraints[i].included(
+                    uri, method)) {
+                log.debug("  Matched constraint '" + constraints[i] +
+                    "' against " + method + " " + uri);
+            }
+            */
+            // START SJSWS 6324431
+            if (log.isLoggable(Level.FINE) &&
+                    constraint.included(uri, method,
+                                        caseSensitiveMapping)) {
+                log.log(Level.FINE, "  Matched constraint '" + constraint +
+                        "' against " + method + " " + origUri);
+            }
+            // END SJSWS 6324431
+
+            for (int j=0; j < collection.length; j++){
+                String[] patterns = collection[j].findPatterns();
+
+                // If patterns is null, continue to avoid an NPE
+                // See Bugzilla 30624
+                if (patterns == null) {
+                    continue;
+                }
+
+                boolean matched = false;
+                for (int k=0; k < patterns.length && !matched; k++) {
+                    /* SJSWS 6324431
+                    String pattern = patterns[k];
+                    */
+                    // START SJSWS 6324431
+                    String pattern = caseSensitiveMapping ? 
+                        patterns[k]:patterns[k].toLowerCase(Locale.ENGLISH);
+                    // END SJSWS 6324431
+                    if (pattern.equals("/")){
+                        matched = true;
+                    }
+                }
+                if (matched) {
+                    if (results == null) {
+                        results = new ArrayList<SecurityConstraint>();
+                    }                    
+                    results.add(constraint);
+                }
+            }
+        } // while
+
+        if (results == null) {
+            // No applicable security constraint was found
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "  No applicable constraint located");
+        }
+
+        return resultsToArray(results);
+    }
+ 
+    /**
+     * Convert an ArrayList to a SecurityContraint [].
+     */
+    private SecurityConstraint [] resultsToArray(ArrayList<SecurityConstraint> results) {
+        if(results == null) {
+            return null;
+        }
+        SecurityConstraint [] array = new SecurityConstraint[results.size()];
+        results.toArray(array);
+        return array;
+    }
+
+    
+    /**
+     * Perform access control based on the specified authorization constraint.
+     * Return <code>true</code> if this constraint is satisfied and processing
+     * should continue, or <code>false</code> otherwise.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraints Security constraint we are enforcing
+     * @param context The Context to which client of this class is attached.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean hasResourcePermission(HttpRequest request,
+                                         HttpResponse response,
+                                         SecurityConstraint []constraints,
+                                         Context context)
+        throws IOException {
+
+        if (constraints == null || constraints.length == 0)
+            return (true);
+
+        // Which user principal have we already authenticated?
+        Principal principal = ((HttpServletRequest)request.getRequest())
+                                                            .getUserPrincipal();
+        for(int i=0; i < constraints.length; i++) {
+            SecurityConstraint constraint = constraints[i];
+            String roles[] = constraint.findAuthRoles();
+            if (roles == null)
+                roles = new String[0];
+
+            if (constraint.getAllRoles())
+                return (true);
+
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "  Checking roles " + principal);
+
+            if (roles.length == 0) {
+                if(constraint.getAuthConstraint()) {
+
+                    // BEGIN S1AS 4878272
+                    ((HttpServletResponse) response.getResponse()).sendError
+                        (HttpServletResponse.SC_FORBIDDEN);
+                    response.setDetailMessage(rb.getString(LogFacade.ACCESS_RESOURCE_DENIED));
+                    // END S1AS 4878272
+
+                    if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "No roles ");
+                    return (false); // No listed roles means no access at all
+                } else {
+                    if (log.isLoggable(Level.FINE)) {
+                        log.log(Level.FINE, "Passing all access");
+                    }
+                    return (true);
+                }
+            } else if (principal == null) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "  No user authenticated, cannot grant access");
+
+                // BEGIN S1AS 4878272
+                ((HttpServletResponse) response.getResponse()).sendError
+                    (HttpServletResponse.SC_FORBIDDEN);
+                response.setDetailMessage(rb.getString(LogFacade.CONFIG_ERROR_NOT_AUTHENTICATED));
+                // END S1AS 4878272
+                return (false);
+            }
+
+
+            for (int j = 0; j < roles.length; j++) {
+                if (hasRole(principal, roles[j])) {
+                    if (log.isLoggable(Level.FINE))
+                        log.log(Level.FINE, "Role found:  " + roles[j]);
+                    return (true);
+                } else {
+                    if (log.isLoggable(Level.FINE))
+                        log.log(Level.FINE, "No role found:  " + roles[j]);
+                }
+            }
+        }
+        // Return a "Forbidden" message denying access to this resource
+        /* S1AS 4878272
+        ((HttpServletResponse) response.getResponse()).sendError
+        */
+        // BEGIN S1AS 4878272
+        ((HttpServletResponse) response.getResponse()).sendError
+            (HttpServletResponse.SC_FORBIDDEN);
+        response.setDetailMessage(rb.getString(LogFacade.ACCESS_RESOURCE_DENIED));
+        // END S1AS 4878272
+        return (false);
+
+    }
+    
+    //START SJSAS 6232464
+    /**
+     * Return <code>true</code> if the specified Principal has the specified
+     * security role, within the context of this Realm; otherwise return
+     * <code>false</code>.  This method can be overridden by Realm
+     * implementations. The default implementation is to forward to
+     * hasRole(Principal principal, String role).
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param principal Principal for whom the role is to be checked
+     * @param role Security role to be checked
+     */
+    public boolean hasRole(HttpRequest request, 
+                           HttpResponse response, 
+                           Principal principal, 
+                           String role) {
+        return hasRole(principal, role);
+    }
+    //END SJSAS 6232464
+    
+    //START SJSAS 6202703
+    /**
+     * Checks whether or not authentication is needed.
+     * Returns an int, one of AUTHENTICATE_NOT_NEEDED, AUTHENTICATE_NEEDED,
+     * or AUTHENTICATED_NOT_AUTHORIZED.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraints Security constraint we are enforcing
+     * @param disableProxyCaching whether or not to disable proxy caching for
+     *        protected resources.
+     * @param securePagesWithPragma true if we add headers which 
+     * are incompatible with downloading office documents in IE under SSL but
+     * which fix a caching problem in Mozilla.
+     * @param ssoEnabled true if sso is enabled
+     * @exception IOException if an input/output error occurs
+     */
+    public int preAuthenticateCheck(HttpRequest request,
+                                    HttpResponse response,
+                                    SecurityConstraint[] constraints,
+                                    boolean disableProxyCaching,
+                                    boolean securePagesWithPragma,
+                                    boolean ssoEnabled)
+                                    throws IOException {
+        for(int i=0; i < constraints.length; i++) {
+            if (constraints[i].getAuthConstraint()) {
+                disableProxyCaching(request, response, disableProxyCaching, securePagesWithPragma);
+                return Realm.AUTHENTICATE_NEEDED;
+            }
+        }
+        return Realm.AUTHENTICATE_NOT_NEEDED;
+    }
+    
+    
+    /**
+     * Authenticates the user making this request, based on the specified
+     * login configuration.  Return <code>true</code> if any specified
+     * requirements have been satisfied, or <code>false</code> if we have
+     * created a response challenge already.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param context The Context to which client of this class is attached.
+     * @param authenticator the current authenticator.
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean invokeAuthenticateDelegate(HttpRequest request,
+                                              HttpResponse response,
+                                              Context context,
+                                              Authenticator authenticator,
+                                              boolean calledFromAuthenticate)
+          throws IOException {
+        LoginConfig config = context.getLoginConfig();
+        return ((AuthenticatorBase) authenticator).authenticate(
+                        request, response, config);
+    }
+    
+    /**
+     * Post authentication for given request and response.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param context The Context to which client of this class is attached.
+     * @exception IOException if an input/output error occurs
+     */
+    public boolean invokePostAuthenticateDelegate(HttpRequest request,
+                                              HttpResponse response,
+                                              Context context)
+          throws IOException {
+         return true;
+    }
+    
+    //END SJSAS 6202703
+
+    /**
+     * Return <code>true</code> if the specified Principal has the specified
+     * security role, within the context of this Realm; otherwise return
+     * <code>false</code>.  This method can be overridden by Realm
+     * implementations, but the default is adequate when an instance of
+     * <code>GenericPrincipal</code> is used to represent authenticated
+     * Principals from this Realm.
+     *
+     * @param principal Principal for whom the role is to be checked
+     * @param role Security role to be checked
+     */
+    public boolean hasRole(Principal principal, String role) {
+
+        // Should be overridden in JAASRealm - to avoid pretty inefficient conversions
+        if ((principal == null) || (role == null) ||
+            !(principal instanceof GenericPrincipal))
+            return (false);
+
+        GenericPrincipal gp = (GenericPrincipal) principal;
+        if (!(gp.getRealm() == this)) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Different realm " + this + " " + gp.getRealm());
+            }
+        }
+        boolean result = gp.hasRole(role);
+        if (log.isLoggable(Level.FINE)) {
+            String name = principal.getName();
+            if (result) {
+                log.log(Level.FINE, LogFacade.USERNAME_HAS_ROLE, new Object[] {name, role});
+            }
+            else {
+                log.log(Level.FINE, LogFacade.USERNAME_NOT_HAVE_ROLE, new Object[] {name, role});
+            }
+        }
+        return (result);
+
+    }
+
+    
+    /**
+     * Enforce any user data constraint required by the security constraint
+     * guarding this request URI.
+     *
+     * @param request Request we are processing
+     * @param response Response we are creating
+     * @param constraints Security constraint being checked
+     *
+     * @exception IOException if an input/output error occurs
+     * 
+     * @return <code>true</code> if this constraint was not violated and
+     * processing should continue, or <code>false</code> if we have created
+     * a response already
+     */
+    public boolean hasUserDataPermission(HttpRequest request,
+                                         HttpResponse response,
+                                         SecurityConstraint[] constraints)
+        throws IOException {
+        return hasUserDataPermission(request,response,constraints,null,null);
+    }
+
+    /**
+     * Checks if the given request URI and method are the target of any
+     * user-data-constraint with a transport-guarantee of CONFIDENTIAL,
+     * and whether any such constraint is already satisfied.
+     * 
+     * If <tt>uri</tt> and <tt>method</tt> are null, then the URI and method
+     * of the given <tt>request</tt> are checked.
+     *
+     * If a user-data-constraint exists that is not satisfied, then the 
+     * given <tt>request</tt> will be redirected to HTTPS.
+     *
+     * @param request the request that may be redirected
+     * @param response the response that may be redirected
+     * @param constraints the security constraints to check against
+     * @param uri the request URI (minus the context path) to check
+     * @param method the request method to check
+     *
+     * @return true if the request URI and method are not the target of any
+     * unsatisfied user-data-constraint with a transport-guarantee of
+     * CONFIDENTIAL, and false if they are (in which case the given request
+     * will have been redirected to HTTPS)
+     */
+    public boolean hasUserDataPermission(HttpRequest request,
+                                         HttpResponse response,
+                                         SecurityConstraint[] constraints,
+                                         String uri,
+                                         String method)
+        throws IOException {
+        // Is there a relevant user data constraint?
+        if (constraints == null || constraints.length == 0) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "  No applicable security constraint defined");
+            return (true);
+        }
+
+        for(int i=0; i < constraints.length; i++) {
+            SecurityConstraint constraint = constraints[i];
+            String userConstraint = constraint.getUserConstraint();
+            if (userConstraint == null) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "  No applicable user data constraint defined");
+                return (true);
+            }
+            if (userConstraint.equals(Constants.NONE_TRANSPORT)) {
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "  User data constraint has no restrictions");
+                return (true);
+            }
+
+        }
+
+        // Validate the request against the user data constraint
+        if (request.getRequest().isSecure()) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "  User data constraint already satisfied");
+            return (true);
+        }
+
+        // Initialize variables we need to determine the appropriate action
+        HttpServletRequest hrequest = (HttpServletRequest)
+            request.getRequest();
+        HttpServletResponse hresponse = (HttpServletResponse)
+            response.getResponse();
+        int redirectPort = request.getConnector().getRedirectPort();
+
+        // Is redirecting disabled?
+        if (redirectPort <= 0) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "  SSL redirect is disabled");
+            /* S1AS 4878272
+            hresponse.sendError
+            response.sendError
+                (HttpServletResponse.SC_FORBIDDEN,
+                 hrequest.getRequestURI());
+            */
+            // BEGIN S1AS 4878272
+            hresponse.sendError(HttpServletResponse.SC_FORBIDDEN);
+            response.setDetailMessage(hrequest.getRequestURI());
+            // END S1AS 4878272
+            return (false);
+        }
+
+        // Redirect to the corresponding SSL port
+        StringBuilder file = new StringBuilder();
+        String protocol = "https";
+        String host = hrequest.getServerName();
+        // Protocol
+        file.append(protocol).append("://").append(host);
+        // Host with port
+        if(redirectPort != 443) {
+            file.append(":").append(redirectPort);
+        }
+        // URI
+        file.append(hrequest.getRequestURI());
+        String requestedSessionId = hrequest.getRequestedSessionId();
+        if ((requestedSessionId != null) &&
+            hrequest.isRequestedSessionIdFromURL()) {
+            String sessionParameterName = ((request.getContext() != null)?
+                    request.getContext().getSessionParameterName() :
+                    Globals.SESSION_PARAMETER_NAME);
+            file.append(";" + sessionParameterName + "=");
+            file.append(requestedSessionId);
+        }
+        String queryString = hrequest.getQueryString();
+        if (queryString != null) {
+            file.append('?');
+            file.append(queryString);
+        }
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Redirecting to " + file.toString());
+        hresponse.sendRedirect(file.toString());
+
+        return (false);
+    }
+    
+    
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+        support.removePropertyChangeListener(listener);
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this Realm.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called before any of the public
+     * methods of this component are utilized.  It should also send a
+     * LifecycleEvent of type START_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.FINE, LogFacade.REALM_BEEN_STARTED);
+            }
+            return;
+        }
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Create a MessageDigest instance for credentials, if desired
+        if (digest != null) {
+            try {
+                md = MessageDigest.getInstance(digest);
+            } catch (NoSuchAlgorithmException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_ALGORITHM_EXCEPTION),
+                                                  digest);
+                throw new LifecycleException(msg, e);
+            }
+        }
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.  It should also send a LifecycleEvent
+     * of type STOP_EVENT to any registered listeners.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop()
+        throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.REALM_NOT_BEEN_STARTED);
+            }
+            return;
+        }
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Clean up allocated resources
+        md = null;
+        
+        destroy();
+    
+    }
+    
+    public void destroy() {
+        // no op
+    }
+
+    @Override
+    public void logout(HttpRequest hreq) {
+        // no-op
+    }
+
+    @Override
+    public boolean isSecurityExtensionEnabled(ServletContext servletContext) {
+        return false;
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Digest the password using the specified algorithm and
+     * convert the result to a corresponding hexadecimal string.
+     * If exception, the plain credentials string is returned.
+     *
+     * @param credentials Password or other credentials to use in
+     *  authenticating this username
+     */
+    protected char[] digest(char[] credentials)  {
+
+        // If no MessageDigest instance is specified, return unchanged
+        if (hasMessageDigest() == false)
+            return (credentials);
+
+        // Digest the user credentials and return as hexadecimal
+        synchronized (this) {
+            try {
+                md.reset();
+    
+                byte[] bytes = null;
+                try {
+                    bytes = Utility.convertCharArrayToByteArray(
+                            credentials, getDigestEncoding());
+                } catch(CharacterCodingException cce) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.ILLEGAL_DIGEST_ENCODING_EXCEPTION),
+                                                      getDigestEncoding());
+                    log.log(Level.SEVERE, msg, cce);
+                        throw new IllegalArgumentException(cce.getMessage());
+                }
+                md.update(bytes);
+
+                return (HexUtils.convert(md.digest()));
+            } catch (Exception e) {
+                log.log(Level.SEVERE, LogFacade.ERROR_DIGESTING_USER_CREDENTIAL_EXCEPTION, e);
+                return (credentials);
+            }
+        }
+
+    }
+
+    protected boolean hasMessageDigest() {
+        return !(md == null);
+    }
+
+    /**
+     * Return the digest associated with given principal's user name.
+     */
+    protected char[] getDigest(String username, String realmName) {
+        if (sha256Helper == null) {
+            try {
+                sha256Helper = MessageDigest.getInstance("SHA-256");
+            } catch (NoSuchAlgorithmException e) {
+                log.log(Level.SEVERE, LogFacade.CANNOT_GET_MD5_DIGEST_EXCEPTION, e);
+                throw new IllegalStateException(e.getMessage());
+            }
+        }
+
+    	if (hasMessageDigest()) {
+    		// Use pre-generated digest
+    		return getPassword(username);
+    	}
+    	
+        char[] pwd = getPassword(username);
+        int usernameLength = ((username != null) ? username.length() : 0);
+        int realmNameLength = ((realmName != null) ? realmName.length() : 0);
+        int pwdLength = ((pwd != null) ? pwd.length : 0);
+
+        // digestValue = username:realmName:pwd
+        char[] digestValue = new char[usernameLength + 1 + realmNameLength + 1 + pwdLength];
+        int ind = 0;
+        if (username != null) {
+            System.arraycopy(username.toCharArray(), 0, digestValue, 0, usernameLength);
+            ind = usernameLength;
+        }
+        digestValue[ind++] = ':';
+        if (realmName != null) {
+            System.arraycopy(realmName.toCharArray(), 0, digestValue, ind, realmNameLength);
+            ind += realmNameLength;
+        }
+        digestValue[ind++] = ':';
+        if (pwd != null) {
+            System.arraycopy(pwd, 0, digestValue, ind, pwdLength);
+        }
+
+        byte[] valueBytes = null;
+        try {
+            valueBytes = Utility.convertCharArrayToByteArray(
+                digestValue, getDigestEncoding());
+        } catch(CharacterCodingException cce) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ILLEGAL_DIGEST_ENCODING_EXCEPTION),
+                                             getDigestEncoding());
+            log.log(Level.SEVERE, msg, cce);
+            throw new IllegalArgumentException(cce.getMessage());
+        }
+
+        byte[] digest = null;
+        // Bugzilla 32137
+        synchronized(sha256Helper) {
+            digest = sha256Helper.digest(valueBytes);
+        }
+
+        return new String(digest).toCharArray();
+    }
+
+
+    /**
+     * Return a short name for this Realm implementation, for use in
+     * log messages.
+     */
+    protected abstract String getName();
+
+
+    /**
+     * Return the password associated with the given principal's user name.
+     */
+    protected abstract char[] getPassword(String username);
+
+
+    /**
+     * Return the Principal associated with the given user name.
+     */
+    protected abstract Principal getPrincipal(String username);
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+        message = neutralizeForLog(message);
+        org.apache.catalina.Logger logger = null;
+        String name = null;
+        if (container != null) {
+            logger = container.getLogger();
+            name = container.getName();
+        }
+        if (logger != null) {
+            logger.log(getName()+"[" + name + "]: " + message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, getName()+"[" + name + "]: " + message);
+            }
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    protected void log(String message, Throwable t) {
+        message = neutralizeForLog(message);
+        org.apache.catalina.Logger logger = null;
+        String name = null;
+        if (container != null) {
+            logger = container.getLogger();
+            name = container.getName();
+        }
+        if (logger != null) {
+            logger.log(getName()+"[" + name + "]: " + message, t,
+                org.apache.catalina.Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, getName()+"[" + name + "]: " + message, t);
+        }
+    }
+    
+    //START SJSAS 6202703
+    protected void disableProxyCaching(HttpRequest request,
+                                       HttpResponse response, 
+                                       boolean disableProxyCaching,
+                                       boolean securePagesWithPragma) {
+        HttpServletRequest hsrequest = (HttpServletRequest) request.getRequest();
+        // Make sure that constrained resources are not cached by web proxies
+        // or browsers as caching can provide a security hole
+        if (disableProxyCaching
+                && !"POST".equalsIgnoreCase(hsrequest.getMethod())
+                && (!checkIfRequestIsSecure || !hsrequest.isSecure())) {
+            HttpServletResponse sresponse =
+                    (HttpServletResponse) response.getResponse();
+            if (securePagesWithPragma) {
+                // FIXME: These cause problems with downloading office docs
+                // from IE under SSL and may not be needed for newer Mozilla
+                // clients.
+                sresponse.setHeader("Pragma", "No-cache");
+                sresponse.setHeader("Cache-Control", "no-cache");
+            } else {
+                sresponse.setHeader("Cache-Control", "private");
+            }
+            sresponse.setHeader("Expires", DATE_ONE);
+        }
+    }
+    //END SJSAS 6202703
+
+
+    // -------------------- JMX and Registration  --------------------
+
+    protected ObjectName controller;
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+    // BEGIN IASRI 4808401, 4934562
+    /**
+     * Return an alternate principal from the request if available.
+     * Tomcat realms do not implement this so always return null as default.
+     *
+     * @param req The request object.
+     * @return Alternate principal or null.
+     */
+    public Principal getAlternatePrincipal(HttpRequest req) {
+        return null;
+    }
+
+        
+    /**
+     * Return an alternate auth type from the request if available.
+     * Tomcat realms do not implement this so always return null as default.
+     *
+     * @param req The request object.
+     * @return Alternate auth type or null.
+     */
+    public String getAlternateAuthType(HttpRequest req) {
+        return null;
+    }
+    // END IASRI 4808401
+
+
+    // BEGIN IASRI 4856062,4918627,4874504
+    /**
+     * Set the name of the associated realm.
+     *
+     * @param name the name of the realm.
+     */
+    public void setRealmName(String name, String authMethod) {
+        // DO NOTHING. PRIVATE EXTENSION
+    }
+
+
+    /**
+     * Returns the name of the associated realm.
+     *
+     * @return realm name or null if not set.
+     */
+    public String getRealmName(){
+        // DO NOTHING. PRIVATE EXTENSION
+        return null;
+    }
+    // END IASRI 4856062,4918627,4874504
+    public Principal authenticate(HttpServletRequest hreq) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    private boolean equalsIgnoreCase(char[] arr1, char[] arr2) {
+        if (arr1 == null) {
+            return (arr2 == null);
+        } else { // arr1 is not null
+            if (arr2 == null || arr1.length != arr2.length) {
+                return false;
+            }
+        }
+        
+        //here, arr1 and arr2 are not null with equal length
+        boolean result = true;
+        for (int i = 0; i < arr1.length; i++) {
+            if (Character.toLowerCase(arr1[i]) != Character.toLowerCase(arr2[i])) {
+                return false;
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/realm/package.html b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/package.html
new file mode 100644
index 0000000..e63f6b3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/realm/package.html
@@ -0,0 +1,83 @@
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<body>
+
+<p>This package contains <code>Realm</code> implementations for the
+various supported realm technologies for authenticating users and
+identifying their associated roles.  The <code>Realm</code> that is
+associated with a web application's <code>Context</code> (or a hierarchically
+superior Container) is used to resolve authentication and role presence
+questions when a web application uses container managed security as described
+in the Servlet API Specification, version 2.2.</p>
+
+<p>The implementations share a common base class that supports basic
+functionality for all of the standard <code>Realm</code> implementations,
+and can be configured by setting the following properties (default values
+are in square brackets):</p>
+<ul>
+<li><b>debug</b> - Debugging detail level for this component. [0]</li>
+</ul>
+
+<p>The standard <code>Realm</code> implementations that are currently
+available include the following (with additional configuration properties
+as specified):</p>
+<ul>
+<li><b>JDBCRealm</b> - Implementation of <code>Realm</code> that operates
+    from data stored in a relational database that is accessed via a JDBC
+    driver.  The name of the driver, database connection information, and
+    the names of the relevant tables and columns are configured with the
+    following additional properties:
+    <ul>
+    <li><b>connectionURL</b> - The URL to use when connecting to this database.
+        [REQUIRED - NO DEFAULT]</li>
+    <li><b>driverName</b> - Fully qualified Java class name of the JDBC driver
+        to be used.  [REQUIRED - NO DEFAULT]</li>
+    <li><b>roleNameCol</b> - Name of the database column that contains role
+        names.  [REQUIRED - NO DEFAULT]</li>
+    <li><b>userCredCol</b> - Name of the database column that contains the
+        user's credentials (i.e. password) in cleartext.  [REQUIRED -
+        NO DEFAULT]</li>
+    <li><b>userNameCol</b> - Name of the database column that contains the
+        user's logon username.  [REQUIRED - NO DEFAULT]</li>
+    <li><b>userRoleTable</b> - Name of the database table containing user
+        role information.  This table must include the columns specified by
+        the <code>userNameCol</code> and <code>roleNameCol</code> properties.
+        [REQUIRED - NO DEFAULT]</li>
+    <li><b>userTable</b> - Name of the database table containing user
+        information.  This table must include the columns specified by the
+        <code>userNameCol</code> and <code>userCredCol</code> properties.
+        [REQUIRED - NO DEFAULT]</li>
+    </ul>
+    </li>
+<li><b>MemoryRealm</b> - Implementation of <code>Realm</code> that uses the
+    contents of a simple XML file (<code>conf/tomcat-users.xml</code>) as the
+    list of valid users and their roles.  This implementation is primarily to
+    demonstrate that the authentication technology functions correctly, and is
+    not anticipated as adequate for general purpose use.  This component
+    supports the following additional properties:
+    <ul>
+    <li><b>pathname</b> - Pathname of the XML file containing our user and
+        role information.  If a relative pathname is specified, it is resolved
+        against the pathname specified by the "catalina.home" system property.
+        [conf/tomcat-users.xml]</li>
+    </ul>
+</ul>
+
+</body>
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityClassLoad.java b/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityClassLoad.java
new file mode 100644
index 0000000..08c8eda
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityClassLoad.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.security;
+
+/**
+ * Static class used to preload java classes when using the
+ * Java SecurityManager so that the defineClassInPackage
+ * RuntimePermission does not trigger an AccessControlException.
+ *
+ * @author Glenn L. Nielsen
+ * @author Jean-Francois Arcand
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:54 $
+ */
+
+public final class SecurityClassLoad {
+
+    public static void securityClassLoad(ClassLoader loader)
+        throws Exception {
+
+        if( System.getSecurityManager() == null ){
+            return;
+        }
+        
+        loadCorePackage(loader);
+        loadLoaderPackage(loader);
+        loadServletsPackage(loader);
+        loadSessionPackage(loader);
+        loadUtilPackage(loader);
+        loadJavaxPackage(loader);
+        loadCoyotePackage(loader);        
+        loadHttp11Package(loader);        
+        loadTomcatPackage(loader);
+    }
+    
+    
+    private final static void loadCorePackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.";
+        loader.loadClass
+            (basePackage +
+             "core.ApplicationContextFacade$1");
+        loader.loadClass
+            (basePackage +
+             "core.ApplicationDispatcher$PrivilegedForward");
+        loader.loadClass
+            (basePackage +
+             "core.ApplicationDispatcher$PrivilegedInclude");
+        loader.loadClass
+            (basePackage +
+             "core.ContainerBase$PrivilegedAddChild");
+        loader.loadClass
+            (basePackage +
+             "core.StandardWrapper$1");
+    }
+    
+    
+    private final static void loadLoaderPackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.";
+        loader.loadClass
+            (basePackage +
+             "loader.WebappClassLoader$PrivilegedFindResource");
+    }
+
+    private static final void loadServletsPackage(ClassLoader loader)
+            throws Exception {
+        final String basePackage = "org.apache.catalina.servlets.";
+        // Avoid a possible memory leak in the DefaultServlet when running with
+        // a security manager. The DefaultServlet needs to load an XML parser
+        // when running under a security manager. We want this to be loaded by
+        // the container rather than a web application to prevent a memory leak
+        // via web application class loader.
+        loader.loadClass(basePackage + "DefaultServlet");
+    }
+    
+    
+    private final static void loadSessionPackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.session.";
+        loader.loadClass(basePackage + "StandardSession");
+        loader.loadClass(basePackage + "StandardSession$1");
+        loader.loadClass(basePackage + "StandardManager$PrivilegedDoUnload");
+    }
+    
+    
+    private final static void loadUtilPackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.";
+        loader.loadClass
+            (basePackage + "util.URL");
+        loader.loadClass(basePackage + "util.Enumerator");
+    }
+    
+    
+    private final static void loadJavaxPackage(ClassLoader loader)
+        throws Exception {
+        loader.loadClass("javax.servlet.http.Cookie");
+    }
+    
+
+    private final static void loadHttp11Package(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.glassfish.grizzly.tcp.http11.";
+        loader.loadClass(basePackage + "Http11Processor$1");
+        loader.loadClass(basePackage + "InternalOutputBuffer$1");
+        loader.loadClass(basePackage + "InternalOutputBuffer$2");
+    }
+    
+    
+    private final static void loadCoyotePackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.apache.catalina.connector.";
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetAttributePrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetParameterMapPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetRequestDispatcherPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetParameterPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetParameterNamesPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetParameterValuePrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetCharacterEncodingPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetHeadersPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetHeaderNamesPrivilegedAction");  
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetCookiesPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetLocalePrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetLocalesPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "ResponseFacade$SetContentTypePrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "RequestFacade$GetSessionPrivilegedAction");
+        loader.loadClass
+            (basePackage +
+             "ResponseFacade$1");
+        loader.loadClass
+            (basePackage +
+             "OutputBuffer$1");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$1");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$2");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$3");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$4");
+        loader.loadClass
+            (basePackage +
+             "CoyoteInputStream$5");
+        loader.loadClass
+            (basePackage +
+             "InputBuffer$1");
+        loader.loadClass
+            (basePackage +
+             "Response$1");
+        loader.loadClass
+            (basePackage +
+             "Response$2");
+        loader.loadClass
+            (basePackage +
+             "Response$3");
+    }
+
+    private final static void loadTomcatPackage(ClassLoader loader)
+        throws Exception {
+        String basePackage = "org.glassfish.grizzly.";
+        loader.loadClass(basePackage + "util.net.SSLSupport$CipherData");
+
+        // security
+        basePackage = "org.apache.tomcat.";
+        loader.loadClass(basePackage + "util.security.PrivilegedGetTccl");
+        loader.loadClass(basePackage + "util.security.PrivilegedSetTccl");
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityConfig.java
new file mode 100644
index 0000000..7f91b40
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityConfig.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.security;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.startup.CatalinaProperties;
+
+import java.security.Security;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Util class to protect Catalina against package access and insertion.
+ * The code are been moved from Catalina.java
+ * @author the Catalina.java authors
+ * @author Jean-Francois Arcand
+ */
+public final class SecurityConfig{
+    private static volatile SecurityConfig singleton = null;
+
+    private static final Logger log = LogFacade.getLogger();
+
+    
+    private final static String PACKAGE_ACCESS =  "sun.,"
+                                                + "org.apache.catalina." 
+                                                + ",org.apache.jasper."
+                                                + ",org.glassfish.grizzly.tcp."
+                                                + ",org.glassfish.grizzly.";
+    
+    private final static String PACKAGE_DEFINITION= "java.,sun."
+                                                + ",org.apache.catalina." 
+                                                + ",org.glassfish.grizzly.tcp."
+                                                + ",org.glassfish.grizzly."
+                                                + ",org.apache.jasper.";
+    /**
+     * List of protected package from conf/catalina.properties
+     */
+    private String packageDefinition;
+    
+    
+    /**
+     * List of protected package from conf/catalina.properties
+     */
+    private String packageAccess; 
+    
+    
+    /**
+     * Create a single instance of this class.
+     */
+    private SecurityConfig(){  
+        try{
+            packageDefinition = CatalinaProperties.getProperty("package.definition");
+            packageAccess = CatalinaProperties.getProperty("package.access");
+        } catch (java.lang.Exception ex){
+            if (log.isLoggable(Level.FINE)){
+                log.log(Level.FINE, "Unable to load properties using CatalinaProperties",
+                        ex); 
+            }            
+        }
+    }
+    
+    
+    /**
+     * Returns the singleton instance of that class.
+     * @return an instance of that class.
+     */
+    public static SecurityConfig newInstance(){
+        if (singleton == null){
+            singleton = new SecurityConfig();
+        }
+        return singleton;
+    }
+    
+    
+    /**
+     * Set the security package.access value.
+     */
+    public void setPackageAccess(){
+        // If catalina.properties is missing, protect all by default.
+        if (packageAccess == null){
+            setSecurityProperty("package.access", PACKAGE_ACCESS);   
+        } else {
+            setSecurityProperty("package.access", packageAccess);   
+        }
+    }
+    
+    
+    /**
+     * Set the security package.definition value.
+     */
+     public void setPackageDefinition(){
+        // If catalina.properties is missing, protect all by default.
+         if (packageDefinition == null){
+            setSecurityProperty("package.definition", PACKAGE_DEFINITION);
+         } else {
+            setSecurityProperty("package.definition", packageDefinition);
+         }
+    }
+     
+     
+    /**
+     * Set the proper security property
+     * @param properties the package.* property.
+     */
+    private final void setSecurityProperty(String properties, String packageList){
+        if (System.getSecurityManager() != null){
+            String definition = Security.getProperty(properties);
+            if( definition != null && definition.length() > 0 ){
+                definition += ",";
+            }
+
+            Security.setProperty(properties,
+                // FIX ME package "javax." was removed to prevent HotSpot
+                // fatal internal errors
+                definition + packageList);      
+        }
+    }
+    
+    
+}
+
+
+
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityUtil.java b/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityUtil.java
new file mode 100644
index 0000000..00266c3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/security/SecurityUtil.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.security;
+
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.util.StringManager;
+
+import javax.security.auth.Subject;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+/**
+ * This utility class associates a <code>Subject</code> to the current 
+ * <code>AccessControlContext</code>. When a <code>SecurityManager</code> is
+ * used, the container will always associate the called thread with an 
+ * AccessControlContext containing only the principal of the requested
+ * Servlet/Filter.
+ *
+ * This class uses reflection to invoke the invoke methods.
+ *
+ * @author Jean-Francois Arcand
+ */
+
+public final class SecurityUtil{
+    
+    private final static int INIT= 0;
+    private final static int SERVICE = 1;
+    private final static int DOFILTER = 1;
+    private final static int DESTROY = 2;
+    
+    private final static String INIT_METHOD = "init";
+    private final static String DOFILTER_METHOD = "doFilter";
+    private final static String SERVICE_METHOD = "service";
+    private final static String DESTROY_METHOD = "destroy";
+   
+    /**
+     * Cache every object for which we are creating method on it.
+     */
+    private static HashMap<Object, Method[]> objectCache =
+        new HashMap<Object, Method[]>();
+        
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    private static boolean packageDefinitionEnabled = (
+         System.getProperty("package.definition") == null ||
+         System.getProperty("package.definition").equals("")) ? false : true;
+    
+    // START SJS WS 7.0 6236329
+    /**
+     * Do we need to execute all invokation under a Subject.doAs call.
+     */
+    public static final boolean executeUnderSubjectDoAs = true;
+    // END SJS WS 7.0 6236329
+
+    
+    /**
+     * Perform work as a particular <code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     */
+    public static void doAsPrivilege(final String methodName, 
+                                     final Servlet targetObject) throws java.lang.Exception{
+         doAsPrivilege(methodName, targetObject, null, null, null);                                
+    }
+
+    
+    /**
+     * Perform work as a particular <code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     * @param targetType <code>Class</code> array used to instantiate a i
+     * <code>Method</code> object.
+     * @param targetObject <code>Object</code> array contains the runtime 
+     * parameters instance.
+     */
+    public static void doAsPrivilege(final String methodName, 
+                                     final Servlet targetObject, 
+                                     final Class<?>[] targetType,
+                                     final Object[] targetArguments) 
+        throws java.lang.Exception{    
+
+         doAsPrivilege(methodName, 
+                       targetObject, 
+                       targetType, 
+                       targetArguments, 
+                       null);                                
+    }
+    
+    
+    /**
+     * Perform work as a particular <code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     * @param targetType <code>Class</code> array used to instantiate a 
+     * <code>Method</code> object.
+     * @param targetArguments <code>Object</code> array contains the
+     * runtime parameters instance.
+     * @param principal the <code>Principal</code> to which the security 
+     * privilege apply..
+     */    
+    public static void doAsPrivilege(final String methodName, 
+                                     final Servlet targetObject, 
+                                     final Class<?>[] targetType,
+                                     final Object[] targetArguments,
+                                     Principal principal) 
+        throws java.lang.Exception{
+
+        Method method = null;
+        Method[] methodsCache = null;
+        if(objectCache.containsKey(targetObject)){
+            methodsCache = objectCache.get(targetObject);
+            method = findMethod(methodsCache, methodName);
+            if (method == null){
+                method = createMethodAndCacheIt(methodsCache,
+                                                methodName,
+                                                targetObject,
+                                                targetType);
+            }
+        } else {
+            method = createMethodAndCacheIt(methodsCache,
+                                            methodName,
+                                            targetObject,
+                                            targetType);                     
+        }
+
+        execute(method, targetObject, targetArguments, principal);
+    }
+ 
+    
+    /**
+     * Perform work as a particular <code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Filter</code> on which the method will 
+     * be called.
+     */    
+    public static void doAsPrivilege(final String methodName, 
+                                     final Filter targetObject) 
+        throws java.lang.Exception{
+
+         doAsPrivilege(methodName, targetObject, null, null);                                
+    }
+ 
+    
+    /**
+     * Perform work as a particular <code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Filter</code> on which the method will 
+     * be called.
+     * @param targetType <code>Class</code> array used to instantiate a
+     * <code>Method</code> object.
+     * @param targetArguments <code>Object</code> array contains the
+     * runtime parameters instance.
+     */    
+    public static void doAsPrivilege(final String methodName, 
+                                     final Filter targetObject, 
+                                     final Class<?>[] targetType,
+                                     final Object[] targetArguments) 
+        throws java.lang.Exception{
+
+        doAsPrivilege(
+                methodName, targetObject, targetType, targetArguments, null);
+    }
+    
+    /**
+     * Perform work as a particular <code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Filter</code> on which the method will 
+     * be called.
+     * @param targetType <code>Class</code> array used to instantiate a
+     * <code>Method</code> object.
+     * @param targetArguments <code>Object</code> array contains the 
+     * runtime parameters instance.
+     * @param principal the <code>Principal</code> to which the security 
+     * privilege apply
+     */    
+    public static void doAsPrivilege(final String methodName, 
+                                     final Filter targetObject, 
+                                     final Class[] targetType,
+                                     final Object[] targetArguments,
+                                     Principal principal) 
+        throws java.lang.Exception{
+
+        Method method = null;
+
+        Method[] methodsCache = null;
+        if(objectCache.containsKey(targetObject)){
+            methodsCache = objectCache.get(targetObject);
+            method = findMethod(methodsCache, methodName);
+            if (method == null){
+                method = createMethodAndCacheIt(methodsCache,
+                                                methodName,
+                                                targetObject,
+                                                targetType);
+            }
+        } else {
+            method = createMethodAndCacheIt(methodsCache,
+                                            methodName,
+                                            targetObject,
+                                            targetType);                     
+        }
+
+        execute(method, targetObject, targetArguments, principal);
+    }
+    
+    
+    /**
+     * Perform work as a particular <code>Subject</code>. Here the work
+     * will be granted to a <code>null</code> subject. 
+     *
+     * @param method the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     * @param targetArguments <code>Object</code> array contains the
+     * runtime parameters instance.
+     * @param principal the <code>Principal</code> to which the security 
+     * privilege apply..
+     */    
+    private static void execute(final Method method,
+                                final Object targetObject, 
+                                final Object[] targetArguments,
+                                Principal principal) 
+        throws java.lang.Exception{
+       
+        try{   
+            Subject subject = null;
+            PrivilegedExceptionAction<Void> pea =
+                new PrivilegedExceptionAction<Void>(){
+                    public Void run() throws Exception{
+                       method.invoke(targetObject, targetArguments);
+                       return null;
+                    }
+            };
+
+            // The first argument is always the request object
+            if (targetArguments != null 
+                    && targetArguments[0] instanceof HttpServletRequest){
+                HttpServletRequest request = 
+                    (HttpServletRequest)targetArguments[0];
+
+                boolean hasSubject = false;
+                HttpSession session = request.getSession(false);
+                if (session != null){
+                    subject = 
+                        (Subject)session.getAttribute(Globals.SUBJECT_ATTR);
+                    hasSubject = (subject != null);
+                }
+
+                if (subject == null){
+                    subject = new Subject();
+                    
+                    if (principal != null){
+                        subject.getPrincipals().add(principal);
+                    }
+                }
+
+                if (session != null && !hasSubject) {
+                    session.setAttribute(Globals.SUBJECT_ATTR, subject);
+                }
+
+            }
+
+            Subject.doAsPrivileged(subject, pea, null);       
+        } catch( PrivilegedActionException pe) {
+            Throwable e;
+            if (pe.getException() instanceof InvocationTargetException) {
+                e = ((InvocationTargetException)pe.getException())
+                                .getTargetException();
+            } else {
+                e = pe;
+            }
+            
+            if (log.isLoggable(Level.FINE)){
+                log.log(Level.FINE, LogFacade.PRIVILEGE_ACTION_EXCEPTION, e);
+            }
+            
+            if (e instanceof UnavailableException)
+                throw (UnavailableException) e;
+            else if (e instanceof ServletException)
+                throw (ServletException) e;
+            else if (e instanceof IOException)
+                throw (IOException) e;
+            else if (e instanceof RuntimeException)
+                throw (RuntimeException) e;
+            else
+                throw new ServletException(e.getMessage(), e);
+        }  
+    }
+    
+    
+    /**
+     * Find a method stored within the cache.
+     * @param methodsCache the cache used to store method instance
+     * @param methodName the method to apply the security restriction
+     * @return the method instance, null if not yet created.
+     */
+    private static Method findMethod(Method[] methodsCache,
+                                     String methodName){
+        if (methodName.equalsIgnoreCase(INIT_METHOD) 
+                && methodsCache[INIT] != null){
+            return methodsCache[INIT];
+        } else if (methodName.equalsIgnoreCase(DESTROY_METHOD) 
+                && methodsCache[DESTROY] != null){
+            return methodsCache[DESTROY];            
+        } else if (methodName.equalsIgnoreCase(SERVICE_METHOD) 
+                && methodsCache[SERVICE] != null){
+            return methodsCache[SERVICE];
+        } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD) 
+                && methodsCache[DOFILTER] != null){
+            return methodsCache[DOFILTER];          
+        } 
+        return null;
+    }
+    
+    
+    /**
+     * Create the method and cache it for further re-use.
+     * @param methodsCache the cache used to store method instance
+     * @param methodName the method to apply the security restriction
+     * @param targetObject the <code>Servlet</code> on which the method will
+     * be called.
+     * @param targetType <code>Class</code> array used to instantiate a 
+     * <code>Method</code> object.
+     * @return the method instance.
+     */
+    private static Method createMethodAndCacheIt(Method[] methodsCache,
+                                                 String methodName,
+                                                 Object targetObject,
+                                                 Class<?>[] targetType) 
+            throws Exception{
+        
+        if ( methodsCache == null){
+            methodsCache = new Method[3];
+        }               
+                
+        Method method = 
+            targetObject.getClass().getMethod(methodName, targetType); 
+
+        if (methodName.equalsIgnoreCase(INIT_METHOD)){
+            methodsCache[INIT] = method;
+        } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)){
+            methodsCache[DESTROY] = method;
+        } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)){
+            methodsCache[SERVICE] = method;
+        } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)){
+            methodsCache[DOFILTER] = method;
+        } 
+         
+        objectCache.put(targetObject, methodsCache );
+                                           
+        return method;
+    }
+
+    
+    /**
+     * Remove the object from the cache.
+     */
+    public static void remove(Object cachedObject){
+        objectCache.remove(cachedObject);
+    }
+    
+    
+    /**
+     * Return the <code>SecurityManager</code> only if Security is enabled AND
+     * package protection mechanism is enabled.
+     */
+    public static boolean isPackageProtectionEnabled(){
+        if (packageDefinitionEnabled && Globals.IS_SECURITY_ENABLED) {
+            return true;
+        }
+        return false;
+    }
+    
+    
+    // START OF SJS WS 7.0 6236329
+    /**
+     * Return true if a <code>SecurityManager</code> is used and is 
+     * <code>isDoAsRequired</code> is required.
+     */
+    public static boolean executeUnderSubjectDoAs(){
+        if (executeUnderSubjectDoAs && Globals.IS_SECURITY_ENABLED) {
+            return true;
+        }
+        return false;
+    }    
+    // END OF SJS WS 7.0 6236329
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/CGIServlet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/CGIServlet.java
new file mode 100644
index 0000000..9d74d72
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/CGIServlet.java
@@ -0,0 +1,1989 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.servlets;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.util.IOTools;
+
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+
+/**
+ *  CGI-invoking servlet for web applications, used to execute scripts which
+ *  comply to the Common Gateway Interface (CGI) specification and are named
+ *  in the path-info used to invoke this servlet.
+ *
+ * <p>
+ * <i>Note: This code compiles and even works for simple CGI cases.
+ *          Exhaustive testing has not been done.  Please consider it beta
+ *          quality.  Feedback is appreciated to the author (see below).</i>
+ * </p>
+ * <p>
+ *
+ * <b>Example</b>:<br>
+ * If an instance of this servlet was mapped (using
+ *       <code>&lt;web-app&gt;/WEB-INF/web.xml</code>) to:
+ * </p>
+ * <p>
+ * <code>
+ * &lt;web-app&gt;/cgi-bin/*
+ * </code>
+ * </p>
+ * <p>
+ * then the following request:
+ * </p>
+ * <p>
+ * <code>
+ * http://localhost:8080/&lt;web-app&gt;/cgi-bin/dir1/script/pathinfo1
+ * </code>
+ * </p>
+ * <p>
+ * would result in the execution of the script
+ * </p>
+ * <p>
+ * <code>
+ * &lt;web-app-root&gt;/WEB-INF/cgi/dir1/script
+ * </code>
+ * </p>
+ * <p>
+ * with the script's <code>PATH_INFO</code> set to <code>/pathinfo1</code>.
+ * </p>
+ * <p>
+ * Recommendation:  House all your CGI scripts under
+ * <code>&lt;webapp&gt;/WEB-INF/cgi</code>.  This will ensure that you do not
+ * accidentally expose your cgi scripts' code to the outside world and that
+ * your cgis will be cleanly ensconced underneath the WEB-INF (i.e.,
+ * non-content) area.
+ * </p>
+ * <p>
+ * The default CGI location is mentioned above.  You have the flexibility to
+ * put CGIs wherever you want, however:
+ * </p>
+ * <p>
+ *   The CGI search path will start at
+ *   webAppRootDir + File.separator + cgiPathPrefix
+ *   (or webAppRootDir alone if cgiPathPrefix is
+ *   null).
+ * </p>
+ * <p>
+ *   cgiPathPrefix is defined by setting
+ *   this servlet's cgiPathPrefix init parameter
+ * </p>
+ *
+ * <p>
+ *
+ * <B>CGI Specification</B>:<br> derived from
+ * <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com</a>.
+ * A work-in-progress & expired Internet Draft.  Note no actual RFC describing
+ * the CGI specification exists.  Where the behavior of this servlet differs
+ * from the specification cited above, it is either documented here, a bug,
+ * or an instance where the specification cited differs from Best
+ * Community Practice (BCP).
+ * Such instances should be well-documented here.  Please email the
+ * <a href="mailto:tomcat-dev@jakarta.apache.org">Jakarta Tomcat group [tomcat-dev@jakarta.apache.org]</a>
+ * with amendments.
+ *
+ * </p>
+ * <p>
+ *
+ * <b>Canonical metavariables</b>:<br>
+ * The CGI specification defines the following canonical metavariables:
+ * <br>
+ * [excerpt from CGI specification]
+ * <PRE>
+ *  AUTH_TYPE
+ *  CONTENT_LENGTH
+ *  CONTENT_TYPE
+ *  GATEWAY_INTERFACE
+ *  PATH_INFO
+ *  PATH_TRANSLATED
+ *  QUERY_STRING
+ *  REMOTE_ADDR
+ *  REMOTE_HOST
+ *  REMOTE_IDENT
+ *  REMOTE_USER
+ *  REQUEST_METHOD
+ *  SCRIPT_NAME
+ *  SERVER_NAME
+ *  SERVER_PORT
+ *  SERVER_PROTOCOL
+ *  SERVER_SOFTWARE
+ * </PRE>
+ * <p>
+ * Metavariables with names beginning with the protocol name (<EM>e.g.</EM>,
+ * "HTTP_ACCEPT") are also canonical in their description of request header
+ * fields.  The number and meaning of these fields may change independently
+ * of this specification.  (See also section 6.1.5 [of the CGI specification].)
+ * </p>
+ * [end excerpt]
+ *
+ * </p>
+ * <h2> Implementation notes</h2>
+ * <p>
+ *
+ * <b>standard input handling</b>: If your script accepts standard input,
+ * then the client must start sending input within a certain timeout period,
+ * otherwise the servlet will assume no input is coming and carry on running
+ * the script.  The script's the standard input will be closed and handling of
+ * any further input from the client is undefined.  Most likely it will be
+ * ignored.  If this behavior becomes undesirable, then this servlet needs
+ * to be enhanced to handle threading of the spawned process' stdin, stdout,
+ * and stderr (which should not be too hard).
+ * <br>
+ * If you find your cgi scripts are timing out receiving input, you can set
+ * the init parameter <code></code> of your webapps' cgi-handling servlet
+ * to be
+ * </p>
+ * <p>
+ *
+ * <b>Metavariable Values</b>: According to the CGI specification,
+ * implementations may choose to represent both null or missing values in an
+ * implementation-specific manner, but must define that manner.  This
+ * implementation chooses to always define all required metavariables, but
+ * set the value to "" for all metavariables whose value is either null or
+ * undefined.  PATH_TRANSLATED is the sole exception to this rule, as per the
+ * CGI Specification.
+ *
+ * </p>
+ * <p>
+ *
+ * <b>NPH --  Non-parsed-header implementation</b>:  This implementation does
+ * not support the CGI NPH concept, whereby server ensures that the data
+ * supplied to the script are precisely as supplied by the client and
+ * unaltered by the server.
+ * </p>
+ * <p>
+ * The function of a servlet container (including Tomcat) is specifically
+ * designed to parse and possible alter CGI-specific variables, and as
+ * such makes NPH functionality difficult to support.
+ * </p>
+ * <p>
+ * The CGI specification states that compliant servers MAY support NPH output.
+ * It does not state servers MUST support NPH output to be unconditionally
+ * compliant.  Thus, this implementation maintains unconditional compliance
+ * with the specification though NPH support is not present.
+ * </p>
+ * <p>
+ *
+ * The CGI specification is located at
+ * <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com</a>.
+ *
+ * </p>
+ * <p>
+ * <h3>TODO:</h3>
+ * <ul>
+ * <li> Support for setting headers (for example, Location headers don't work)
+ * <li> Support for collapsing multiple header lines (per RFC 2616)
+ * <li> Ensure handling of POST method does not interfere with 2.3 Filters
+ * <li> Refactor some debug code out of core
+ * <li> Ensure header handling preserves encoding
+ * <li> Possibly rewrite CGIRunner.run()?
+ * <li> Possibly refactor CGIRunner and CGIEnvironment as non-inner classes?
+ * <li> Document handling of cgi stdin when there is no stdin
+ * <li> Revisit IOException handling in CGIRunner.run()
+ * <li> Better documentation
+ * <li> Confirm use of ServletInputStream.available() in CGIRunner.run() is
+ *      not needed
+ * <li> Make checking for "." and ".." in servlet & cgi PATH_INFO less
+ *      draconian
+ * <li> [add more to this TODO list]
+ * </ul>
+ * </p>
+ *
+ * @author Martin T Dengler [root@martindengler.com]
+ * @author Amy Roh
+ * @version $Revision: 1.4 $, $Date: 2006/09/06 16:02:28 $
+ * @since Tomcat 4.0
+ *
+ */
+
+
+public final class CGIServlet extends HttpServlet {
+
+    /* some vars below copied from Craig R. McClanahan's InvokerServlet */
+
+    /** the debugging detail level for this servlet. */
+    private int debug = 0;
+
+    /**
+     *  The CGI search path will start at
+     *    webAppRootDir + File.separator + cgiPathPrefix
+     *    (or webAppRootDir alone if cgiPathPrefix is
+     *    null)
+     */
+    private String cgiPathPrefix = "WEB-INF/cgi";
+
+    /** the executable to use with the script */
+    private String cgiExecutable = "perl";
+    
+    /** strip the substring from the request URI before passing it on
+        to the CGI environment */
+    private String stripRequestURI = "";
+
+    /** the encoding to use for parameters */
+    private String parameterEncoding = System.getProperty("file.encoding",
+                                                          "UTF-8");
+    /**
+     * The time (in milliseconds) to wait for the reading of stderr to complete
+     * before terminating the CGI process.
+     */
+    private long stderrTimeout = 2000;    
+
+    /** object used to ensure multiple threads don't try to expand same file */
+    static Object expandFileLock = new Object();
+
+    /** the shell environment variables to be passed to the CGI script */
+    static Hashtable<String, String> shellEnv = new Hashtable<String, String>();
+
+    /**
+     * Sets instance variables.
+     * <P>
+     * Modified from Craig R. McClanahan's InvokerServlet
+     * </P>
+     *
+     * @param config                    a <code>ServletConfig</code> object
+     *                                  containing the servlet's
+     *                                  configuration and initialization
+     *                                  parameters
+     *
+     * @exception ServletException      if an exception has occurred that
+     *                                  interferes with the servlet's normal
+     *                                  operation
+     */
+    public void init(ServletConfig config) throws ServletException {
+
+        super.init(config);
+
+        // Verify that we were not accessed using the invoker servlet
+        String servletName = getServletConfig().getServletName();
+        if (servletName == null)
+            servletName = "";
+        if (servletName.startsWith("org.apache.catalina.INVOKER."))
+            throw new UnavailableException
+                ("Cannot invoke CGIServlet through the invoker");
+        
+        // Set our properties from the initialization parameters
+        if (getServletConfig().getInitParameter("debug") != null)
+            debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));
+        if (getServletConfig().getInitParameter("cgiPathPrefix") != null) {
+            cgiPathPrefix = getServletConfig().getInitParameter("cgiPathPrefix");
+        }
+        boolean passShellEnvironment =
+            Boolean.valueOf(getServletConfig().getInitParameter("passShellEnvironment")).booleanValue();
+
+        if (passShellEnvironment) {
+            shellEnv.putAll(System.getenv());
+        }
+
+        if (getServletConfig().getInitParameter("executable") != null) {
+            cgiExecutable = getServletConfig().getInitParameter("executable"); 
+        }
+
+        if (getServletConfig().getInitParameter("parameterEncoding") != null) {
+            parameterEncoding = getServletConfig().getInitParameter("parameterEncoding");
+        }
+
+        if (getServletConfig().getInitParameter("stderrTimeout") != null) {
+            stderrTimeout = Long.parseLong(getServletConfig().getInitParameter(
+                    "stderrTimeout"));
+        }
+
+        if (getServletConfig().getInitParameter("stripRequestURI") != null) {
+            stripRequestURI = getServletConfig().getInitParameter("stripRequestURI");
+        }
+    }
+
+
+
+    /**
+     * Prints out important Servlet API and container information
+     *
+     * <p>
+     * Copied from SnoopAllServlet by Craig R. McClanahan
+     * </p>
+     *
+     * @param  out    ServletOutputStream as target of the information
+     * @param  req    HttpServletRequest object used as source of information
+     * @param  res    HttpServletResponse object currently not used but could
+     *                provide future information
+     *
+     * @exception  IOException  if a write operation exception occurs
+     *
+     */
+    protected void printServletEnvironment(ServletOutputStream out,
+        HttpServletRequest req, HttpServletResponse res) throws IOException {
+
+        // Document the properties from ServletRequest
+        out.println("<h1>ServletRequest Properties</h1>");
+        out.println("<ul>");
+        Enumeration<String> attrs = req.getAttributeNames();
+        while (attrs.hasMoreElements()) {
+            String attr = attrs.nextElement();
+            out.println("<li><b>attribute</b> " + HtmlEntityEncoder.encodeXSS(attr) + " = " +
+                           HtmlEntityEncoder.encodeXSS(req.getAttribute(attr)));
+        }
+        out.println("<li><b>characterEncoding</b> = " +
+                       HtmlEntityEncoder.encodeXSS(req.getCharacterEncoding()));
+        out.println("<li><b>contentLength</b> = " +
+                       req.getContentLength());
+        out.println("<li><b>contentType</b> = " +
+                       HtmlEntityEncoder.encodeXSS(req.getContentType()));
+        Enumeration<Locale> locales = req.getLocales();
+        while (locales.hasMoreElements()) {
+            Locale locale = locales.nextElement();
+            out.println("<li><b>locale</b> = " + HtmlEntityEncoder.encodeXSS(locale));
+        }
+        Enumeration<String> params = req.getParameterNames();
+        while (params.hasMoreElements()) {
+            String param = params.nextElement();
+            String values[] = req.getParameterValues(param);
+            for (int i = 0; i < values.length; i++)
+                out.println("<li><b>parameter</b> " + HtmlEntityEncoder.encodeXSS(param) + " = " +
+                               HtmlEntityEncoder.encodeXSS(values[i]));
+        }
+        out.println("<li><b>protocol</b> = " + req.getProtocol());
+        out.println("<li><b>remoteAddr</b> = " + req.getRemoteAddr());
+        out.println("<li><b>remoteHost</b> = " + req.getRemoteHost());
+        out.println("<li><b>scheme</b> = " + req.getScheme());
+        out.println("<li><b>secure</b> = " + req.isSecure());
+        out.println("<li><b>serverName</b> = " + HtmlEntityEncoder.encodeXSS(req.getServerName()));
+        out.println("<li><b>serverPort</b> = " + req.getServerPort());
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the properties from HttpServletRequest
+        out.println("<h1>HttpServletRequest Properties</h1>");
+        out.println("<ul>");
+        out.println("<li><b>authType</b> = " + req.getAuthType());
+        out.println("<li><b>contextPath</b> = " +
+                       req.getContextPath());
+        Cookie cookies[] = req.getCookies();
+        if (cookies!=null) {
+            for (int i = 0; i < cookies.length; i++)
+                out.println("<li><b>cookie</b> " + HtmlEntityEncoder.encodeXSS(cookies[i].getName())
+                       +" = " +HtmlEntityEncoder.encodeXSS(cookies[i].getValue()));
+        }
+        Enumeration<String> headers = req.getHeaderNames();
+        while (headers.hasMoreElements()) {
+            String header = headers.nextElement();
+            out.println("<li><b>header</b> " + HtmlEntityEncoder.encodeXSS(header) + " = " +
+                           HtmlEntityEncoder.encodeXSS(req.getHeader(header)));
+        }
+        out.println("<li><b>method</b> = " + HtmlEntityEncoder.encodeXSS(req.getMethod()));
+        out.println("<li><a name=\"pathInfo\"><b>pathInfo</b></a> = "
+                    + HtmlEntityEncoder.encodeXSS(req.getPathInfo()));
+        out.println("<li><b>pathTranslated</b> = " +
+                       HtmlEntityEncoder.encodeXSS(req.getPathTranslated()));
+        out.println("<li><b>queryString</b> = " +
+                       HtmlEntityEncoder.encodeXSS(req.getQueryString()));
+        out.println("<li><b>remoteUser</b> = " +
+                       HtmlEntityEncoder.encodeXSS(req.getRemoteUser()));
+        out.println("<li><b>requestedSessionId</b> = " +
+                       HtmlEntityEncoder.encodeXSS(req.getRequestedSessionId()));
+        out.println("<li><b>requestedSessionIdFromCookie</b> = " +
+                       req.isRequestedSessionIdFromCookie());
+        out.println("<li><b>requestedSessionIdFromURL</b> = " +
+                       req.isRequestedSessionIdFromURL());
+        out.println("<li><b>requestedSessionIdValid</b> = " +
+                       req.isRequestedSessionIdValid());
+        out.println("<li><b>requestURI</b> = " +
+                       HtmlEntityEncoder.encodeXSS(req.getRequestURI()));
+        out.println("<li><b>servletPath</b> = " +
+                       req.getServletPath());
+        out.println("<li><b>userPrincipal</b> = " +
+                       HtmlEntityEncoder.encodeXSS(req.getUserPrincipal()));
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet request attributes
+        out.println("<h1>ServletRequest Attributes</h1>");
+        out.println("<ul>");
+        attrs = req.getAttributeNames();
+        while (attrs.hasMoreElements()) {
+            String attr = attrs.nextElement();
+            out.println("<li><b>" + HtmlEntityEncoder.encodeXSS(attr) + "</b> = " +
+                           HtmlEntityEncoder.encodeXSS(req.getAttribute(attr)));
+        }
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Process the current session (if there is one)
+        HttpSession session = req.getSession(false);
+        if (session != null) {
+
+            // Document the session properties
+            out.println("<h1>HttpSession Properties</h1>");
+            out.println("<ul>");
+            out.println("<li><b>id</b> = " +
+                           HtmlEntityEncoder.encodeXSS(session.getId()));
+            out.println("<li><b>creationTime</b> = " +
+                           new Date(session.getCreationTime()));
+            out.println("<li><b>lastAccessedTime</b> = " +
+                           new Date(session.getLastAccessedTime()));
+            out.println("<li><b>maxInactiveInterval</b> = " +
+                           session.getMaxInactiveInterval());
+            out.println("</ul>");
+            out.println("<hr>");
+
+            // Document the session attributes
+            out.println("<h1>HttpSession Attributes</h1>");
+            out.println("<ul>");
+            attrs = session.getAttributeNames();
+            while (attrs.hasMoreElements()) {
+                String attr = attrs.nextElement();
+                out.println("<li><b>" + HtmlEntityEncoder.encodeXSS(attr) + "</b> = " +
+                               HtmlEntityEncoder.encodeXSS(session.getAttribute(attr)));
+            }
+            out.println("</ul>");
+            out.println("<hr>");
+
+        }
+
+        // Document the servlet configuration properties
+        out.println("<h1>ServletConfig Properties</h1>");
+        out.println("<ul>");
+        out.println("<li><b>servletName</b> = " +
+                       getServletConfig().getServletName());
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet configuration initialization parameters
+        out.println("<h1>ServletConfig Initialization Parameters</h1>");
+        out.println("<ul>");
+        params = getServletConfig().getInitParameterNames();
+        while (params.hasMoreElements()) {
+            String param = params.nextElement();
+            String value = getServletConfig().getInitParameter(param);
+            out.println("<li><b>" + param + "</b> = " + value);
+        }
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet context properties
+        out.println("<h1>ServletContext Properties</h1>");
+        out.println("<ul>");
+        out.println("<li><b>majorVersion</b> = " +
+                       getServletContext().getMajorVersion());
+        out.println("<li><b>minorVersion</b> = " +
+                       getServletContext().getMinorVersion());
+        out.println("<li><b>realPath('/')</b> = " +
+                       getServletContext().getRealPath("/"));
+        out.println("<li><b>serverInfo</b> = " +
+                       getServletContext().getServerInfo());
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet context initialization parameters
+        out.println("<h1>ServletContext Initialization Parameters</h1>");
+        out.println("<ul>");
+        params = getServletContext().getInitParameterNames();
+        while (params.hasMoreElements()) {
+            String param = params.nextElement();
+            String value = getServletContext().getInitParameter(param);
+            out.println("<li><b>" + param + "</b> = " + value);
+        }
+        out.println("</ul>");
+        out.println("<hr>");
+
+        // Document the servlet context attributes
+        out.println("<h1>ServletContext Attributes</h1>");
+        out.println("<ul>");
+        attrs = getServletContext().getAttributeNames();
+        while (attrs.hasMoreElements()) {
+            String attr = attrs.nextElement();
+            out.println("<li><b>" + attr + "</b> = " +
+                           getServletContext().getAttribute(attr));
+        }
+        out.println("</ul>");
+        out.println("<hr>");
+
+
+
+    }
+
+
+
+    /**
+     * Provides CGI Gateway service -- delegates to <code>doGet</code>
+     *
+     * @param  req   HttpServletRequest passed in by servlet container
+     * @param  res   HttpServletResponse passed in by servlet container
+     *
+     * @exception  ServletException  if a servlet-specific exception occurs
+     * @exception  IOException  if a read/write exception occurs
+     *
+     * @see javax.servlet.http.HttpServlet
+     *
+     */
+    protected void doPost(HttpServletRequest req, HttpServletResponse res)
+        throws IOException, ServletException {
+        doGet(req, res);
+    }
+
+
+
+    /**
+     * Provides CGI Gateway service
+     *
+     * @param  req   HttpServletRequest passed in by servlet container
+     * @param  res   HttpServletResponse passed in by servlet container
+     *
+     * @exception  ServletException  if a servlet-specific exception occurs
+     * @exception  IOException  if a read/write exception occurs
+     *
+     * @see javax.servlet.http.HttpServlet
+     *
+     */
+    protected void doGet(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException {
+
+        // Verify that we were not accessed using the invoker servlet
+        if (req.getAttribute(Globals.INVOKED_ATTR) != null)
+            throw new UnavailableException
+                ("Cannot invoke CGIServlet through the invoker");
+
+        CGIEnvironment cgiEnv = new CGIEnvironment(req, getServletContext());
+
+        if (cgiEnv.isValid()) {
+            CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(),
+                                          cgiEnv.getEnvironment(),
+                                          cgiEnv.getWorkingDirectory(),
+                                          cgiEnv.getParameters());
+            //if POST, we need to cgi.setInput
+            //REMIND: how does this interact with Servlet API 2.3's Filters?!
+            if ("POST".equals(req.getMethod())) {
+                cgi.setInput(req.getInputStream());
+            }
+            cgi.setResponse(res);
+            cgi.run();
+        }
+
+        if (!cgiEnv.isValid()) {
+            res.setStatus(404);
+        }
+ 
+        if (debug >= 10) {
+            ServletOutputStream out = res.getOutputStream();
+            out.println("<HTML><HEAD><TITLE>$Name:  $</TITLE></HEAD>");
+            out.println("<BODY>$Header$<p>");
+
+            if (cgiEnv.isValid()) {
+                out.println(cgiEnv.toString());
+            } else {
+                out.println("<H3>");
+                out.println("CGI script not found or not specified.");
+                out.println("</H3>");
+                out.println("<H4>");
+                out.println("Check the <b>HttpServletRequest ");
+                out.println("<a href=\"#pathInfo\">pathInfo</a></b> ");
+                out.println("property to see if it is what you meant ");
+                out.println("it to be.  You must specify an existant ");
+                out.println("and executable file as part of the ");
+                out.println("path-info.");
+                out.println("</H4>");
+                out.println("<H4>");
+                out.println("For a good discussion of how CGI scripts ");
+                out.println("work and what their environment variables ");
+                out.println("mean, please visit the <a ");
+                out.println("href=\"http://cgi-spec.golux.com\">CGI ");
+                out.println("Specification page</a>.");
+                out.println("</H4>");
+
+            }
+
+            printServletEnvironment(out, req, res);
+
+            out.println("</BODY></HTML>");
+        } //debugging
+
+
+    } //doGet
+
+
+
+    /** For future testing use only; does nothing right now */
+    public static void main(String[] args) {
+        System.out.println("$Header$");
+    }
+
+
+
+    /**
+     * Encapsulates the CGI environment and rules to derive
+     * that environment from the servlet container and request information.
+     *
+     * <p>
+     * </p>
+     *
+     * @version  $Revision: 1.4 $, $Date: 2006/09/06 16:02:28 $
+     * @since    Tomcat 4.0
+     *
+     */
+    protected class CGIEnvironment {
+
+
+        /** context of the enclosing servlet */
+        private ServletContext context = null;
+
+        /** context path of enclosing servlet */
+        private String contextPath = null;
+
+        /** servlet URI of the enclosing servlet */
+        private String servletPath = null;
+
+        /** pathInfo for the current request */
+        private String pathInfo = null;
+
+        /** real file system directory of the enclosing servlet's web app */
+        private String webAppRootDir = null;
+
+        /** tempdir for context - used to expand scripts in unexpanded wars */
+        private File tmpDir = null;
+
+        /** derived cgi environment */
+        private Hashtable<String, String> env = null;
+
+        /** cgi command to be invoked */
+        private String command = null;
+
+        /** cgi command's desired working directory */
+        private File workingDirectory = null;
+
+        /** cgi command's command line parameters */
+        private ArrayList<String> cmdLineParameters = new ArrayList<String>();
+
+        /** whether or not this object is valid or not */
+        private boolean valid = false;
+
+
+        /**
+         * Creates a CGIEnvironment and derives the necessary environment,
+         * query parameters, working directory, cgi command, etc.
+         *
+         * @param  req       HttpServletRequest for information provided by
+         *                   the Servlet API
+         * @param  context   ServletContext for information provided by the
+         *                   Servlet API
+         *
+         */
+        protected CGIEnvironment(HttpServletRequest req,
+                                 ServletContext context) throws IOException {
+            setupFromContext(context);
+            setupFromRequest(req);
+
+            this.valid = setCGIEnvironment(req);
+
+            if (this.valid) {
+                workingDirectory = new File(command.substring(0,
+                      command.lastIndexOf(File.separator)));
+            }
+
+        }
+
+
+
+        /**
+         * Uses the ServletContext to set some CGI variables
+         *
+         * @param  context   ServletContext for information provided by the
+         *                   Servlet API
+         */
+        protected void setupFromContext(ServletContext context) {
+            this.context = context;
+            this.webAppRootDir = context.getRealPath("/");
+            this.tmpDir = (File) context.getAttribute(ServletContext.TEMPDIR);
+        }
+
+
+
+        /**
+         * Uses the HttpServletRequest to set most CGI variables
+         *
+         * @param  req   HttpServletRequest for information provided by
+         *               the Servlet API
+         * @throws UnsupportedEncodingException 
+         */
+        protected void setupFromRequest(HttpServletRequest req)
+                throws UnsupportedEncodingException {
+            
+            boolean isIncluded = false;
+
+            // Look to see if this request is an include
+            if (req.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
+                isIncluded = true;
+            }
+            if (isIncluded) {
+                this.contextPath = (String)req.getAttribute(
+                    RequestDispatcher.INCLUDE_CONTEXT_PATH);
+                this.servletPath = (String)req.getAttribute(
+                    RequestDispatcher.INCLUDE_SERVLET_PATH);
+                this.pathInfo = (String)req.getAttribute(
+                    RequestDispatcher.INCLUDE_PATH_INFO);
+            } else {
+                this.contextPath = req.getContextPath();
+                this.servletPath = req.getServletPath();
+                this.pathInfo = req.getPathInfo();
+            }
+            // If getPathInfo() returns null, must be using extension mapping
+            // In this case, pathInfo should be same as servletPath
+            if (this.pathInfo == null) {
+                this.pathInfo = this.servletPath;
+            }
+            
+            // If the request method is GET, POST or HEAD and the query string
+            // does not contain an unencoded "=" this is an indexed query.
+            // The parsed query string becomes the command line parameters
+            // for the cgi command.
+            if (req.getMethod().equals("GET")
+                || req.getMethod().equals("POST")
+                || req.getMethod().equals("HEAD")) {
+                String qs;
+                if (isIncluded) {
+                    qs = (String)req.getAttribute(
+                            RequestDispatcher.INCLUDE_QUERY_STRING);
+                } else {
+                    qs = req.getQueryString();
+                }
+                if (qs != null && qs.indexOf("=") == -1) {
+                    StringTokenizer qsTokens = new StringTokenizer(qs, "+");
+                    while ( qsTokens.hasMoreTokens() ) {
+                        cmdLineParameters.add(URLDecoder.decode(qsTokens.nextToken(),
+                                              parameterEncoding));
+                    }
+                }
+            }
+        }
+
+
+        /**
+         * Resolves core information about the cgi script.
+         *
+         * <p>
+         * Example URI:
+         * <PRE> /servlet/cgigateway/dir1/realCGIscript/pathinfo1 </PRE>
+         * <ul>
+         * <LI><b>path</b> = $CATALINA_HOME/mywebapp/dir1/realCGIscript
+         * <LI><b>scriptName</b> = /servlet/cgigateway/dir1/realCGIscript
+         * <LI><b>cgiName</b> = /dir1/realCGIscript
+         * <LI><b>name</b> = realCGIscript
+         * </ul>
+         * </p>
+         * <p>
+         * CGI search algorithm: search the real path below
+         *    &lt;my-webapp-root&gt; and find the first non-directory in
+         *    the getPathTranslated("/"), reading/searching from left-to-right.
+         *</p>
+         *<p>
+         *   The CGI search path will start at
+         *   webAppRootDir + File.separator + cgiPathPrefix
+         *   (or webAppRootDir alone if cgiPathPrefix is
+         *   null).
+         *</p>
+         *<p>
+         *   cgiPathPrefix is defined by setting
+         *   this servlet's cgiPathPrefix init parameter
+         *
+         *</p>
+         *
+         * @param pathInfo       String from HttpServletRequest.getPathInfo()
+         * @param webAppRootDir  String from context.getRealPath("/")
+         * @param contextPath    String as from
+         *                       HttpServletRequest.getContextPath()
+         * @param servletPath    String as from
+         *                       HttpServletRequest.getServletPath()
+         * @param cgiPathPrefix  subdirectory of webAppRootDir below which
+         *                       the web app's CGIs may be stored; can be null.
+         *                       The CGI search path will start at
+         *                       webAppRootDir + File.separator + cgiPathPrefix
+         *                       (or webAppRootDir alone if cgiPathPrefix is
+         *                       null).  cgiPathPrefix is defined by setting
+         *                       the servlet's cgiPathPrefix init parameter.
+         *
+         *
+         * @return
+         * <ul>
+         * <li>
+         * <code>path</code> -    full file-system path to valid cgi script,
+         *                        or null if no cgi was found
+         * <li>
+         * <code>scriptName</code> -
+         *                        CGI variable SCRIPT_NAME; the full URL path
+         *                        to valid cgi script or null if no cgi was
+         *                        found
+         * <li>
+         * <code>cgiName</code> - servlet pathInfo fragment corresponding to
+         *                        the cgi script itself, or null if not found
+         * <li>
+         * <code>name</code> -    simple name (no directories) of the
+         *                        cgi script, or null if no cgi was found
+         * </ul>
+         *
+         * @since Tomcat 4.0
+         */
+        protected String[] findCGI(String pathInfo, String webAppRootDir,
+                                   String contextPath, String servletPath,
+                                   String cgiPathPrefix) {
+            String path = null;
+            String name = null;
+            String scriptname = null;
+            String cginame = null;
+
+            if ((webAppRootDir != null)
+                && (webAppRootDir.lastIndexOf(File.separator) ==
+                    (webAppRootDir.length() - 1))) {
+                    //strip the trailing "/" from the webAppRootDir
+                    webAppRootDir =
+                    webAppRootDir.substring(0, (webAppRootDir.length() - 1));
+            }
+
+            if (cgiPathPrefix != null) {
+                webAppRootDir = webAppRootDir + File.separator
+                    + cgiPathPrefix;
+            }
+
+            if (debug >= 2) {
+                log("findCGI: path=" + pathInfo + ", " + webAppRootDir);
+            }
+
+            File currentLocation = new File(webAppRootDir);
+            StringTokenizer dirWalker =
+            new StringTokenizer(pathInfo, "/");
+            if (debug >= 3) {
+                log("findCGI: currentLoc=" + currentLocation);
+            }
+
+            StringBuilder sb = new StringBuilder("");
+            while (!currentLocation.isFile() && dirWalker.hasMoreElements()) {
+                if (debug >= 3) {
+                    log("findCGI: currentLoc=" + currentLocation);
+                }
+                String nextElement = (String)dirWalker.nextElement();
+                currentLocation = new File(currentLocation, nextElement);
+                sb.append("/").append(nextElement);
+            }
+            cginame = sb.toString();
+            
+            if (!currentLocation.isFile()) {
+                return new String[] { null, null, null, null };
+            } else {
+                if (debug >= 2) {
+                    log("findCGI: FOUND cgi at " + currentLocation);
+                }
+                path = currentLocation.getAbsolutePath();
+                name = currentLocation.getName();
+                if (".".equals(contextPath)) {
+                    scriptname = servletPath;
+                } else {
+                    scriptname = contextPath + servletPath;
+                }
+                if (!servletPath.equals(cginame)) {
+                    scriptname = scriptname + cginame;
+                }
+            }
+
+            if (debug >= 1) {
+                log("findCGI calc: name=" + name + ", path=" + path
+                    + ", scriptname=" + scriptname + ", cginame=" + cginame);
+            }
+            return new String[] { path, scriptname, cginame, name };
+        }
+
+        /**
+         * Constructs the CGI environment to be supplied to the invoked CGI
+         * script; relies heavily on Servlet API methods and findCGI
+         *
+         * @param    req request associated with the CGI
+         *           invokation
+         *
+         * @return   true if environment was set OK, false if there
+         *           was a problem and no environment was set
+         */
+        protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException {
+
+            /*
+             * This method is slightly ugly; c'est la vie.
+             * "You cannot stop [ugliness], you can only hope to contain [it]"
+             * (apologies to Marv Albert regarding MJ)
+             */
+
+            Hashtable<String, String> envp = new Hashtable<String, String>();
+
+            // Add the shell environment variables (if any)
+            envp.putAll(shellEnv);
+
+            // Add the CGI environment variables
+            String sPathInfoOrig = null;
+            String sPathInfoCGI = null;
+            String sPathTranslatedCGI = null;
+            String sCGIFullPath = null;
+            String sCGIScriptName = null;
+            String sCGIFullName = null;
+            String sCGIName = null;
+            String[] sCGINames;
+
+
+            sPathInfoOrig = this.pathInfo;
+            sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig;
+
+            if (webAppRootDir == null ) {
+                // The app has not been deployed in exploded form
+                webAppRootDir = tmpDir.toString();
+                expandCGIScript();
+            } 
+            
+            sCGINames = findCGI(sPathInfoOrig,
+                                webAppRootDir,
+                                contextPath,
+                                servletPath,
+                                cgiPathPrefix);
+
+            sCGIFullPath = sCGINames[0];
+            sCGIScriptName = sCGINames[1];
+            sCGIFullName = sCGINames[2];
+            sCGIName = sCGINames[3];
+
+            if (sCGIFullPath == null
+                || sCGIScriptName == null
+                || sCGIFullName == null
+                || sCGIName == null) {
+                return false;
+            }
+
+            envp.put("SERVER_SOFTWARE", "TOMCAT");
+
+            envp.put("SERVER_NAME", nullsToBlanks(req.getServerName()));
+
+            envp.put("GATEWAY_INTERFACE", "CGI/1.1");
+
+            envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol()));
+
+            int port = req.getServerPort();
+            Integer iPort = (port == 0 ?
+                             Integer.valueOf(-1) : Integer.valueOf(port));
+            envp.put("SERVER_PORT", iPort.toString());
+
+            envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod()));
+
+            envp.put("REQUEST_URI", stripRequestURI(
+                nullsToBlanks(req.getRequestURI())) );
+
+
+            /*-
+             * PATH_INFO should be determined by using sCGIFullName:
+             * 1) Let sCGIFullName not end in a "/" (see method findCGI)
+             * 2) Let sCGIFullName equal the pathInfo fragment which
+             *    corresponds to the actual cgi script.
+             * 3) Thus, PATH_INFO = request.getPathInfo().substring(
+             *                      sCGIFullName.length())
+             *
+             * (see method findCGI, where the real work is done)
+             *
+             */
+            if (pathInfo == null
+                || (pathInfo.substring(sCGIFullName.length()).length() <= 0)) {
+                sPathInfoCGI = "";
+            } else {
+                sPathInfoCGI = pathInfo.substring(sCGIFullName.length());
+            }
+            envp.put("PATH_INFO", sPathInfoCGI);
+
+
+            /*-
+             * PATH_TRANSLATED must be determined after PATH_INFO (and the
+             * implied real cgi-script) has been taken into account.
+             *
+             * The following example demonstrates:
+             *
+             * servlet info   = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2
+             * cgifullpath    = /servlet/cgigw/dir1/dir2/cgi1
+             * path_info      = /trans1/trans2
+             * webAppRootDir  = servletContext.getRealPath("/")
+             *
+             * path_translated = servletContext.getRealPath("/trans1/trans2")
+             *
+             * That is, PATH_TRANSLATED = webAppRootDir + sPathInfoCGI
+             * (unless sPathInfoCGI is null or blank, then the CGI
+             * specification dictates that the PATH_TRANSLATED metavariable
+             * SHOULD NOT be defined.
+             *
+             */
+            if (!("".equals(sPathInfoCGI))) {
+                sPathTranslatedCGI = context.getRealPath(sPathInfoCGI);
+            }
+            if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) {
+                //NOOP
+            } else {
+                envp.put("PATH_TRANSLATED", nullsToBlanks(sPathTranslatedCGI));
+            }
+
+
+            envp.put("SCRIPT_NAME", nullsToBlanks(sCGIScriptName));
+
+            envp.put("QUERY_STRING", nullsToBlanks(req.getQueryString()));
+
+            envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost()));
+
+            envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr()));
+
+            envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType()));
+
+            envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser()));
+
+            envp.put("REMOTE_IDENT", ""); //not necessary for full compliance
+
+            envp.put("CONTENT_TYPE", nullsToBlanks(req.getContentType()));
+
+
+            /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined
+             * if there is no content, so we cannot put 0 or -1 in as per the
+             * Servlet API spec.
+             */
+            int contentLength = req.getContentLength();
+            String sContentLength = (contentLength <= 0 ? "" :
+                                     (Integer.toString(contentLength)));
+            envp.put("CONTENT_LENGTH", sContentLength);
+
+
+            Enumeration<String> headers = req.getHeaderNames();
+            String header = null;
+            while (headers.hasMoreElements()) {
+                header = null;
+                header = (headers.nextElement()).toUpperCase(Locale.ENGLISH);
+                //REMIND: rewrite multiple headers as if received as single
+                //REMIND: change character set
+                //REMIND: I forgot what the previous REMIND means
+                if ("AUTHORIZATION".equalsIgnoreCase(header) ||
+                    "PROXY_AUTHORIZATION".equalsIgnoreCase(header) ||
+                    "PROXY".equalsIgnoreCase(header)) {
+                    //NOOP per CGI specification section 11.2
+                } else {
+                    envp.put("HTTP_" + header.replace('-', '_'),
+                             req.getHeader(header));
+                }
+            }
+
+            File fCGIFullPath = new File(sCGIFullPath);
+            command = fCGIFullPath.getCanonicalPath();
+
+            envp.put("X_TOMCAT_SCRIPT_PATH", command);  //for kicks
+            
+            envp.put("SCRIPT_FILENAME", command);  //for PHP
+            
+            this.env = envp;
+
+            return true;
+
+        }
+
+        /**
+         * If the stripRequestURI is specified and the requestURI
+         * starts with it, the stripRequestURI is lopped off in
+         * in the returned value. Else, the requestURI is returned as is
+         */
+        protected String stripRequestURI(String reqURI) {
+
+            if( stripRequestURI == null ||
+                stripRequestURI.intern() == "".intern() ||
+                ! reqURI.startsWith(stripRequestURI) ) 
+                return reqURI;
+
+            int index = reqURI.indexOf(stripRequestURI);
+            if( index <= 0 )
+                return reqURI;
+            else
+                return reqURI.substring(index);
+        }
+
+        /**
+         * Extracts requested resource from web app archive to context work 
+         * directory to enable CGI script to be executed.
+         */
+        protected void expandCGIScript() {
+            StringBuilder srcPath = new StringBuilder();
+            StringBuilder destPath = new StringBuilder();
+            InputStream is = null;
+
+            try {
+                // paths depend on mapping
+                if (cgiPathPrefix == null) {
+                    srcPath.append(pathInfo);
+                    is = context.getResourceAsStream(srcPath.toString());
+                    destPath.append(tmpDir);
+                    destPath.append(pathInfo);
+                } else {
+                    // essentially same search algorithm as findCGI()
+                    srcPath.append(cgiPathPrefix);
+                    StringTokenizer pathWalker =
+                      new StringTokenizer(pathInfo, "/");
+                    // start with first element
+                    while (pathWalker.hasMoreElements() && (is == null)) {
+                        srcPath.append("/");
+                        srcPath.append(pathWalker.nextElement());
+                        is = context.getResourceAsStream(srcPath.toString());
+                    }
+                    destPath.append(tmpDir);
+                    destPath.append("/");
+                    destPath.append(srcPath);
+                }
+
+                if (is == null) {
+                    // didn't find anything, give up now
+                    if (debug >= 2) {
+                        log("expandCGIScript: source '" + srcPath + "' not found");
+                    }
+                    return;
+                }
+
+
+                File f = new File(destPath.toString());
+                if (f.exists()) {
+                    // Don't need to expand if it already exists
+                    return;
+                }
+
+                // create directories
+                String dirPath = destPath.toString().substring(
+                        0,destPath.toString().lastIndexOf("/"));
+                File dir = new File(dirPath);
+                if (!dir.mkdirs() && debug >= 2) {
+                    log("expandCGIScript: failed to create directories for '" +
+                            dir.getAbsolutePath() + "'");
+                    return;
+                }
+
+                try (FileOutputStream fos = new FileOutputStream(f)){
+                    synchronized (expandFileLock) {
+                        // make sure file doesn't exist
+                        if (f.exists()) {
+                            return;
+                        }
+
+                        // create file
+                        if (!f.createNewFile()) {
+                            return;
+                        }
+
+                        // copy data
+                        IOTools.flow(is, fos);
+                        if (debug >= 2) {
+                            log("expandCGIScript: expanded '" + srcPath + "' to '" + destPath + "'");
+                        }
+                    }
+                } catch (IOException ioe) {
+                    // delete in case file is corrupted
+                    if (f.exists()) {
+                        if (!f.delete() && debug >= 2) {
+                            log("expandCGIScript: failed to delete '" +
+                                    f.getAbsolutePath() + "'");
+                        }
+                    }
+                }
+            } finally {
+                if (is != null) {
+                  try {
+                    is.close();
+                  } catch (IOException ioe) {
+                    //ignore
+                  }
+                }
+            }
+        }
+
+
+        /**
+         * Print important CGI environment information in a easy-to-read HTML
+         * table
+         *
+         * @return  HTML string containing CGI environment info
+         *
+         */
+        public String toString() {
+
+            StringBuilder sb = new StringBuilder();
+
+            sb.append("<TABLE border=2>");
+
+            sb.append("<tr><th colspan=2 bgcolor=grey>");
+            sb.append("CGIEnvironment Info</th></tr>");
+
+            sb.append("<tr><td>Debug Level</td><td>");
+            sb.append(debug);
+            sb.append("</td></tr>");
+
+            sb.append("<tr><td>Validity:</td><td>");
+            sb.append(isValid());
+            sb.append("</td></tr>");
+
+            if (isValid()) {
+                Enumeration<String> envk = env.keys();
+                while (envk.hasMoreElements()) {
+                    String s = envk.nextElement();
+                    sb.append("<tr><td>");
+                    sb.append(HtmlEntityEncoder.encodeXSS(s));
+                    sb.append("</td><td>");
+                    sb.append(blanksToString(HtmlEntityEncoder.encodeXSS(env.get(s)),
+                                             "[will be set to blank]"));
+                    sb.append("</td></tr>");
+                }
+            }
+
+            sb.append("<tr><td colspan=2><HR></td></tr>");
+
+            sb.append("<tr><td>Derived Command</td><td>");
+            sb.append(nullsToBlanks(command));
+            sb.append("</td></tr>");
+
+            sb.append("<tr><td>Working Directory</td><td>");
+            if (workingDirectory != null) {
+                sb.append(HtmlEntityEncoder.encodeXSS(workingDirectory.toString()));
+            }
+            sb.append("</td></tr>");
+
+            sb.append("<tr><td>Command Line Params</td><td>");
+            for (int i=0; i < cmdLineParameters.size(); i++) {
+                String param = cmdLineParameters.get(i);
+                sb.append("<p>");
+                sb.append(HtmlEntityEncoder.encodeXSS(param));
+                sb.append("</p>");
+            }
+            sb.append("</td></tr>");
+
+            sb.append("</TABLE><p>end.");
+
+            return sb.toString();
+        }
+
+
+
+        /**
+         * Gets derived command string
+         *
+         * @return  command string
+         *
+         */
+        protected String getCommand() {
+            return command;
+        }
+
+
+
+        /**
+         * Gets derived CGI working directory
+         *
+         * @return  working directory
+         *
+         */
+        protected File getWorkingDirectory() {
+            return workingDirectory;
+        }
+
+
+
+        /**
+         * Gets derived CGI environment
+         *
+         * @return   CGI environment
+         *
+         */
+        protected Hashtable<String, String> getEnvironment() {
+            return env;
+        }
+
+
+
+        /**
+         * Gets derived CGI query parameters
+         *
+         * @return   CGI query parameters
+         *
+         */
+        protected ArrayList<String> getParameters() {
+            return cmdLineParameters;
+        }
+
+
+
+        /**
+         * Gets validity status
+         *
+         * @return   true if this environment is valid, false
+         *           otherwise
+         *
+         */
+        protected boolean isValid() {
+            return valid;
+        }
+
+
+
+        /**
+         * Converts null strings to blank strings ("")
+         *
+         * @param    s string to be converted if necessary
+         * @return   a non-null string, either the original or the empty string
+         *           ("") if the original was <code>null</code>
+         */
+        protected String nullsToBlanks(String s) {
+            return nullsToString(s, "");
+        }
+
+
+
+        /**
+         * Converts null strings to another string
+         *
+         * @param    couldBeNull string to be converted if necessary
+         * @param    subForNulls string to return instead of a null string
+         * @return   a non-null string, either the original or the substitute
+         *           string if the original was <code>null</code>
+         */
+        protected String nullsToString(String couldBeNull,
+                                       String subForNulls) {
+            return (couldBeNull == null ? subForNulls : couldBeNull);
+        }
+
+
+
+        /**
+         * Converts blank strings to another string
+         *
+         * @param    couldBeBlank string to be converted if necessary
+         * @param    subForBlanks string to return instead of a blank string
+         * @return   a non-null string, either the original or the substitute
+         *           string if the original was <code>null</code> or empty ("")
+         */
+        protected String blanksToString(String couldBeBlank,
+                                      String subForBlanks) {
+            return (("".equals(couldBeBlank) || couldBeBlank == null)
+                    ? subForBlanks
+                    : couldBeBlank);
+        }
+
+
+
+    } //class CGIEnvironment
+
+
+
+
+
+
+    /**
+     * Encapsulates the knowledge of how to run a CGI script, given the
+     * script's desired environment and (optionally) input/output streams
+     *
+     * <p>
+     *
+     * Exposes a <code>run</code> method used to actually invoke the
+     * CGI.
+     *
+     * </p>
+     * <p>
+     *
+     * The CGI environment and settings are derived from the information
+     * passed to the constructor.
+     *
+     * </p>
+     * <p>
+     *
+     * The input and output streams can be set by the <code>setInput</code>
+     * and <code>setResponse</code> methods, respectively.
+     * </p>
+     *
+     * @version   $Revision: 1.4 $, $Date: 2006/09/06 16:02:28 $
+     */
+
+    protected class CGIRunner {
+
+        /** script/command to be executed */
+        private String command = null;
+
+        /** environment used when invoking the cgi script */
+        private Hashtable<String, String> env = null;
+
+        /** working directory used when invoking the cgi script */
+        private File wd = null;
+
+        /** command line parameters to be passed to the invoked script */
+        private ArrayList<String> params = null;
+
+        /** stdin to be passed to cgi script */
+        private InputStream stdin = null;
+
+        /** response object used to set headers & get output stream */
+        private HttpServletResponse response = null;
+
+        /** boolean tracking whether this object has enough info to run() */
+        private boolean readyToRun = false;
+
+
+
+
+        /**
+         *  Creates a CGIRunner and initializes its environment, working
+         *  directory, and query parameters.
+         *  <BR>
+         *  Input/output streams (optional) are set using the
+         *  <code>setInput</code> and <code>setResponse</code> methods,
+         *  respectively.
+         *
+         * @param  command  string full path to command to be executed
+         * @param  env      Hashtable with the desired script environment
+         * @param  wd       File with the script's desired working directory
+         * @param  params   ArrayList with the script's query command line
+         *                  parameters as strings
+         */
+        protected CGIRunner(String command, Hashtable<String, String> env,
+                            File wd, ArrayList<String> params) {
+            this.command = command;
+            this.env = env;
+            this.wd = wd;
+            this.params = params;
+            updateReadyStatus();
+        }
+
+
+
+        /**
+         * Checks & sets ready status
+         */
+        protected void updateReadyStatus() {
+            if (command != null
+                && env != null
+                && wd != null
+                && params != null
+                && response != null) {
+                readyToRun = true;
+            } else {
+                readyToRun = false;
+            }
+        }
+
+
+
+        /**
+         * Gets ready status
+         *
+         * @return   false if not ready (<code>run</code> will throw
+         *           an exception), true if ready
+         */
+        protected boolean isReady() {
+            return readyToRun;
+        }
+
+
+
+        /**
+         * Sets HttpServletResponse object used to set headers and send
+         * output to
+         *
+         * @param  response   HttpServletResponse to be used
+         *
+         */
+        protected void setResponse(HttpServletResponse response) {
+            this.response = response;
+            updateReadyStatus();
+        }
+
+
+
+        /**
+         * Sets standard input to be passed on to the invoked cgi script
+         *
+         * @param  stdin   InputStream to be used
+         *
+         */
+        protected void setInput(InputStream stdin) {
+            this.stdin = stdin;
+            updateReadyStatus();
+        }
+
+
+
+        /**
+         * Converts a Hashtable to a String array by converting each
+         * key/value pair in the Hashtable to a String in the form
+         * "key=value" (hashkey + "=" + hash.get(hashkey).toString())
+         *
+         * @param  h   Hashtable to convert
+         *
+         * @return     converted string array
+         *
+         * @exception  NullPointerException   if a hash key has a null value
+         *
+         */
+        protected String[] hashToStringArray(Hashtable<String, ?> h)
+            throws NullPointerException {
+            Vector<String> v = new Vector<String>();
+            Enumeration<String> e = h.keys();
+            while (e.hasMoreElements()) {
+                String k = e.nextElement();
+                v.add(k + "=" + h.get(k));
+            }
+            String[] strArr = new String[v.size()];
+            v.copyInto(strArr);
+            return strArr;
+        }
+
+
+
+        /**
+         * Executes a CGI script with the desired environment, current working
+         * directory, and input/output streams
+         *
+         * <p>
+         * This implements the following CGI specification recommedations:
+         * <UL>
+         * <LI> Servers SHOULD provide the "<code>query</code>" component of
+         *      the script-URI as command-line arguments to scripts if it
+         *      does not contain any unencoded "=" characters and the
+         *      command-line arguments can be generated in an unambiguous
+         *      manner.
+         * <LI> Servers SHOULD set the AUTH_TYPE metavariable to the value
+         *      of the "<code>auth-scheme</code>" token of the
+         *      "<code>Authorization</code>" if it was supplied as part of the
+         *      request header.  See <code>getCGIEnvironment</code> method.
+         * <LI> Where applicable, servers SHOULD set the current working
+         *      directory to the directory in which the script is located
+         *      before invoking it.
+         * <LI> Server implementations SHOULD define their behavior for the
+         *      following cases:
+         *     <ul>
+         *     <LI> <u>Allowed characters in pathInfo</u>:  This implementation
+         *             does not allow ASCII NUL nor any character which cannot
+         *             be URL-encoded according to internet standards;
+         *     <LI> <u>Allowed characters in path segments</u>: This
+         *             implementation does not allow non-terminal NULL
+         *             segments in the the path -- IOExceptions may be thrown;
+         *     <LI> <u>"<code>.</code>" and "<code>..</code>" path
+         *             segments</u>:
+         *             This implementation does not allow "<code>.</code>" and
+         *             "<code>..</code>" in the the path, and such characters
+         *             will result in an IOException being thrown;
+         *     <LI> <u>Implementation limitations</u>: This implementation
+         *             does not impose any limitations except as documented
+         *             above.  This implementation may be limited by the
+         *             servlet container used to house this implementation.
+         *             In particular, all the primary CGI variable values
+         *             are derived either directly or indirectly from the
+         *             container's implementation of the Servlet API methods.
+         *     </ul>
+         * </UL>
+         * </p>
+         *
+         * @exception IOException if problems during reading/writing occur
+         *
+         * @see    java.lang.Runtime#exec(String command, String[] envp,
+         *                                File dir)
+         */
+        protected void run() throws IOException {
+
+            /*
+             * REMIND:  this method feels too big; should it be re-written?
+             */
+
+            if (!isReady()) {
+                throw new IOException(this.getClass().getName()
+                                      + ": not ready to run.");
+            }
+
+            if (debug >= 1 ) {
+                log("runCGI(envp=[" + env + "], command=" + command + ")");
+            }
+
+            if ((command.indexOf(File.separator + "." + File.separator) >= 0)
+                || (command.indexOf(File.separator + "..") >= 0)
+                || (command.indexOf(".." + File.separator) >= 0)) {
+                throw new IOException(this.getClass().getName()
+                                      + "Illegal Character in CGI command "
+                                      + "path ('.' or '..') detected.  Not "
+                                      + "running CGI [" + command + "].");
+            }
+
+            /* original content/structure of this section taken from
+             * http://developer.java.sun.com/developer/
+             *                               bugParade/bugs/4216884.html
+             * with major modifications by Martin Dengler
+             */
+            Runtime rt = null;
+            BufferedReader cgiHeaderReader = null;
+            InputStream cgiOutput = null;
+            BufferedReader commandsStdErr = null;
+            Thread errReaderThread = null;
+            BufferedOutputStream commandsStdIn = null;
+            Process proc = null;
+            int bufRead = -1;
+
+            //create query arguments
+            StringBuilder cmdAndArgs = new StringBuilder();
+            if (command.indexOf(" ") < 0) {
+                cmdAndArgs.append(command);
+            } else {
+                // Spaces used as delimiter, so need to use quotes
+                cmdAndArgs.append("\"");
+                cmdAndArgs.append(command);
+                cmdAndArgs.append("\"");
+            }
+
+            for (int i=0; i < params.size(); i++) {
+                cmdAndArgs.append(" ");
+                String param = params.get(i);
+                if (param.indexOf(" ") < 0) {
+                    cmdAndArgs.append(param);
+                } else {
+                    // Spaces used as delimiter, so need to use quotes
+                    cmdAndArgs.append("\"");
+                    cmdAndArgs.append(param);
+                    cmdAndArgs.append("\"");
+                }
+            }
+
+            StringBuilder command = new StringBuilder(cgiExecutable);
+            command.append(" ");
+            command.append(cmdAndArgs.toString());
+            cmdAndArgs = command;
+
+            try {
+                rt = Runtime.getRuntime();
+                proc = rt.exec(cmdAndArgs.toString(), hashToStringArray(env), wd);
+    
+                String sContentLength = env.get("CONTENT_LENGTH");
+
+                if(!"".equals(sContentLength)) {
+                    commandsStdIn = new BufferedOutputStream(proc.getOutputStream());
+                    IOTools.flow(stdin, commandsStdIn);
+                    commandsStdIn.flush();
+                    commandsStdIn.close();
+                }
+
+                /* we want to wait for the process to exit,  Process.waitFor()
+                 * is useless in our situation; see
+                 * http://developer.java.sun.com/developer/
+                 *                               bugParade/bugs/4223650.html
+                 */
+
+                boolean isRunning = true;
+                commandsStdErr = new BufferedReader
+                    (new InputStreamReader(proc.getErrorStream()));
+                final BufferedReader stdErrRdr = commandsStdErr ;
+
+                errReaderThread = new Thread() {
+                    public void run () {
+                        sendToLog(stdErrRdr) ;
+                    }
+                };
+                errReaderThread.start();
+
+                InputStream cgiHeaderStream =
+                    new HTTPHeaderInputStream(proc.getInputStream());
+                cgiHeaderReader =
+                    new BufferedReader(new InputStreamReader(cgiHeaderStream));
+            
+                while (isRunning) {
+                    try {
+                        //set headers
+                        String line = null;
+                        while (((line = cgiHeaderReader.readLine()) != null)
+                               && !("".equals(line))) {
+                            if (debug >= 2) {
+                                log("runCGI: addHeader(\"" + line + "\")");
+                            }
+                            if (line.startsWith("HTTP")) {
+                                response.setStatus(getSCFromHttpStatusLine(line));
+                            } else if (line.indexOf(":") >= 0) {
+                                String header =
+                                    line.substring(0, line.indexOf(":")).trim();
+                                String value =
+                                    line.substring(line.indexOf(":") + 1).trim(); 
+                                if (header.equalsIgnoreCase("status")) {
+                                    response.setStatus(getSCFromCGIStatusHeader(value));
+                                } else {
+                                    response.addHeader(header , value);
+                                }
+                            } else {
+                                log("runCGI: bad header line \"" + line + "\"");
+                            }
+                        }
+    
+                        //write output
+                        byte[] bBuf = new byte[2048];
+    
+                        OutputStream out = response.getOutputStream();
+                        cgiOutput = proc.getInputStream();
+    
+                        try {
+                            while ((bufRead = cgiOutput.read(bBuf)) != -1) {
+                                if (debug >= 4) {
+                                    log("runCGI: output " + bufRead +
+                                        " bytes of data");
+                                }
+                                out.write(bBuf, 0, bufRead);
+                            }
+                        } finally {
+                            // Attempt to consume any leftover byte if something bad happens,
+                            // such as a socket disconnect on the servlet side; otherwise, the
+                            // external process could hang
+                            if (bufRead != -1) {
+                                while ((bufRead = cgiOutput.read(bBuf)) != -1) {
+                                    // NOOP - just read the data
+                                }
+                            }
+                        }
+        
+                        proc.exitValue(); // Throws exception if alive
+    
+                        isRunning = false;
+    
+                    } catch (IllegalThreadStateException e) {
+                        try {
+                            Thread.sleep(500);
+                        } catch (InterruptedException ignored) {
+                            // Ignore
+                        }
+                    }
+                } //replacement for Process.waitFor()
+    
+            }
+            catch (IOException e){
+                log ("Caught exception " + e);
+                throw e;
+            }
+            finally{
+                // Close the header reader
+                if (cgiHeaderReader != null) {
+                    try {
+                        cgiHeaderReader.close();
+                    } catch (IOException ioe) {
+                        log ("Exception closing header reader " + ioe);
+                    }
+                }
+                // Close the output stream used if used
+                if (cgiOutput != null) {
+                    try {
+                        cgiOutput.close();
+                    } catch(IOException ioe) {
+                        log("Exception closing output stream " + ioe);
+                    }
+                }
+                // Make sure the error stream reader has finished
+                if (errReaderThread != null) {
+                    try {
+                        errReaderThread.join(stderrTimeout);
+                    } catch (InterruptedException e) {
+                        log ("Interupted waiting for stderr reader thread");
+                    }
+                }
+                if (debug > 4) {
+                    log ("Running finally block");
+                }
+                if (proc != null){
+                    proc.destroy();
+                    proc = null;
+                }
+            }
+        }
+
+        /**
+         * Parses the Status-Line and extracts the status code.
+         * 
+         * @param line The HTTP Status-Line (RFC2616, section 6.1)
+         * @return The extracted status code or the code representing an
+         * internal error if a valid status code cannot be extracted. 
+         */
+        private int getSCFromHttpStatusLine(String line) {
+            int statusStart = line.indexOf(' ') + 1;
+            
+            if (statusStart < 1 || line.length() < statusStart + 3) {
+                // Not a valid HTTP Status-Line
+                log ("runCGI: invalid HTTP Status-Line:" + line);
+                return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+            }
+            
+            String status = line.substring(statusStart, statusStart + 3);
+            
+            int statusCode;
+            try {
+                statusCode = Integer.parseInt(status);
+            } catch (NumberFormatException nfe) {
+                // Not a valid status code
+                log ("runCGI: invalid status code:" + status);
+                return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+            }
+            
+            return statusCode;
+        }
+
+        /**
+         * Parses the CGI Status Header value and extracts the status code.
+         * 
+         * @param value The CGI Status value of the form <code>
+         *             digit digit digit SP reason-phrase</code>
+         * @return The extracted status code or the code representing an
+         * internal error if a valid status code cannot be extracted. 
+         */
+        private int getSCFromCGIStatusHeader(String value) {
+            if (value.length() < 3) {
+                // Not a valid status value
+                log ("runCGI: invalid status value:" + value);
+                return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+            }
+            
+            String status = value.substring(0, 3);
+            
+            int statusCode;
+            try {
+                statusCode = Integer.parseInt(status);
+            } catch (NumberFormatException nfe) {
+                // Not a valid status code
+                log ("runCGI: invalid status code:" + status);
+                return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+            }
+            
+            return statusCode;
+        }
+        
+        private void sendToLog(BufferedReader rdr) {
+            String line = null;
+            int lineCount = 0 ;
+            try {
+                while ((line = rdr.readLine()) != null) {
+                    log("runCGI (stderr):" +  line) ;
+                    lineCount++ ;
+                }
+            } catch (IOException e) {
+                log("sendToLog error", e) ;
+            } finally {
+                try {
+                    rdr.close() ;
+                } catch (IOException ce) {
+                    log("sendToLog error", ce) ;
+                }
+            }
+            if ( lineCount > 0 && debug > 2) {
+                log("runCGI: " + lineCount + " lines received on stderr") ;
+            }
+        }
+    } //class CGIRunner
+
+    /**
+     * This is an input stream specifically for reading HTTP headers. It reads
+     * upto and including the two blank lines terminating the headers. It
+     * allows the content to be read using bytes or characters as appropriate.
+     */
+    protected static class HTTPHeaderInputStream extends InputStream {
+        private static final int STATE_CHARACTER = 0;
+        private static final int STATE_FIRST_CR = 1;
+        private static final int STATE_FIRST_LF = 2;
+        private static final int STATE_SECOND_CR = 3;
+        private static final int STATE_HEADER_END = 4;
+        
+        private InputStream input;
+        private int state;
+        
+        HTTPHeaderInputStream(InputStream theInput) {
+            input = theInput;
+            state = STATE_CHARACTER;
+        }
+
+        /**
+         * @see java.io.InputStream#read()
+         */
+        public int read() throws IOException {
+            if (state == STATE_HEADER_END) {
+                return -1;
+            }
+
+            int i = input.read();
+
+            // Update the state
+            // State machine looks like this
+            //
+            //    -------->--------
+            //   |      (CR)       |
+            //   |                 |
+            //  CR1--->---         |
+            //   |        |        |
+            //   ^(CR)    |(LF)    |
+            //   |        |        |
+            // CHAR--->--LF1--->--EOH
+            //      (LF)  |  (LF)  |
+            //            |(CR)    ^(LF)
+            //            |        |
+            //          (CR2)-->---
+            
+            if (i == 10) {
+                // LF
+                switch(state) {
+                    case STATE_CHARACTER:
+                    case STATE_FIRST_CR:
+                        state = STATE_FIRST_LF;
+                        break;
+                    case STATE_FIRST_LF:
+                    case STATE_SECOND_CR:
+                        state = STATE_HEADER_END;
+                        break;
+                    default:
+                        break;
+                }
+
+            } else if (i == 13) {
+                // CR
+                switch(state) {
+                    case STATE_CHARACTER:
+                        state = STATE_FIRST_CR;
+                        break;
+                    case STATE_FIRST_CR:
+                        state = STATE_HEADER_END;
+                        break;
+                    case STATE_FIRST_LF:
+                        state = STATE_SECOND_CR;
+                        break;
+                    default:
+                        break;
+                }
+
+            } else {
+                state = STATE_CHARACTER;
+            }
+            
+            return i;            
+        }
+    }  // class HTTPHeaderInputStream
+
+} //class CGIServlet
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/Constants.java
new file mode 100644
index 0000000..ba0710f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/Constants.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.servlets;
+
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.servlets";
+
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/DefaultServlet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/DefaultServlet.java
new file mode 100644
index 0000000..b2227cc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -0,0 +1,2658 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.servlets;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.RandomAccessFile;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.security.AccessController;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.naming.InitialContext;
+import javax.naming.NameClassPair;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.core.ContextsAdapterUtility;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.URLEncoder;
+import org.apache.naming.resources.CacheEntry;
+import org.apache.naming.resources.ProxyDirContext;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+import org.apache.tomcat.util.security.PrivilegedGetTccl;
+import org.apache.tomcat.util.security.PrivilegedSetTccl;
+import org.glassfish.grizzly.http.server.util.AlternateDocBase;
+import org.glassfish.web.util.HtmlEntityEncoder;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.EntityResolver2;
+
+/**
+ * <p>The default resource-serving servlet for most web applications,
+ * used to serve static resources such as HTML pages and images.
+ * </p>
+ * <p>
+ * This servlet is intended to be mapped to <em>/</em> e.g.:
+ * </p>
+ * <pre>
+ *   &lt;servlet-mapping&gt;
+ *       &lt;servlet-name&gt;default&lt;/servlet-name&gt;
+ *       &lt;url-pattern&gt;/&lt;/url-pattern&gt;
+ *   &lt;/servlet-mapping&gt;
+ * </pre>
+ * <p>It can be mapped to sub-paths, however in all cases resources are served
+ * from the web appplication resource root using the full path from the root
+ * of the web application context.
+ * <br/>e.g. given a web application structure:
+ *</p>
+ * <pre>
+ * /context
+ *   /images
+ *     tomcat2.jpg
+ *   /static
+ *     /images
+ *       tomcat.jpg
+ * </pre>
+ * <p>
+ * ... and a servlet mapping that maps only <code>/static/*</code> to the default servlet:
+ * </p>
+ * <pre>
+ *   &lt;servlet-mapping&gt;
+ *       &lt;servlet-name&gt;default&lt;/servlet-name&gt;
+ *       &lt;url-pattern&gt;/static/*&lt;/url-pattern&gt;
+ *   &lt;/servlet-mapping&gt;
+ * </pre>
+ * <p>
+ * Then a request to <code>/context/static/images/tomcat.jpg</code> will succeed
+ * while a request to <code>/context/images/tomcat2.jpg</code> will fail. 
+ * </p>
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.16 $ $Date: 2007/06/06 16:01:12 $
+ */
+
+public class DefaultServlet
+    extends HttpServlet {
+
+    protected static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    private static final DocumentBuilderFactory factory;
+
+    private static final SecureEntityResolver secureEntityResolver;
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The debugging detail level for this servlet.
+     */
+    protected int debug = 0;
+
+
+    /**
+     * The input buffer size to use when serving resources.
+     */
+    protected int input = 2048;
+
+
+    /**
+     * Should we generate directory listings?
+     */
+    protected volatile boolean listings = false;
+
+
+    /**
+     * The sorting mechanism for directory listings
+     */
+    protected SortedBy sortedBy = SortedBy.NAME;
+
+
+    /**
+     * Read only flag. By default, it's set to true.
+     */
+    protected boolean readOnly = true;
+
+
+    /**
+     * The output buffer size to use when serving resources.
+     */
+    protected int output = 2048;
+
+
+    /**
+     * Array containing the safe characters set.
+     */
+    protected static final URLEncoder urlEncoder;
+
+
+    /**
+     * Allow customized directory listing per directory.
+     */
+    protected String  localXsltFile = null;
+
+
+    /**
+     * Allow customized directory listing per context.
+     */
+    protected String contextXsltFile = null;
+
+
+    /**
+     * Allow customized directory listing per instance.
+     */
+    protected String  globalXsltFile = null;
+
+
+    /**
+     * Allow a readme file to be included.
+     */
+    protected String readmeFile = null;
+
+
+    /**
+     * Proxy directory context.
+     */
+    protected transient ProxyDirContext resources = null;
+
+
+    /**
+     * Alternate doc bases
+     */
+    protected transient ArrayList<AlternateDocBase> alternateDocBases = null;
+
+
+    /**
+     * File encoding to be used when reading static files. If none is specified
+     * the platform default is used.
+     */
+    protected String fileEncoding = null;
+
+
+    /**
+     * Minimum size for sendfile usage in bytes.
+     */
+    protected int sendfileSize = 48 * 1024;
+    
+    
+    /**
+     * Should the Accept-Ranges: bytes header be send with static resources?
+     */
+    protected boolean useAcceptRanges = true;
+
+
+    /**
+     * Full range marker.
+     */
+    protected static final ArrayList<Range> FULL = new ArrayList<Range>();
+    
+    /**
+     * The maximum number of items allowed in Range header.
+     * -1 means unbounded.
+     */
+    protected int maxHeaderRangeItems = 10;
+
+    
+    // ----------------------------------------------------- Static Initializer
+
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+        urlEncoder = new URLEncoder();
+        urlEncoder.addSafeCharacter('-');
+        urlEncoder.addSafeCharacter('_');
+        urlEncoder.addSafeCharacter('.');
+        urlEncoder.addSafeCharacter('*');
+        urlEncoder.addSafeCharacter('/');
+
+        if (Globals.IS_SECURITY_ENABLED) {
+            factory = DocumentBuilderFactory.newInstance();
+            factory.setNamespaceAware(true);
+            factory.setValidating(false);
+            secureEntityResolver = new SecureEntityResolver();
+        } else {
+            factory = null;
+            secureEntityResolver = null;
+        }
+    }
+
+
+    /**
+     * MIME multipart separation string
+     */
+    protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";
+
+
+    /**
+     * JNDI resources name.
+     */
+    protected static final String RESOURCES_JNDI_NAME = "java:/comp/Resources";
+
+
+    /**
+     * Size of file transfer buffer in bytes.
+     */
+    protected static final int BUFFER_SIZE = 4096;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Finalize this servlet.
+     */
+    public void destroy() {
+        // NOOP
+    }
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init() throws ServletException {
+        ServletConfig sc = getServletConfig();
+        if (sc.getInitParameter("debug") != null)
+            debug = Integer.parseInt(sc.getInitParameter("debug"));
+
+        if (sc.getInitParameter("input") != null)
+            input = Integer.parseInt(sc.getInitParameter("input"));
+
+        if (sc.getInitParameter("output") != null)
+            output = Integer.parseInt(sc.getInitParameter("output"));
+
+        listings = Boolean.parseBoolean(sc.getInitParameter("listings"));
+
+        String sortedByInitParam = sc.getInitParameter("sortedBy");
+        if (sortedByInitParam != null) {
+            sortedBy = Enum.valueOf(SortedBy.class, sortedByInitParam);
+        }
+
+        if (sc.getInitParameter("readonly") != null)
+            readOnly = Boolean.parseBoolean(sc.getInitParameter("readonly"));
+
+        if (sc.getInitParameter("sendfileSize") != null)
+            sendfileSize = 
+                Integer.parseInt(sc.getInitParameter("sendfileSize")) * 1024;
+
+        if (sc.getInitParameter("maxHeaderRangeItems") != null) {
+            maxHeaderRangeItems = 
+                Integer.parseInt(sc.getInitParameter("maxHeaderRangeItems"));
+        }
+
+        fileEncoding = sc.getInitParameter("fileEncoding");
+
+        globalXsltFile = sc.getInitParameter("globalXsltFile");
+        contextXsltFile = sc.getInitParameter("contextXsltFile");
+        localXsltFile = sc.getInitParameter("localXsltFile");
+        readmeFile = sc.getInitParameter("readmeFile");
+
+        if (sc.getInitParameter("useAcceptRanges") != null)
+            useAcceptRanges = Boolean.parseBoolean(sc.getInitParameter("useAcceptRanges"));
+
+        // Sanity check on the specified buffer sizes
+        if (input < 256)
+            input = 256;
+        if (output < 256)
+            output = 256;
+
+        if (debug > 0) {
+            log("DefaultServlet.init:  input buffer size=" + input +
+                ", output buffer size=" + output);
+        }
+
+        // Load the proxy dir context.
+        resources = (ProxyDirContext) getServletContext()
+                .getAttribute(Globals.RESOURCES_ATTR);
+        if (resources == null) {
+            try {
+                resources =
+                    (ProxyDirContext) new InitialContext()
+                    .lookup(RESOURCES_JNDI_NAME);
+            } catch (NamingException e) {
+                throw new ServletException("No resources", e);
+            } catch (ClassCastException e) {
+                // Failed : Not the right type
+            }
+        }
+
+        if (resources == null) {
+            throw new UnavailableException("No resources");
+        }
+
+        try {
+            alternateDocBases = getAlternateDocBases();
+        } catch(ClassCastException e) {
+            // Failed : Not the right type
+        }
+
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private ArrayList<AlternateDocBase> getAlternateDocBases() {
+
+        return (ArrayList<AlternateDocBase>)
+                getServletContext().getAttribute(
+                Globals.ALTERNATE_RESOURCES_ATTR);
+    }
+    
+    
+    /**
+     * Return if directory listings are enabled
+     */
+    public boolean isListings() {
+        return this.listings;
+    }
+
+    
+    /**
+     * Enables or disables directory listings for this DefaultServlet.
+     *
+     * @param listings true if directory listings are to be enabled, false
+     * otherwise
+     */
+    public void setListings(boolean listings) {
+        this.listings = listings;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return the relative path associated with this servlet.
+     *
+     * @param request The servlet request we are processing
+     */
+    protected String getRelativePath(HttpServletRequest request) {
+        // IMPORTANT: DefaultServlet can be mapped to '/' or '/path/*' but always
+        // serves resources from the web app root with context rooted paths.
+        // i.e. it can not be used to mount the web app root under a sub-path
+        // This method must construct a complete context rooted path, although
+        // subclasses can change this behaviour.
+
+        // Are we being processed by a RequestDispatcher.include()?
+        if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
+            String result = (String) request.getAttribute(
+                RequestDispatcher.INCLUDE_PATH_INFO);
+            if (result == null)
+                result = (String) request.getAttribute(
+                    RequestDispatcher.INCLUDE_SERVLET_PATH);
+            if ((result == null) || (result.equals("")))
+                result = "/";
+            return (result);
+        }
+
+        // No, extract the desired path directly from the request
+        String result = request.getPathInfo();
+        if (result == null) {
+            result = request.getServletPath();
+        } else {
+            result = request.getServletPath() + result;
+        }
+        if ((result == null) || (result.equals(""))) {
+            result = "/";
+        }
+        return (result);
+
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doGet(HttpServletRequest request,
+                         HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Serve the requested resource, including the data content
+        serveResource(request, response, true);
+
+    }
+
+
+    /**
+     * Process a HEAD request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doHead(HttpServletRequest request,
+                          HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Serve the requested resource, without the data content
+        serveResource(request, response, false);
+
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doPost(HttpServletRequest request,
+                          HttpServletResponse response)
+        throws IOException, ServletException {
+        doGet(request, response);
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param req The servlet request we are processing
+     * @param resp The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        boolean exists = true;
+        try {
+            resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        boolean result = true;
+
+        // Temp. content file used to support partial PUT
+        File contentFile = null;
+
+        Range range = parseContentRange(req, resp);
+
+        InputStream resourceInputStream = null;
+
+        // Append data specified in ranges to existing content for this
+        // resource - create a temp. file on the local filesystem to
+        // perform this operation
+        // Assume just one range is specified for now
+        if (range != null) {
+            contentFile = executePartialPut(req, range, path);
+            resourceInputStream = new FileInputStream(contentFile);
+        } else {
+            resourceInputStream = req.getInputStream();
+        }
+
+        try {
+            Resource newResource = new Resource(resourceInputStream);
+            // FIXME: Add attributes
+            if (exists) {
+                resources.rebind(path, newResource);
+            } else {
+                resources.bind(path, newResource);
+            }
+        } catch(NamingException e) {
+            result = false;
+        }
+
+        if (result) {
+            if (exists) {
+                resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
+            } else {
+                resp.setStatus(HttpServletResponse.SC_CREATED);
+            }
+        } else {
+            resp.sendError(HttpServletResponse.SC_CONFLICT);
+        }
+
+    }
+
+
+    /**
+     * Handle a partial PUT.  New content specified in request is appended to
+     * existing content in oldRevisionContent (if present). This code does
+     * not support simultaneous partial updates to the same resource.
+     */
+    protected File executePartialPut(HttpServletRequest req, Range range,
+                                     String path)
+        throws IOException {
+
+        // Append data specified in ranges to existing content for this
+        // resource - create a temp. file on the local filesystem to
+        // perform this operation
+        File tempDir = (File) getServletContext().getAttribute(
+            ServletContext.TEMPDIR);
+        // Convert all '/' characters to '.' in resourcePath
+        String convertedResourcePath = path.replace('/', '.');
+        File contentFile = new File(tempDir, convertedResourcePath);
+        if (contentFile.createNewFile()) {
+            // Clean up contentFile when Tomcat is terminated
+            contentFile.deleteOnExit();
+        }
+
+        Resource oldResource = null;
+        try {
+            Object obj = resources.lookup(path);
+            if (obj instanceof Resource)
+                oldResource = (Resource) obj;
+        } catch (NamingException e) {
+            // Ignore
+        }
+
+        RandomAccessFile randAccessContentFile =
+            new RandomAccessFile(contentFile, "rw");
+        try {
+            // Copy data in oldRevisionContent to contentFile
+            if (oldResource != null) {
+                BufferedInputStream bufOldRevStream = null;
+                try {
+                    bufOldRevStream = 
+                        new BufferedInputStream(oldResource.streamContent(),
+                                                BUFFER_SIZE);
+
+                    int numBytesRead;
+                    byte[] copyBuffer = new byte[BUFFER_SIZE];
+                    while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) {
+                        randAccessContentFile.write(copyBuffer, 0, numBytesRead);
+                    }
+                } finally {
+                    if (bufOldRevStream != null) {
+                        bufOldRevStream.close();
+                    }
+                }
+            }
+
+            randAccessContentFile.setLength(range.length);
+
+            // Append data in request input stream to contentFile
+            randAccessContentFile.seek(range.start);
+            int numBytesRead;
+            byte[] transferBuffer = new byte[BUFFER_SIZE];
+            BufferedInputStream requestBufInStream = null;
+            try {
+                requestBufInStream =
+                    new BufferedInputStream(req.getInputStream(), BUFFER_SIZE);
+
+                while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) {
+                    randAccessContentFile.write(transferBuffer, 0, numBytesRead);
+                }
+            } finally {
+                if (requestBufInStream != null) {
+                    requestBufInStream.close();
+                }
+            }
+        } finally {
+            randAccessContentFile.close();
+        }
+
+        return contentFile;
+
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param req The servlet request we are processing
+     * @param resp The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        boolean exists = true;
+        try {
+            resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        if (exists) {
+            boolean result = true;
+            try {
+                resources.unbind(path);
+            } catch (NamingException e) {
+                result = false;
+            }
+            if (result) {
+                resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
+            } else {
+                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+            }
+        } else {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+        }
+
+    }
+
+
+    /**
+     * Check if the conditions specified in the optional If headers are
+     * satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes The resource information
+     * @return boolean true if the resource meets all the specified conditions,
+     * and false if any of the conditions is not satisfied, in which case
+     * request processing is stopped
+     */
+    protected boolean checkIfHeaders(HttpServletRequest request,
+                                     HttpServletResponse response,
+                                     ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        return checkIfMatch(request, response, resourceAttributes)
+            && checkIfModifiedSince(request, response, resourceAttributes)
+            && checkIfNoneMatch(request, response, resourceAttributes)
+            && checkIfUnmodifiedSince(request, response, resourceAttributes);
+
+    }
+
+
+    /**
+     * URL rewriter.
+     *
+     * @param path Path which has to be rewritten
+     */
+    protected String rewriteUrl(String path) {
+        return urlEncoder.encode( path );
+    }
+
+
+    /**
+     * Display the size of a file.
+     */
+    protected void displaySize(StringBuilder buf, int filesize) {
+
+        int leftside = filesize / 1024;
+        int rightside = (filesize % 1024) / 103;  // makes 1 digit
+        // To avoid 0.0 for non-zero file, we bump to 0.1
+        if (leftside == 0 && rightside == 0 && filesize != 0)
+            rightside = 1;
+        buf.append(leftside).append(".").append(rightside);
+        buf.append(" KB");
+
+    }
+
+
+    /**
+     * Serve the specified resource, optionally including the data content.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param content Should the content be included?
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void serveResource(HttpServletRequest request,
+                                 HttpServletResponse response,
+                                 boolean content)
+        throws IOException, ServletException {
+
+        // Identify the requested resource path
+        String path = getRelativePath(request);
+        if (debug > 0) {
+            if (content)
+                log("DefaultServlet.serveResource:  Serving resource '" +
+                    path + "' headers and data");
+            else
+                log("DefaultServlet.serveResource:  Serving resource '" +
+                    path + "' headers only");
+        }
+
+        CacheEntry cacheEntry = null;
+        ProxyDirContext proxyDirContext = resources;
+        if (alternateDocBases == null
+                || alternateDocBases.size() == 0) {
+            cacheEntry = proxyDirContext.lookupCache(path);
+        } else {
+            AlternateDocBase match = AlternateDocBase.findMatch(
+                                            path, alternateDocBases);
+            if (match != null) {
+                cacheEntry = ((ProxyDirContext) ContextsAdapterUtility.unwrap(match.getResources())).lookupCache(path);
+            } else {
+                // None of the url patterns for alternate docbases matched
+                cacheEntry = proxyDirContext.lookupCache(path);
+            }
+        }
+
+        if (!cacheEntry.exists) {
+            // Check if we're included so we can return the appropriate 
+            // missing resource name in the error
+            String requestUri = (String) request.getAttribute(
+                RequestDispatcher.INCLUDE_REQUEST_URI);
+            /* IASRI 4878272 
+            if (requestUri == null) {
+                requestUri = request.getRequestURI();
+            } else {
+            */
+            if (requestUri != null) {
+                /*
+                 * We're included, and the response.sendError() below is going
+                 * to be ignored by the including resource (see SRV.8.3,
+                 * "The Include Method").
+                 * Therefore, the only way we can let the including resource
+                 * know about the missing resource is by throwing an 
+                 * exception
+                */
+                throw new FileNotFoundException(requestUri);
+            }
+
+            /* IASRI 4878272
+            response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                               requestUri);
+            */
+            // BEGIN IASRI 4878272
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            // END IASRI 4878272
+            return;
+        }
+
+        // If the resource is not a collection, and the resource path
+        // ends with "/" or "\", return NOT FOUND
+        if (cacheEntry.context == null) {
+            if (path.endsWith("/") || (path.endsWith("\\"))) {
+                /* IASRI 4878272
+                // Check if we're included so we can return the appropriate 
+                // missing resource name in the error
+                String requestUri = (String) request.getAttribute(
+                    RequestDispatcher.INCLUDE_REQUEST_URI);
+                if (requestUri == null) {
+                    requestUri = request.getRequestURI();
+                }
+                 response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                    requestUri);
+                */
+                // BEGIN IASRI 4878272
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                // END IASRI 4878272
+                return;
+            }
+        }
+
+        // Check if the conditions specified in the optional If headers are
+        // satisfied.
+        if (cacheEntry.context == null) {
+
+            // Checking If headers
+            boolean included =
+                (request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH) != null);
+            if (!included
+                && !checkIfHeaders(request, response, cacheEntry.attributes)) {
+                return;
+            }
+
+        }
+
+        // Find content type.
+        String contentType = cacheEntry.attributes.getMimeType();
+        if (contentType == null && !cacheEntry.attributes.isMimeTypeInitialized()) {
+            contentType = getServletContext().getMimeType(cacheEntry.name);
+            cacheEntry.attributes.setMimeType(contentType);
+        }
+
+        ArrayList<Range> ranges = null;
+        long contentLength = -1L;
+
+        if (cacheEntry.context != null) {
+
+            // Skip directory listings if we have been configured to
+            // suppress them
+            if (!listings) {
+                /* IASRI 4878272
+                 response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                    request.getRequestURI());
+                */
+                // BEGIN IASRI 4878272
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                // END IASRI 4878272
+                return;
+            }
+            contentType = "text/html;charset=UTF-8";
+
+        } else {
+            if (useAcceptRanges) {
+                // Accept ranges header
+                response.setHeader("Accept-Ranges", "bytes");
+            }
+
+            // Parse range specifier
+            ranges = parseRange(request, response, cacheEntry.attributes);
+
+            // ETag header
+            response.setHeader("ETag", cacheEntry.attributes.getETag());
+
+            // Last-Modified header
+            response.setHeader("Last-Modified",
+                    cacheEntry.attributes.getLastModifiedHttp());
+
+            // Get content length
+            contentLength = cacheEntry.attributes.getContentLength();
+            // Special case for zero length files, which would cause a
+            // (silent) ISE when setting the output buffer size
+            if (contentLength == 0L) {
+                content = false;
+            }
+
+        }
+
+        ServletOutputStream ostream = null;
+        PrintWriter writer = null;
+
+        if (content) {
+
+            // Trying to retrieve the servlet output stream
+
+            try {
+                ostream = response.getOutputStream();
+            } catch (IllegalStateException e) {
+                // If it fails, we try to get a Writer instead if we're
+                // trying to serve a text file
+                if ( (contentType == null)
+                     || (contentType.startsWith("text"))
+                     || (contentType.startsWith("xml")) ) {
+                    writer = response.getWriter();
+                } else {
+                    throw e;
+                }
+            }
+
+        }
+
+        if ( (cacheEntry.context != null) 
+                || ( ((ranges == null) || (ranges.isEmpty()))
+                        && (request.getHeader("Range") == null) )
+                || (ranges == FULL) ) {
+
+            // Set the appropriate output headers
+            if (contentType != null) {
+                if (debug > 0)
+                    log("DefaultServlet.serveFile:  contentType='" +
+                        contentType + "'");
+                response.setContentType(contentType);
+            }
+            if ((cacheEntry.resource != null) && (contentLength >= 0)) {
+                if (debug > 0)
+                    log("DefaultServlet.serveFile:  contentLength=" +
+                        contentLength);
+                if (contentLength < Integer.MAX_VALUE) {
+                    response.setContentLength((int) contentLength);
+                } else {
+                    // Set the content-length as String to be able to use a long
+                    response.setHeader("content-length", "" + contentLength);
+                }
+            }
+
+            InputStream renderResult = null;
+            if (cacheEntry.context != null) {
+
+                if (content) {
+                    // Serve the directory browser
+                    renderResult =
+                        render(request.getContextPath(), cacheEntry, proxyDirContext);
+                }
+
+            }
+
+            // Copy the input stream to our output stream (if requested)
+            if (content) {
+                try {
+                    response.setBufferSize(output);
+                } catch (IllegalStateException e) {
+                    // Silent catch
+                }
+                if (ostream != null) {
+                    if (!checkSendfile(request, response, cacheEntry, contentLength, null))
+                        copy(cacheEntry, renderResult, ostream);
+                } else {
+                    copy(cacheEntry, renderResult, writer);
+                }
+            }
+
+        } else {
+
+            if ((ranges == null) || (ranges.isEmpty()))
+                return;
+
+            if (maxHeaderRangeItems >= 0 && ranges.size() > maxHeaderRangeItems) {
+                response.sendError(HttpServletResponse.SC_FORBIDDEN);
+                return;
+            }
+
+            // Partial content response.
+
+            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
+
+            if (ranges.size() == 1) {
+
+                Range range = ranges.get(0);
+                response.addHeader("Content-Range", "bytes "
+                                   + range.start
+                                   + "-" + range.end + "/"
+                                   + range.length);
+                long length = range.end - range.start + 1;
+                if (length < Integer.MAX_VALUE) {
+                    response.setContentLength((int) length);
+                } else {
+                    // Set the content-length as String to be able to use a long
+                    response.setHeader("content-length", "" + length);
+                }
+
+                if (contentType != null) {
+                    if (debug > 0)
+                        log("DefaultServlet.serveFile:  contentType='" +
+                            contentType + "'");
+                    response.setContentType(contentType);
+                }
+
+                if (content) {
+                    try {
+                        response.setBufferSize(output);
+                    } catch (IllegalStateException e) {
+                        // Silent catch
+                    }
+                    if (ostream != null) {
+                        if (!checkSendfile(request, response, cacheEntry, range.end - range.start + 1, range))
+                            copy(cacheEntry, ostream, range);
+                    } else {
+                        copy(cacheEntry, writer, range);
+                    }
+                }
+
+            } else {
+
+                response.setContentType("multipart/byteranges; boundary="
+                                        + mimeSeparation);
+
+                if (content) {
+                    try {
+                        response.setBufferSize(output);
+                    } catch (IllegalStateException e) {
+                        // Silent catch
+                    }
+                    if (ostream != null) {
+                        copy(cacheEntry, ostream, ranges.iterator(),
+                             contentType);
+                    } else {
+                        copy(cacheEntry, writer, ranges.iterator(),
+                             contentType);
+                    }
+                }
+
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Parse the content-range header.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @return Range
+     */
+    protected Range parseContentRange(HttpServletRequest request,
+                                      HttpServletResponse response)
+        throws IOException {
+
+        // Retrieving the content-range header (if any is specified
+        String rangeHeader = request.getHeader("Content-Range");
+
+        if (rangeHeader == null)
+            return null;
+
+        // bytes is the only range unit supported
+        if (!rangeHeader.startsWith("bytes")) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        rangeHeader = rangeHeader.substring(6).trim();
+
+        int dashPos = rangeHeader.indexOf('-');
+        int slashPos = rangeHeader.indexOf('/');
+
+        if (dashPos == -1) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        if (slashPos == -1) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        Range range = new Range();
+
+        try {
+            range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
+            range.end =
+                Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
+            range.length = Long.parseLong
+                (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
+        } catch (NumberFormatException e) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        if (!range.validate()) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return null;
+        }
+
+        return range;
+
+    }
+
+
+    /**
+     * Parse the range header.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @return Vector of ranges
+     */
+    protected ArrayList<Range> parseRange(HttpServletRequest request,
+        HttpServletResponse response,
+        ResourceAttributes resourceAttributes) throws IOException {
+
+        // Checking If-Range
+        String headerValue = request.getHeader("If-Range");
+
+        if (headerValue != null) {
+
+            long headerValueTime = (-1L);
+            try {
+                headerValueTime = request.getDateHeader("If-Range");
+            } catch (IllegalArgumentException e) {
+                // Ignore
+            }
+
+            String eTag = resourceAttributes.getETag();
+            long lastModified = resourceAttributes.getLastModified();
+
+            if (headerValueTime == (-1L)) {
+
+                // If the ETag the client gave does not match the entity
+                // etag, then the entire entity is returned.
+                if (!eTag.equals(headerValue.trim()))
+                    return FULL;
+
+            } else {
+
+                // If the timestamp of the entity the client got is older than
+                // the last modification date of the entity, the entire entity
+                // is returned.
+                if (lastModified > (headerValueTime + 1000))
+                    return FULL;
+
+            }
+
+        }
+
+        long fileLength = resourceAttributes.getContentLength();
+
+        if (fileLength == 0)
+            return null;
+
+        // Retrieving the range header (if any is specified
+        String rangeHeader = request.getHeader("Range");
+
+        if (rangeHeader == null)
+            return null;
+        // bytes is the only range unit supported (and I don't see the point
+        // of adding new ones).
+        if (!rangeHeader.startsWith("bytes")) {
+            response.addHeader("Content-Range", "bytes */" + fileLength);
+            response.sendError
+                (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+            return null;
+        }
+
+        rangeHeader = rangeHeader.substring(6);
+
+        // Vector which will contain all the ranges which are successfully
+        // parsed.
+        ArrayList<Range> result = new ArrayList<Range>();
+        StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
+
+        // Parsing the range list
+        while (commaTokenizer.hasMoreTokens()) {
+            String rangeDefinition = commaTokenizer.nextToken().trim();
+
+            Range currentRange = new Range();
+            currentRange.length = fileLength;
+
+            int dashPos = rangeDefinition.indexOf('-');
+
+            if (dashPos == -1) {
+                response.addHeader("Content-Range", "bytes */" + fileLength);
+                response.sendError
+                    (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+                return null;
+            }
+
+            if (dashPos == 0) {
+
+                try {
+                    long offset = Long.parseLong(rangeDefinition);
+                    currentRange.start = fileLength + offset;
+                    currentRange.end = fileLength - 1;
+                } catch (NumberFormatException e) {
+                    response.addHeader("Content-Range",
+                                       "bytes */" + fileLength);
+                    response.sendError
+                        (HttpServletResponse
+                         .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+                    return null;
+                }
+
+            } else {
+
+                try {
+                    currentRange.start = Long.parseLong
+                        (rangeDefinition.substring(0, dashPos));
+                    if (dashPos < rangeDefinition.length() - 1)
+                        currentRange.end = Long.parseLong
+                            (rangeDefinition.substring
+                             (dashPos + 1, rangeDefinition.length()));
+                    else
+                        currentRange.end = fileLength - 1;
+                } catch (NumberFormatException e) {
+                    response.addHeader("Content-Range",
+                                       "bytes */" + fileLength);
+                    response.sendError
+                        (HttpServletResponse
+                         .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+                    return null;
+                }
+
+            }
+
+            if (!currentRange.validate()) {
+                response.addHeader("Content-Range", "bytes */" + fileLength);
+                response.sendError
+                    (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
+                return null;
+            }
+
+            result.add(currentRange);
+        }
+
+        return result;
+    }
+
+
+
+    /**
+     *  Decide which way to render. HTML or XML.
+     */
+    protected InputStream render(String contextPath, CacheEntry cacheEntry)
+            throws IOException, ServletException {
+        return render(contextPath, cacheEntry, resources);
+    }
+
+    private InputStream render(String contextPath, CacheEntry cacheEntry,
+            ProxyDirContext proxyDirContext)
+            throws IOException, ServletException {
+
+        Source xsltSource = findXsltInputStream(cacheEntry.context);
+
+        if (xsltSource == null) {
+            return renderHtml(contextPath, cacheEntry, proxyDirContext);
+        } else {
+            return renderXml(contextPath, cacheEntry, xsltSource, proxyDirContext);
+        }
+
+    }
+
+    /**
+     * Return an InputStream to an HTML representation of the contents
+     * of this directory.
+     *
+     * @param contextPath Context path to which our internal paths are
+     *  relative
+     */
+    protected InputStream renderXml(String contextPath,
+                                    CacheEntry cacheEntry,
+                                    Source xsltSource)
+            throws IOException, ServletException {
+        return renderXml(contextPath, cacheEntry, xsltSource, resources);
+    }
+
+    private InputStream renderXml(String contextPath,
+                                    CacheEntry cacheEntry,
+                                    Source xsltSource,
+                                    ProxyDirContext proxyDirContext)
+            throws IOException, ServletException {
+
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<?xml version=\"1.0\"?>");
+        sb.append("<listing ");
+        sb.append(" contextPath='");
+        sb.append(contextPath);
+        sb.append("'");
+        sb.append(" directory='");
+        sb.append(cacheEntry.name);
+        sb.append("' ");
+        sb.append(" hasParent='").append(!cacheEntry.name.equals("/"));
+        sb.append("'>");
+
+        sb.append("<entries>");
+
+        try {
+
+            // Render the directory entries within this directory
+            Enumeration<NameClassPair> enumeration =
+                proxyDirContext.list(cacheEntry.name);
+            if (sortedBy.equals(SortedBy.LAST_MODIFIED)) {
+                ArrayList<NameClassPair> list = 
+                    Collections.list(enumeration);
+                Comparator<NameClassPair> c = new LastModifiedComparator(
+                    proxyDirContext, cacheEntry.name);
+                Collections.sort(list, c);
+                enumeration = Collections.enumeration(list);
+            } else if (sortedBy.equals(SortedBy.SIZE)) {
+                ArrayList<NameClassPair> list = 
+                    Collections.list(enumeration);
+                Comparator<NameClassPair> c = new SizeComparator(
+                    proxyDirContext, cacheEntry.name);
+                Collections.sort(list, c);
+                enumeration = Collections.enumeration(list);
+            }
+
+            // rewriteUrl(contextPath) is expensive. cache result for later reuse
+            String rewrittenContextPath =  rewriteUrl(contextPath);
+
+            while (enumeration.hasMoreElements()) {
+
+                NameClassPair ncPair = enumeration.nextElement();
+                String resourceName = ncPair.getName();
+                String trimmed = resourceName/*.substring(trim)*/;
+                if (trimmed.equalsIgnoreCase("WEB-INF") ||
+                    trimmed.equalsIgnoreCase("META-INF") ||
+                    trimmed.equalsIgnoreCase(localXsltFile))
+                    continue;
+
+                if ((cacheEntry.name + trimmed).equals(contextXsltFile))
+                    continue;
+
+                CacheEntry childCacheEntry =
+                    proxyDirContext.lookupCache(cacheEntry.name + resourceName);
+                if (!childCacheEntry.exists) {
+                    continue;
+                }
+
+                sb.append("<entry");
+                sb.append(" type='")
+                  .append((childCacheEntry.context != null)?"dir":"file")
+                  .append("'");
+                sb.append(" urlPath='")
+                  .append(rewrittenContextPath)
+                  .append(rewriteUrl(cacheEntry.name + resourceName))
+                  .append((childCacheEntry.context != null)?"/":"")
+                  .append("'");
+                if (childCacheEntry.resource != null) {
+                    sb.append(" size='")
+                      .append(renderSize(childCacheEntry.attributes.getContentLength()))
+                      .append("'");
+                }
+                sb.append(" date='")
+                  .append(childCacheEntry.attributes.getLastModifiedHttp())
+                  .append("'");
+
+                sb.append(">");
+                sb.append(HtmlEntityEncoder.encodeXSS(trimmed));
+                if (childCacheEntry.context != null)
+                    sb.append("/");
+                sb.append("</entry>");
+
+            }
+
+        } catch (NamingException e) {
+            // Something went wrong
+            throw new ServletException("Error accessing resource", e);
+        }
+
+        sb.append("</entries>");
+
+        String readme = getReadme(cacheEntry.context);
+
+        if (readme!=null) {
+            sb.append("<readme><![CDATA[");
+            sb.append(readme);
+            sb.append("]]></readme>");
+        }
+
+
+        sb.append("</listing>");
+
+        // Prevent possible memory leak. Ensure Transformer and
+        // TransformerFactory are not loaded from the web application.
+        ClassLoader original;
+        if (Globals.IS_SECURITY_ENABLED) {
+            PrivilegedGetTccl pa = new PrivilegedGetTccl();
+            original = AccessController.doPrivileged(pa);
+        } else {
+            original = Thread.currentThread().getContextClassLoader();
+        }
+        try {
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedSetTccl pa =
+                        new PrivilegedSetTccl(DefaultServlet.class.getClassLoader());
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(
+                        DefaultServlet.class.getClassLoader());
+            }
+            TransformerFactory tFactory = TransformerFactory.newInstance();
+            Source xmlSource = new StreamSource(new StringReader(sb.toString()));
+            Transformer transformer = tFactory.newTransformer(xsltSource);
+
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+            OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
+            StreamResult out = new StreamResult(osWriter);
+            transformer.transform(xmlSource, out);
+            osWriter.flush();
+            return (new ByteArrayInputStream(stream.toByteArray()));
+        } catch (Exception e) {
+            log("directory transform failure: " + e.getMessage());
+            return renderHtml(contextPath, cacheEntry);
+        } finally {
+            if (Globals.IS_SECURITY_ENABLED) {
+                PrivilegedSetTccl pa = new PrivilegedSetTccl(original);
+                AccessController.doPrivileged(pa);
+            } else {
+                Thread.currentThread().setContextClassLoader(original);
+            }
+        }
+    }
+
+    /**
+     * Return an InputStream to an HTML representation of the contents
+     * of this directory.
+     *
+     * @param contextPath Context path to which our internal paths are
+     *  relative
+     */
+    protected InputStream renderHtml (String contextPath, CacheEntry cacheEntry)
+            throws IOException, ServletException {
+        return renderHtml(contextPath, cacheEntry, resources);
+    }
+
+    private InputStream renderHtml (String contextPath, CacheEntry cacheEntry,
+            ProxyDirContext proxyDirContext)
+            throws IOException, ServletException {
+        String name = cacheEntry.name;
+
+        /*
+        // Number of characters to trim from the beginnings of filenames
+        int trim = name.length();
+        if (!name.endsWith("/"))
+            trim += 1;
+        if (name.equals("/"))
+            trim = 1;
+        */
+
+        // Prepare a writer to a buffered area
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        OutputStreamWriter osWriter = null;
+        try {
+            osWriter = new OutputStreamWriter(stream, "UTF8");
+        } catch (Exception e) {
+            // Should never happen
+            osWriter = new OutputStreamWriter(stream);
+        }
+        PrintWriter writer = new PrintWriter(osWriter);
+
+        StringBuilder sb = new StringBuilder();
+        
+        // rewriteUrl(contextPath) is expensive. cache result for later reuse
+        String rewrittenContextPath =  rewriteUrl(contextPath);
+
+        String dirTitle = MessageFormat.format(rb.getString(LogFacade.DIR_TITLE_INFO), name);
+
+        // Render the page header
+        sb.append("<html>\r\n");
+        sb.append("<head>\r\n");
+        sb.append("<title>");
+        sb.append(dirTitle);
+        sb.append("</title>\r\n");
+        sb.append("<STYLE><!--");
+        sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
+        sb.append("--></STYLE> ");
+        sb.append("</head>\r\n");
+        sb.append("<body>");
+        sb.append("<h1>");
+        sb.append(dirTitle);
+
+        // Render the link to our parent (if required)
+        String parentDirectory = name;
+
+        if (parentDirectory.endsWith("/")) {
+            parentDirectory =
+                parentDirectory.substring(0, parentDirectory.length() - 1);
+        }
+        int slash = parentDirectory.lastIndexOf('/');
+        if (slash >= 0) {
+            String parent = name.substring(0, slash);
+            String dirParent = MessageFormat.format(rb.getString(LogFacade.DIR_PARENT_INFO), parent);
+            sb.append(" - <a href=\"");
+            sb.append(rewrittenContextPath);
+            if (parent.equals(""))
+                parent = "/";
+            sb.append(rewriteUrl(parent));
+            if (!parent.endsWith("/"))
+                sb.append("/");
+            sb.append("\">");
+            sb.append("<b>");
+            sb.append(dirParent);
+            sb.append("</b>");
+            sb.append("</a>");
+        }
+
+        sb.append("</h1>");
+        sb.append("<HR size=\"1\" noshade=\"noshade\">");
+
+        sb.append("<table width=\"100%\" cellspacing=\"0\"" +
+                     " cellpadding=\"5\" align=\"center\">\r\n");
+
+        // Render the column headings
+        sb.append("<tr>\r\n");
+        sb.append("<td align=\"left\"><font size=\"+1\"><strong>");
+        sb.append(rb.getString(LogFacade.DIR_FILENAME_INFO));
+        sb.append("</strong></font></td>\r\n");
+        sb.append("<td align=\"center\"><font size=\"+1\"><strong>");
+        sb.append(rb.getString(LogFacade.DIR_SIZE_INFO));
+        sb.append("</strong></font></td>\r\n");
+        sb.append("<td align=\"right\"><font size=\"+1\"><strong>");
+        sb.append(rb.getString(LogFacade.DIR_LAST_MODIFIED_INFO));
+        sb.append("</strong></font></td>\r\n");
+        sb.append("</tr>");
+
+        try {
+
+            // Render the directory entries within this directory
+            Enumeration<NameClassPair> enumeration =
+                proxyDirContext.list(cacheEntry.name);
+            if (sortedBy.equals(SortedBy.LAST_MODIFIED)) {
+                ArrayList<NameClassPair> list = 
+                    Collections.list(enumeration);
+                Comparator<NameClassPair> c = new LastModifiedComparator(
+                    proxyDirContext, cacheEntry.name);
+                Collections.sort(list, c);
+                enumeration = Collections.enumeration(list);
+            } else if (sortedBy.equals(SortedBy.SIZE)) {
+                ArrayList<NameClassPair> list = 
+                    Collections.list(enumeration);
+                Comparator<NameClassPair> c = new SizeComparator(
+                    proxyDirContext, cacheEntry.name);
+                Collections.sort(list, c);
+                enumeration = Collections.enumeration(list);
+            }
+
+            boolean shade = false;
+            while (enumeration.hasMoreElements()) {
+
+                NameClassPair ncPair = enumeration.nextElement();
+                String resourceName = ncPair.getName();
+                String trimmed = resourceName/*.substring(trim)*/;
+                if (trimmed.equalsIgnoreCase("WEB-INF") ||
+                    trimmed.equalsIgnoreCase("META-INF"))
+                    continue;
+
+                CacheEntry childCacheEntry =
+                    proxyDirContext.lookupCache(cacheEntry.name + resourceName);
+                if (!childCacheEntry.exists) {
+                    continue;
+                }
+
+                sb.append("<tr");
+                if (shade)
+                    sb.append(" bgcolor=\"#eeeeee\"");
+                sb.append(">\r\n");
+                shade = !shade;
+
+                sb.append("<td align=\"left\">&nbsp;&nbsp;\r\n");
+                sb.append("<a href=\"");
+                sb.append(rewrittenContextPath);
+                resourceName = rewriteUrl(name + resourceName);
+                sb.append(resourceName);
+                if (childCacheEntry.context != null)
+                    sb.append("/");
+                sb.append("\"><tt>");
+                sb.append(HtmlEntityEncoder.encodeXSS(trimmed));
+                if (childCacheEntry.context != null)
+                    sb.append("/");
+                sb.append("</tt></a></td>\r\n");
+
+                sb.append("<td align=\"right\"><tt>");
+                if (childCacheEntry.context != null)
+                    sb.append("&nbsp;");
+                else
+                    sb.append(renderSize(childCacheEntry.attributes.getContentLength()));
+                sb.append("</tt></td>\r\n");
+
+                sb.append("<td align=\"right\"><tt>");
+                sb.append(childCacheEntry.attributes.getLastModifiedHttp());
+                sb.append("</tt></td>\r\n");
+
+                sb.append("</tr>\r\n");
+            }
+
+        } catch (NamingException e) {
+            // Something went wrong
+            throw new ServletException("Error accessing resource", e);
+        }
+
+        // Render the page footer
+        sb.append("</table>\r\n");
+
+        sb.append("<HR size=\"1\" noshade=\"noshade\">");
+
+        String readme = getReadme(cacheEntry.context);
+        if (readme!=null) {
+            sb.append(readme);
+            sb.append("<HR size=\"1\" noshade=\"noshade\">");
+        }
+
+        String serverInfo = ServerInfo.getPublicServerInfo();
+        if (serverInfo != null && !serverInfo.isEmpty()) {
+            sb.append("<h3>").append(serverInfo).append("</h3>");
+        }
+        sb.append("</body>\r\n");
+        sb.append("</html>\r\n");
+
+        // Return an input stream to the underlying bytes
+        writer.write(sb.toString());
+        writer.flush();
+        return (new ByteArrayInputStream(stream.toByteArray()));
+
+    }
+
+
+    /**
+     * Render the specified file size (in bytes).
+     *
+     * @param size File size (in bytes)
+     */
+    protected String renderSize(long size) {
+
+        long leftSide = size / 1024;
+        long rightSide = (size % 1024) / 103;   // Makes 1 digit
+        if ((leftSide == 0) && (rightSide == 0) && (size > 0))
+            rightSide = 1;
+
+        return ("" + leftSide + "." + rightSide + " kb");
+
+    }
+
+
+    /**
+     * Get the readme file as a string.
+     */
+    protected String getReadme(DirContext directory)
+        throws IOException, ServletException {
+
+        if (readmeFile!=null) {
+            try {
+                Object obj = directory.lookup(readmeFile);
+
+                if (obj!=null && obj instanceof Resource) {
+                    StringWriter buffer = new StringWriter();
+                    InputStream is = ((Resource)obj).streamContent();
+                    copyRange(new InputStreamReader(is),
+                              new PrintWriter(buffer));
+
+                    return buffer.toString();
+                 }
+             } catch(Throwable e) {
+                 ; /* Should only be IOException or NamingException
+                    * can be ignored
+                    */
+                 if (debug > 10)
+                    log("readme '" + readmeFile + "' not found", e);
+             }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Return a Source for the xsl template (if possible)
+     */
+    protected Source findXsltInputStream(DirContext directory)
+        throws IOException, ServletException {
+
+        if (localXsltFile!=null) {
+            try {
+                Object obj = directory.lookup(localXsltFile);
+                if (obj!=null && obj instanceof Resource) {
+                    InputStream is = ((Resource)obj).streamContent();
+                    if (is != null) {
+                        if (Globals.IS_SECURITY_ENABLED) {
+                            return secureXslt(is);
+                        } else {
+                            return new StreamSource(is);
+                        }
+                    }
+                }
+             } catch(Throwable e) {
+                 ; /* Should only be IOException or NamingException
+                    * can be ignored
+                    */
+                if (debug > 10)
+                    log("localXsltFile '" + localXsltFile + "' not found", e);
+
+                return null;
+             }
+        }
+
+        if (contextXsltFile != null) {
+            InputStream is =
+                getServletContext().getResourceAsStream(contextXsltFile);
+            if (is != null) {
+                if (Globals.IS_SECURITY_ENABLED) {
+                    return secureXslt(is);
+                } else {
+                    return new StreamSource(is);
+                }
+            }
+
+            if (debug > 10)
+                log("contextXsltFile '" + contextXsltFile + "' not found");
+        }
+
+        /*  Open and read in file in one fell swoop to reduce chance
+         *  chance of leaving handle open.
+         */
+        if (globalXsltFile!=null) {
+            File f = validateGlobalXsltFile();
+            if (f != null){
+                FileInputStream fis = null;
+                try {
+                    fis = new FileInputStream(f);
+                    long len = f.length();
+                    byte b[] = new byte[(int)len]; /* danger! */
+                    if (len != fis.read(b)) {
+                        throw new IOException(MessageFormat.format(rb.getString(LogFacade.READ_FILE_EXCEPTION), f.getAbsolutePath()));
+                    }
+                    return new StreamSource(new ByteArrayInputStream(b));
+                } finally {
+                    if (fis != null) {
+                        try {
+                            fis.close();
+                        } catch (IOException ioe) {
+                            if (debug > 10) {
+                                log(ioe.getMessage(), ioe);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+
+    }
+
+    private File validateGlobalXsltFile() {
+
+        File result = null;
+        String base = System.getProperty("catalina.base");
+
+        if (base != null) {
+            File baseConf = new File(base, "conf");
+            result = validateGlobalXsltFile(baseConf);
+        }
+
+        if (result == null) {
+            String home = System.getProperty("catalina.home");
+            if (home != null && !home.equals(base)) {
+                File homeConf = new File(home, "conf");
+                result = validateGlobalXsltFile(homeConf);
+            }
+        }
+
+        return result;
+    }
+
+
+    private File validateGlobalXsltFile(File base) {
+        File candidate = new File(globalXsltFile);
+        if (!candidate.isAbsolute()) {
+            candidate = new File(base, globalXsltFile);
+        }
+
+        if (!candidate.isFile()) {
+            return null;
+        }
+
+        // First check that the resulting path is under the provided base
+        try {
+            if (!candidate.getCanonicalPath().startsWith(base.getCanonicalPath())) {
+                return null;
+            }
+        } catch (IOException ioe) {
+            return null;
+        }
+
+        // Next check that an .xsl or .xslt file has been specified
+        String nameLower = candidate.getName().toLowerCase(Locale.ENGLISH);
+        if (!nameLower.endsWith(".xslt") && !nameLower.endsWith(".xsl")) {
+            return null;
+        }
+
+        return candidate;
+    }
+
+
+    private Source secureXslt(InputStream is) {
+        // Need to filter out any external entities
+        Source result = null;
+        try {
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            builder.setEntityResolver(secureEntityResolver);
+            Document document = builder.parse(is);
+            result = new DOMSource(document);
+        } catch (ParserConfigurationException e) {
+            if (debug > 0) {
+                log(e.getMessage(), e);
+            }
+        } catch (SAXException e) {
+            if (debug > 0) {
+                log(e.getMessage(), e);
+            }
+        } catch (IOException e) {
+            if (debug > 0) {
+                log(e.getMessage(), e);
+            }
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    if (debug > 10) {
+                        log(e.getMessage(), e);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+ 
+
+    // -------------------------------------------------------- protected Methods
+
+
+    /**
+     * Check if sendfile can be used.
+     */
+    protected boolean checkSendfile(HttpServletRequest request,
+                                  HttpServletResponse response,
+                                  CacheEntry entry,
+                                  long length, Range range) {
+        if ((sendfileSize > 0)
+            && (entry.resource != null)
+            && ((length > sendfileSize) || (entry.resource.getContent() == null))
+            && (entry.attributes.getCanonicalPath() != null)
+            && (Boolean.TRUE.equals(request.getAttribute("org.apache.tomcat.sendfile.support")))
+            && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade"))
+            && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) {
+            request.setAttribute("org.apache.tomcat.sendfile.filename", entry.attributes.getCanonicalPath());
+            if (range == null) {
+                request.setAttribute("org.apache.tomcat.sendfile.start", Long.valueOf(0L));
+                request.setAttribute("org.apache.tomcat.sendfile.end", Long.valueOf(length));
+            } else {
+                request.setAttribute("org.apache.tomcat.sendfile.start", Long.valueOf(range.start));
+                request.setAttribute("org.apache.tomcat.sendfile.end", Long.valueOf(range.end + 1));
+            }
+            request.setAttribute("org.apache.tomcat.sendfile.token", this);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    /**
+     * Check if the if-match condition is satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes File object
+     * @return boolean true if the resource meets the specified condition,
+     * and false if the condition is not satisfied, in which case request
+     * processing is stopped
+     */
+    protected boolean checkIfMatch(HttpServletRequest request,
+                                 HttpServletResponse response,
+                                 ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        String eTag = resourceAttributes.getETag();
+        String headerValue = request.getHeader("If-Match");
+        if (headerValue != null) {
+            if (headerValue.indexOf('*') == -1) {
+
+                StringTokenizer commaTokenizer = new StringTokenizer
+                    (headerValue, ",");
+                boolean conditionSatisfied = false;
+
+                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
+                    String currentToken = commaTokenizer.nextToken();
+                    if (currentToken.trim().equals(eTag))
+                        conditionSatisfied = true;
+                }
+
+                // If none of the given ETags match, 412 Precodition failed is
+                // sent back
+                if (!conditionSatisfied) {
+                    response.sendError
+                        (HttpServletResponse.SC_PRECONDITION_FAILED);
+                    return false;
+                }
+
+            }
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Check if the if-modified-since condition is satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes File object
+     * @return boolean true if the resource meets the specified condition,
+     * and false if the condition is not satisfied, in which case request
+     * processing is stopped
+     */
+    protected boolean checkIfModifiedSince(HttpServletRequest request,
+                                           HttpServletResponse response,
+                                           ResourceAttributes resourceAttributes)
+        throws IOException {
+        try {
+            long headerValue = request.getDateHeader("If-Modified-Since");
+            long lastModified = resourceAttributes.getLastModified();
+            if (headerValue != -1) {
+
+                // If an If-None-Match header has been specified,
+                // If-Modified-Since is ignored.
+                if ((request.getHeader("If-None-Match") == null)
+                    && (lastModified < headerValue + 1000)) {
+                    // The entity has not been modified since the date
+                    // specified by the client. This is not an error case.
+                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+                    response.setHeader("ETag", resourceAttributes.getETag());
+                    return false;
+                }
+            }
+        } catch(IllegalArgumentException illegalArgument) {
+            return true;
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Check if the if-none-match condition is satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes File object
+     * @return boolean true if the resource meets the specified condition,
+     * and false if the condition is not satisfied, in which case request
+     * processing is stopped
+     */
+    protected boolean checkIfNoneMatch(HttpServletRequest request,
+                                       HttpServletResponse response,
+                                       ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        String eTag = resourceAttributes.getETag();
+        String headerValue = request.getHeader("If-None-Match");
+        if (headerValue != null) {
+
+            boolean conditionSatisfied = false;
+
+            if (!headerValue.equals("*")) {
+
+                StringTokenizer commaTokenizer =
+                    new StringTokenizer(headerValue, ",");
+
+                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
+                    String currentToken = commaTokenizer.nextToken();
+                    if (currentToken.trim().equals(eTag))
+                        conditionSatisfied = true;
+                }
+
+            } else {
+                conditionSatisfied = true;
+            }
+
+            if (conditionSatisfied) {
+
+                // For GET and HEAD, we should respond with
+                // 304 Not Modified.
+                // For every other method, 412 Precondition Failed is sent
+                // back.
+                if ( ("GET".equals(request.getMethod()))
+                     || ("HEAD".equals(request.getMethod())) ) {
+                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+                    response.setHeader("ETag", eTag);
+                    return false;
+                } else {
+                    response.sendError
+                        (HttpServletResponse.SC_PRECONDITION_FAILED);
+                    return false;
+                }
+            }
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Check if the if-unmodified-since condition is satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes File object
+     * @return boolean true if the resource meets the specified condition,
+     * and false if the condition is not satisfied, in which case request
+     * processing is stopped
+     */
+    protected boolean checkIfUnmodifiedSince(HttpServletRequest request,
+                                           HttpServletResponse response,
+                                           ResourceAttributes resourceAttributes)
+        throws IOException {
+        try {
+            long lastModified = resourceAttributes.getLastModified();
+            long headerValue = request.getDateHeader("If-Unmodified-Since");
+            if (headerValue != -1) {
+                if ( lastModified >= (headerValue + 1000)) {
+                    // The entity has not been modified since the date
+                    // specified by the client. This is not an error case.
+                    response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+                    return false;
+                }
+            }
+        } catch(IllegalArgumentException illegalArgument) {
+            return true;
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param cacheEntry The CacheEntry object
+     * @param is The InputStream
+     * @param ostream The output stream to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected void copy(CacheEntry cacheEntry, InputStream is,
+                        ServletOutputStream ostream)
+        throws IOException {
+
+        IOException exception = null;
+        InputStream resourceInputStream = null;
+
+        // Optimization: If the binary content has already been loaded, send
+        // it directly
+        if (cacheEntry.resource != null) {
+            byte buffer[] = cacheEntry.resource.getContent();
+            if (buffer != null) {
+                ostream.write(buffer, 0, buffer.length);
+                return;
+            }
+            resourceInputStream = cacheEntry.resource.streamContent();
+        } else {
+            resourceInputStream = is;
+        }
+
+        InputStream istream = new BufferedInputStream
+            (resourceInputStream, input);
+
+        try {
+            // Copy the input stream to the output stream
+            exception = copyRange(istream, ostream);
+        } finally {
+            // Clean up the input stream
+            istream.close();
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param cacheEntry The cache entry
+     * @param is The InputStream
+     * @param writer The writer to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected void copy(CacheEntry cacheEntry, InputStream is,
+                        PrintWriter writer)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = null;
+        if (cacheEntry.resource != null) {
+            resourceInputStream = cacheEntry.resource.streamContent();
+        } else {
+            resourceInputStream = is;
+        }
+
+        Reader reader;
+        if (fileEncoding == null) {
+            reader = new InputStreamReader(resourceInputStream);
+        } else {
+            reader = new InputStreamReader(resourceInputStream,
+                                           fileEncoding);
+        }
+
+        // Copy the input stream to the output stream
+        exception = copyRange(reader, writer);
+
+        // Clean up the reader
+        reader.close();
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param cacheEntry The CacheEntry object
+     * @param ostream The output stream to write to
+     * @param range Range the client wanted to retrieve
+     * @exception IOException if an input/output error occurs
+     */
+    protected void copy(CacheEntry cacheEntry, ServletOutputStream ostream,
+                        Range range)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = cacheEntry.resource.streamContent();
+        InputStream istream =
+            new BufferedInputStream(resourceInputStream, input);
+        try {
+            exception = copyRange(istream, ostream, range.start, range.end);
+        } finally {
+            // Clean up the input stream
+            istream.close();
+        }
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param cacheEntry The CacheEntry object
+     * @param writer The writer to write to
+     * @param range Range the client wanted to retrieve
+     * @exception IOException if an input/output error occurs
+     */
+    protected void copy(CacheEntry cacheEntry, PrintWriter writer,
+                        Range range)
+        throws IOException {
+
+        IOException exception = null;
+
+        InputStream resourceInputStream = cacheEntry.resource.streamContent();
+
+        Reader reader;
+        if (fileEncoding == null) {
+            reader = new InputStreamReader(resourceInputStream);
+        } else {
+            reader = new InputStreamReader(resourceInputStream,
+                                           fileEncoding);
+        }
+
+        exception = copyRange(reader, writer, range.start, range.end);
+
+        // Clean up the input stream
+        reader.close();
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param cacheEntry The CacheEntry object
+     * @param ostream The output stream to write to
+     * @param ranges Enumeration of the ranges the client wanted to retrieve
+     * @param contentType Content type of the resource
+     * @exception IOException if an input/output error occurs
+     */
+    protected void copy(CacheEntry cacheEntry, ServletOutputStream ostream,
+                        Iterator<Range> ranges, String contentType)
+        throws IOException {
+
+        IOException exception = null;
+
+        while ( (exception == null) && (ranges.hasNext()) ) {
+
+            InputStream resourceInputStream = cacheEntry.resource.streamContent();
+            InputStream istream = null;
+            try {
+                istream = 
+                    new BufferedInputStream(resourceInputStream, input);
+
+                Range currentRange = ranges.next();
+
+                // Writing MIME header.
+                ostream.println();
+                ostream.println("--" + mimeSeparation);
+                if (contentType != null)
+                    ostream.println("Content-Type: " + contentType);
+                ostream.println("Content-Range: bytes " + currentRange.start
+                               + "-" + currentRange.end + "/"
+                               + currentRange.length);
+                ostream.println();
+
+                // Printing content
+                exception = copyRange(istream, ostream, currentRange.start,
+                                      currentRange.end);
+
+            } finally {
+                if (istream != null) {
+                    istream.close();
+                }
+            }
+        }
+
+        ostream.println();
+        ostream.print("--" + mimeSeparation + "--");
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param cacheEntry The CacheEntry object
+     * @param writer The writer to write to
+     * @param ranges Enumeration of the ranges the client wanted to retrieve
+     * @param contentType Content type of the resource
+     * @exception IOException if an input/output error occurs
+     */
+    protected void copy(CacheEntry cacheEntry, PrintWriter writer,
+                        Iterator<Range> ranges, String contentType)
+        throws IOException {
+
+        IOException exception = null;
+
+        while ( (exception == null) && (ranges.hasNext()) ) {
+
+            InputStream resourceInputStream = cacheEntry.resource.streamContent();
+            
+            Reader reader;
+            if (fileEncoding == null) {
+                reader = new InputStreamReader(resourceInputStream);
+            } else {
+                reader = new InputStreamReader(resourceInputStream,
+                                               fileEncoding);
+            }
+
+            Range currentRange = ranges.next();
+
+            // Writing MIME header.
+            writer.println();
+            writer.println("--" + mimeSeparation);
+            if (contentType != null)
+                writer.println("Content-Type: " + contentType);
+            writer.println("Content-Range: bytes " + currentRange.start
+                           + "-" + currentRange.end + "/"
+                           + currentRange.length);
+            writer.println();
+
+            // Printing content
+            exception = copyRange(reader, writer, currentRange.start,
+                                  currentRange.end);
+
+            reader.close();  
+        }
+
+        writer.println();
+        writer.print("--" + mimeSeparation + "--");
+
+        // Rethrow any exception that has occurred
+        if (exception != null)
+            throw exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param istream The input stream to read from
+     * @param ostream The output stream to write to
+     * @return Exception which occurred during processing
+     */
+    protected IOException copyRange(InputStream istream,
+                                    ServletOutputStream ostream) {
+        // Copy the input stream to the output stream
+        IOException exception = null;
+        byte buffer[] = new byte[input];
+        int len;
+        while (true) {
+            try {
+                len = istream.read(buffer);
+                if (len == -1)
+                    break;
+                ostream.write(buffer, 0, len);
+            } catch (IOException e) {
+                exception = e;
+                break;
+            }
+        }
+        return exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param reader The reader to read from
+     * @param writer The writer to write to
+     * @return Exception which occurred during processing
+     */
+    protected IOException copyRange(Reader reader, PrintWriter writer) {
+        // Copy the input stream to the output stream
+        IOException exception = null;
+        char buffer[] = new char[input];
+        int len;
+        while (true) {
+            try {
+                len = reader.read(buffer);
+                if (len == -1)
+                    break;
+                writer.write(buffer, 0, len);
+            } catch (IOException e) {
+                exception = e;
+                break;
+            }
+        }
+        return exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param istream The input stream to read from
+     * @param ostream The output stream to write to
+     * @param start Start of the range which will be copied
+     * @param end End of the range which will be copied
+     * @return Exception which occurred during processing
+     */
+    protected IOException copyRange(InputStream istream,
+                                    ServletOutputStream ostream,
+                                    long start, long end) {
+        if (debug > 10)
+            log("Serving bytes:" + start + "-" + end);
+
+        long skipped = 0;
+        try {
+            skipped = istream.skip(start);
+        } catch (IOException e) {
+            return e;
+        }
+        if (skipped < start) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SKIP_BYTES_EXCEPTION),
+                                              new Object[] {Long.valueOf(skipped),
+                                                            Long.valueOf(start)});
+            return new IOException(msg);
+        }
+
+        IOException exception = null;
+        long bytesToRead = end - start + 1;
+
+        byte buffer[] = new byte[input];
+        int len = buffer.length;
+        while ( (bytesToRead > 0) && (len >= buffer.length)) {
+            try {
+                len = istream.read(buffer);
+                if (bytesToRead >= len) {
+                    ostream.write(buffer, 0, len);
+                    bytesToRead -= len;
+                } else {
+                    ostream.write(buffer, 0, (int) bytesToRead);
+                    bytesToRead = 0;
+                }
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+            }
+            if (len < buffer.length)
+                break;
+        }
+
+        return exception;
+
+    }
+
+
+    /**
+     * Copy the contents of the specified input stream to the specified
+     * output stream, and ensure that both streams are closed before returning
+     * (even in the face of an exception).
+     *
+     * @param reader The reader to read from
+     * @param writer The writer to write to
+     * @param start Start of the range which will be copied
+     * @param end End of the range which will be copied
+     * @return Exception which occurred during processing
+     */
+    protected IOException copyRange(Reader reader, PrintWriter writer,
+                                    long start, long end) {
+
+        long skipped = 0;
+        try {
+            skipped = reader.skip(start);
+        } catch (IOException e) {
+            return e;
+        }
+        if (skipped < start) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SKIP_BYTES_EXCEPTION),
+                                              new Object[] {Long.valueOf(skipped),
+                                                            Long.valueOf(start)});
+            return new IOException(msg);
+        } 
+
+        IOException exception = null;
+        long bytesToRead = end - start + 1;
+
+        char buffer[] = new char[input];
+        int len = buffer.length;
+        while ( (bytesToRead > 0) && (len >= buffer.length)) {
+            try {
+                len = reader.read(buffer);
+                if (bytesToRead >= len) {
+                    writer.write(buffer, 0, len);
+                    bytesToRead -= len;
+                } else {
+                    writer.write(buffer, 0, (int) bytesToRead);
+                    bytesToRead = 0;
+                }
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+            }
+            if (len < buffer.length)
+                break;
+        }
+
+        return exception;
+
+    }
+
+
+    // ------------------------------------------------------ Inner Classes
+
+    protected static class Range {
+
+        public long start;
+        public long end;
+        public long length;
+
+        /**
+         * Validate range.
+         */
+        public boolean validate() {
+            if (end >= length)
+                end = length - 1;
+            return ( (start >= 0) && (end >= 0) && (start <= end)
+                     && (length > 0) );
+        }
+    }
+
+    /**
+     * Enumeration of sorting mechanisms for directory listings.
+     */
+    private enum SortedBy {
+        NAME,
+        LAST_MODIFIED,
+        SIZE
+    }
+
+    /**
+     * Comparator which sorts directory listings by their creation
+     * or lastModified date
+     *
+     * This comparator class cannot be used with TreeSet and TreeMap
+     * as it is not Serializable.
+     */
+    private static class LastModifiedComparator
+            implements Comparator<NameClassPair> {
+
+        private ProxyDirContext resources;
+        private String dirName;
+
+        public LastModifiedComparator(ProxyDirContext resources,
+                                      String dirName) {
+            this.resources = resources;
+            this.dirName = dirName;
+        }
+
+        public int compare(NameClassPair p1, NameClassPair p2) {
+
+            CacheEntry ce1 = resources.lookupCache(
+                dirName + p1.getName());
+            Date date1 = ce1.attributes.getCreationOrLastModifiedDate();
+            CacheEntry ce2 = resources.lookupCache(
+                dirName + p2.getName());
+            Date date2 = ce2.attributes.getCreationOrLastModifiedDate();
+            if (date1.before(date2)) {
+                return -1;
+            } else if (date1.after(date2)) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    /**
+     * Comparator which sorts directory listings by their file size
+     *
+     * This comparator class cannot be used with TreeSet and TreeMap
+     * as it is not Serializable.
+     */
+    private static class SizeComparator
+            implements Comparator<NameClassPair> {
+
+        private ProxyDirContext resources;
+        private String dirName;
+
+        public SizeComparator(ProxyDirContext resources,
+                              String dirName) {
+            this.resources = resources;
+            this.dirName = dirName;
+        }
+
+        public int compare(NameClassPair p1, NameClassPair p2) {
+
+            CacheEntry ce1 = resources.lookupCache(
+                dirName + p1.getName());
+            long size1 = ce1.attributes.getContentLength();
+            CacheEntry ce2 = resources.lookupCache(
+                dirName + p2.getName());
+            long size2 = ce2.attributes.getContentLength();
+            if (size1 < size2) {
+                return -1;
+            } else if (size1 > size2) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    /**
+     * This is secure in the sense that any attempt to use an external entity
+     * will trigger an exception.
+     */
+    private static class SecureEntityResolver implements EntityResolver2  {
+
+        public InputSource resolveEntity(String publicId, String systemId)
+                throws SAXException, IOException {
+            throw new SAXException(
+                    MessageFormat.format(rb.getString(LogFacade.BLOCK_EXTERNAL_ENTITY), publicId, systemId));
+        }
+
+        public InputSource getExternalSubset(String name, String baseURI)
+                throws SAXException, IOException {
+            throw new SAXException(
+                    MessageFormat.format(rb.getString(LogFacade.BLOCK_EXTERNAL_SUBSET), name, baseURI));
+        }
+
+        public InputSource resolveEntity(String name, String publicId,
+                String baseURI, String systemId) throws SAXException,
+                IOException {
+            throw new SAXException(
+                    MessageFormat.format(rb.getString(LogFacade.BLOCK_EXTERNAL_ENTITY2),
+                    name, publicId, baseURI, systemId));
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/InvokerHttpRequest.java b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/InvokerHttpRequest.java
new file mode 100644
index 0000000..5c34b5b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/InvokerHttpRequest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.servlets;
+
+
+import org.apache.catalina.util.StringManager;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.http.HttpServletRequest</code>
+ * utilized when <code>InvokerServlet</code> processes the initial request
+ * for an invoked servlet.  Subsequent requests will be mapped directly
+ * to the servlet, because a new servlet mapping will have been created.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:56 $
+ */
+
+class InvokerHttpRequest extends HttpServletRequestWrapper {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new wrapped request around the specified servlet request.
+     *
+     * @param request The servlet request being wrapped
+     */
+    public InvokerHttpRequest(HttpServletRequest request) {
+
+        super(request);
+        this.pathInfo = request.getPathInfo();
+        this.pathTranslated = request.getPathTranslated();
+        this.requestURI = request.getRequestURI();
+        this.servletPath = request.getServletPath();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Descriptive information about this implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.servlets.InvokerHttpRequest/1.0";
+
+
+    /**
+     * The path information for this request.
+     */
+    protected String pathInfo = null;
+
+
+    /**
+     * The translated path information for this request.
+     */
+    protected String pathTranslated = null;
+
+
+    /**
+     * The request URI for this request.
+     */
+    protected String requestURI = null;
+
+
+    /**
+     * The servlet path for this request.
+     */
+    protected String servletPath = null;
+
+
+    // --------------------------------------------- HttpServletRequest Methods
+
+
+    /**
+     * Override the <code>getPathInfo()</code> method of the wrapped request.
+     */
+    public String getPathInfo() {
+
+        return (this.pathInfo);
+
+    }
+
+
+    /**
+     * Override the <code>getPathTranslated()</code> method of the
+     * wrapped request.
+     */
+    public String getPathTranslated() {
+
+        return (this.pathTranslated);
+
+    }
+
+
+    /**
+     * Override the <code>getRequestURI()</code> method of the wrapped request.
+     */
+    public String getRequestURI() {
+
+        return (this.requestURI);
+
+    }
+
+
+    /**
+     * Override the <code>getServletPath()</code> method of the wrapped
+     * request.
+     */
+    public String getServletPath() {
+
+        return (this.servletPath);
+
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    /**
+     * Set the path information for this request.
+     *
+     * @param pathInfo The new path info
+     */
+    void setPathInfo(String pathInfo) {
+
+        this.pathInfo = pathInfo;
+
+    }
+
+
+    /**
+     * Set the translated path info for this request.
+     *
+     * @param pathTranslated The new translated path info
+     */
+    void setPathTranslated(String pathTranslated) {
+
+        this.pathTranslated = pathTranslated;
+
+    }
+
+
+    /**
+     * Set the request URI for this request.
+     *
+     * @param requestURI The new request URI
+     */
+    void setRequestURI(String requestURI) {
+
+        this.requestURI = requestURI;
+
+    }
+
+
+    /**
+     * Set the servlet path for this request.
+     *
+     * @param servletPath The new servlet path
+     */
+    void setServletPath(String servletPath) {
+
+        this.servletPath = servletPath;
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/InvokerServlet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/InvokerServlet.java
new file mode 100644
index 0000000..d1f9470
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/InvokerServlet.java
@@ -0,0 +1,526 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.servlets;
+
+
+import org.apache.catalina.ContainerServlet;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.util.StringManager;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+
+/**
+ * The default servlet-invoking servlet for most web applications,
+ * used to serve requests to servlets that have not been registered
+ * in the web application deployment descriptor.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:27:56 $
+ */
+
+public final class InvokerServlet
+    extends HttpServlet implements ContainerServlet {
+
+    public static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Context container associated with our web application.
+     */
+    private Context context = null;
+
+
+    /**
+     * The debugging detail level for this servlet.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The Wrapper container associated with this servlet.
+     */
+    private Wrapper wrapper = null;
+
+
+    // ----------------------------------------------- ContainerServlet Methods
+
+
+    /**
+     * Return the Wrapper with which we are associated.
+     */
+    public synchronized Wrapper getWrapper() {
+
+        return (this.wrapper);
+
+    }
+
+
+    /**
+     * Set the Wrapper with which we are associated.
+     *
+     * @param wrapper The new wrapper
+     */
+    public synchronized void setWrapper(Wrapper wrapper) {
+        this.wrapper = wrapper;
+        if (wrapper == null)
+            context = null;
+        else
+            context = (Context) wrapper.getParent();
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Finalize this servlet.
+     */
+    public void destroy() {
+
+        ;       // No actions necessary
+
+    }
+
+
+    /**
+     * Process a GET request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doGet(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException {
+
+        serveRequest(request, response);
+
+    }
+
+
+    /**
+     * Process a HEAD request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doHead(HttpServletRequest request,
+                       HttpServletResponse response)
+        throws IOException, ServletException {
+
+        serveRequest(request, response);
+
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public void doPost(HttpServletRequest request,
+                       HttpServletResponse response)
+        throws IOException, ServletException {
+
+        serveRequest(request, response);
+
+    }
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public synchronized void init() throws ServletException {
+
+        // Ensure that our ContainerServlet properties have been set
+        if ((wrapper == null) || (context == null))
+            throw new UnavailableException
+                (rb.getString(LogFacade.SET_WRAPPER_NOT_CALLED_EXCEPTION));
+
+        // Set our properties from the initialization parameters
+        String value = null;
+        try {
+            value = getServletConfig().getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+        if (debug >= 1)
+            log("init: Associated with Context '" + context.getPath() + "'");
+
+    }
+
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Serve the specified request, creating the corresponding response.
+     * After the first time a particular servlet class is requested, it will
+     * be served directly (like any registered servlet) because it will have
+     * been registered and mapped in our associated Context.
+     *
+     * <p>Synchronize to avoid race conditions when multiple requests
+     * try to initialize the same servlet at the same time
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    public synchronized void serveRequest(HttpServletRequest request,
+            HttpServletResponse response)
+        throws IOException, ServletException {
+
+        // Disallow calling this servlet via a named dispatcher
+        if (request.getAttribute(Globals.NAMED_DISPATCHER_ATTR) != null)
+            throw new ServletException
+                (rb.getString(LogFacade.CANNOT_CALL_INVOKER_SERVLET));
+
+        // Identify the input parameters and our "included" state
+        String inRequestURI = null;
+        String inServletPath = null;
+        String inPathInfo = null;
+        boolean included =
+            (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null);
+
+        if (included) {
+            inRequestURI = (String) request.getAttribute(
+                RequestDispatcher.INCLUDE_REQUEST_URI);
+            inServletPath = (String) request.getAttribute(
+                RequestDispatcher.INCLUDE_SERVLET_PATH);
+            inPathInfo = (String) request.getAttribute(
+                RequestDispatcher.INCLUDE_PATH_INFO);
+        } else {
+            inRequestURI = request.getRequestURI();
+            inServletPath = request.getServletPath();
+            inPathInfo = request.getPathInfo();
+        }
+        if (debug >= 1) {
+            log("included='" + included + "', requestURI='" +
+                inRequestURI + "'");
+            log("  servletPath='" + inServletPath + "', pathInfo='" +
+                inPathInfo + "'");
+        }
+
+        // Make sure a servlet name or class name was specified
+        if (inPathInfo == null) {
+            if (debug >= 1)
+                log("Invalid pathInfo 'null'");
+            String msg = MessageFormat.format(rb.getString(LogFacade.INVALID_PATH_EXCEPTION),
+                    inRequestURI);
+            if (included) {
+                throw new ServletException(msg);
+            }
+            else {
+                /* IASRI 4878272
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                   inRequestURI);
+                */
+                // BEGIN IASRI 4878272
+                log(msg);
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                // END IASRI 4878272
+                return;
+            }
+        }
+
+        // Identify the outgoing servlet name or class, and outgoing path info
+        String pathInfo = inPathInfo;
+        String servletClass = pathInfo.substring(1);
+        int slash = servletClass.indexOf('/');
+        //        if (debug >= 2)
+        //            log("  Calculating with servletClass='" + servletClass +
+        //                "', pathInfo='" + pathInfo + "', slash=" + slash);
+        if (slash >= 0) {
+            pathInfo = servletClass.substring(slash);
+            servletClass = servletClass.substring(0, slash);
+        } else {
+            pathInfo = "";
+        }
+
+        if (servletClass.startsWith("org.apache.catalina")) {
+            /* IASRI 4878272
+            response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                               inRequestURI);
+            */
+            // BEGIN IASRI 4878272
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            // END IASRI 4878272
+            return;
+        }
+
+        if (debug >= 1)
+            log("Processing servlet '" + servletClass +
+                "' with path info '" + pathInfo + "'");
+        String name = "org.apache.catalina.INVOKER." + servletClass;
+        String pattern = inServletPath + "/" + servletClass + "/*";
+        Wrapper wrapper = null;
+
+        // Are we referencing an existing servlet class or name?
+        wrapper = (Wrapper) context.findChild(servletClass);
+        if (wrapper == null)
+            wrapper = (Wrapper) context.findChild(name);
+        if (wrapper != null) {
+            String actualServletClass = wrapper.getServletClassName();
+            if ((actualServletClass != null)
+                && (actualServletClass.startsWith
+                    ("org.apache.catalina"))) {
+                /* IASRI 4878272
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                   inRequestURI);
+                */
+                // BEGIN IASRI 4878272
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                // END IASRI 4878272
+                return;
+            }
+            if (debug >= 1)
+                log("Using wrapper for servlet '" +
+                    wrapper.getName() + "' with mapping '" +
+                    pattern + "'");
+            context.addServletMapping(pattern, wrapper.getName());
+        }
+
+        // No, create a new wrapper for the specified servlet class
+        else {
+            if (debug >= 1)
+                log("Creating wrapper for '" + servletClass +
+                    "' with mapping '" + pattern + "'");
+            try {
+                wrapper = context.createWrapper();
+                wrapper.setName(name);
+                wrapper.setLoadOnStartup(1);
+                wrapper.setServletClassName(servletClass);
+                context.addChild(wrapper);
+                context.addServletMapping(pattern, name);
+            } catch (Throwable t) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_CREATE_SERVLET_WRAPPER_EXCEPTION),
+                                                  inRequestURI);
+                log(msg, t);
+                context.removeServletMapping(pattern);
+                context.removeChild(wrapper);
+                if (included)
+                    throw new ServletException
+                        (msg, t);
+                else {
+                    /* IASRI 4878272
+                    response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                       inRequestURI);
+                    */
+                    // BEGIN IASRI 4878272
+                    String invalidPathMsg = MessageFormat.format(rb.getString(LogFacade.INVALID_PATH_EXCEPTION),
+                                                                 inRequestURI);
+                    log(invalidPathMsg);
+                    response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                    // END IASRI 4878272
+                    return;
+                }
+            }
+        }
+
+        // Create a request wrapper to pass on to the invoked servlet
+        InvokerHttpRequest wrequest =
+            new InvokerHttpRequest(request);
+        wrequest.setRequestURI(inRequestURI);
+        StringBuilder sb = new StringBuilder(inServletPath);
+        sb.append("/");
+        sb.append(servletClass);
+        wrequest.setServletPath(sb.toString());
+        if ((pathInfo == null) || (pathInfo.length() < 1)) {
+            wrequest.setPathInfo(null);
+            wrequest.setPathTranslated(null);
+        } else {
+            wrequest.setPathInfo(pathInfo);
+            wrequest.setPathTranslated
+                (getServletContext().getRealPath(pathInfo));
+        }
+
+        // Allocate a servlet instance to perform this request
+        Servlet instance = null;
+
+        String cannotAllocateMsg = MessageFormat.format(rb.getString(LogFacade.CANNOT_ALLOCATE_SERVLET_INSTANCE_EXCEPTION),
+                inRequestURI);
+        String invalidPathMsg = MessageFormat.format(rb.getString(LogFacade.INVALID_PATH_EXCEPTION),
+                inRequestURI);
+        try {
+            //            if (debug >= 2)
+            //                log("  Allocating servlet instance");
+            instance = wrapper.allocate();
+        } catch (ServletException e) {
+
+            log(cannotAllocateMsg, e);
+            context.removeServletMapping(pattern);
+            context.removeChild(wrapper);
+            Throwable rootCause = e.getRootCause();
+            if (rootCause == null)
+                rootCause = e;
+            if (rootCause instanceof ClassNotFoundException) {
+                /* IASRI 4878272
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                   inRequestURI);
+                */
+                // BEGIN IASRI 4878272
+                log(invalidPathMsg);
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                // END IASRI 4878272
+                return;
+            } else if (rootCause instanceof IOException) {
+                throw (IOException) rootCause;
+            } else if (rootCause instanceof RuntimeException) {
+                throw (RuntimeException) rootCause;
+            } else if (rootCause instanceof ServletException) {
+                throw (ServletException) rootCause;
+            } else {
+                throw new ServletException(cannotAllocateMsg, rootCause);
+            }
+        } catch (Throwable e) {
+            log(cannotAllocateMsg, e);
+            context.removeServletMapping(pattern);
+            context.removeChild(wrapper);
+            throw new ServletException
+                (cannotAllocateMsg, e);
+        }
+
+        // After loading the wrapper, restore some of the fields when including
+        if (included) {
+            wrequest.setRequestURI(request.getRequestURI());
+            wrequest.setPathInfo(request.getPathInfo());
+            wrequest.setServletPath(request.getServletPath());
+        }
+
+        // Invoke the service() method of the allocated servlet
+        try {
+            String jspFile = wrapper.getJspFile();
+            if (jspFile != null)
+                request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
+            else
+                request.removeAttribute(Globals.JSP_FILE_ATTR);
+            request.setAttribute(Globals.INVOKED_ATTR,
+                                 request.getServletPath());
+            //            if (debug >= 2)
+            //                log("  Calling service() method, jspFile=" +
+            //                    jspFile);
+            instance.service(wrequest, response);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+        } catch (IOException e) {
+            //            if (debug >= 2)
+            //                log("  service() method IOException", e);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw e;
+        } catch (UnavailableException e) {
+            //            if (debug >= 2)
+            //                log("  service() method UnavailableException", e);
+            context.removeServletMapping(pattern);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw e;
+        } catch (ServletException e) {
+            //            if (debug >= 2)
+            //                log("  service() method ServletException", e);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw e;
+        } catch (RuntimeException e) {
+            //            if (debug >= 2)
+            //                log("  service() method RuntimeException", e);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw e;
+        } catch (Throwable e) {
+            //            if (debug >= 2)
+            //                log("  service() method Throwable", e);
+            request.removeAttribute(Globals.INVOKED_ATTR);
+            request.removeAttribute(Globals.JSP_FILE_ATTR);
+            try {
+                wrapper.deallocate(instance);
+            } catch (Throwable f) {
+                ;
+            }
+            throw new ServletException("Invoker service() exception", e);
+        }
+
+        // Deallocate the allocated servlet instance
+        String cannotDeallocateMsg = MessageFormat.format(rb.getString(LogFacade.CANNOT_DEALLOCATE_SERVLET_INSTANCE_EXCEPTION),
+                                          inRequestURI);
+        try {
+            //            if (debug >= 2)
+            //                log("  deallocate servlet instance");
+            wrapper.deallocate(instance);
+        } catch (ServletException e) {
+            log(cannotDeallocateMsg, e);
+            throw e;
+        } catch (Throwable e) {
+            log(cannotDeallocateMsg, e);
+            throw new ServletException
+                (cannotDeallocateMsg, e);
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/WebdavServlet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/WebdavServlet.java
new file mode 100644
index 0000000..c3d3d95
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -0,0 +1,3128 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.servlets;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.TimeZone;
+import java.util.Vector;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.util.DOMWriter;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.XMLWriter;
+import org.apache.naming.resources.CacheEntry;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+import org.glassfish.grizzly.http.util.FastHttpDateFormat;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+
+/**
+ * Servlet which adds support for WebDAV level 2. All the basic HTTP requests
+ * are handled by the DefaultServlet. The WebDAVServlet must not be used as the
+ * default servlet (ie mapped to '/') as it will not work in this configuration.
+ * To enable WebDAV for a context add the following to web.xml:<br/><code>
+ * &lt;servlet&gt;<br/>
+ *  &lt;servlet-name&gt;webdav&lt;/servlet-name&gt;<br/>
+ *  &lt;servlet-class&gt;org.apache.catalina.servlets.WebdavServlet&lt;/servlet-class&gt;<br/>
+ *    &lt;init-param&gt;<br/>
+ *      &lt;param-name&gt;debug&lt;/param-name&gt;<br/>
+ *      &lt;param-value&gt;0&lt;/param-value&gt;<br/>
+ *    &lt;/init-param&gt;<br/>
+ *    &lt;init-param&gt;<br/>
+ *      &lt;param-name&gt;listings&lt;/param-name&gt;<br/>
+ *      &lt;param-value&gt;true&lt;/param-value&gt;<br/>
+ *    &lt;/init-param&gt;<br/>
+ *  &lt;/servlet&gt;<br/>
+ *  &lt;servlet-mapping&gt;<br/>
+ *    &lt;servlet-name&gt;webdav&lt;/servlet-name&gt;<br/>
+ *    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br/>
+ *  &lt;/servlet-mapping&gt;
+ * </code>
+ * <p/>
+ * This will enable read only access. To enable read-write access add:<br/>
+ * <code>
+ *    &lt;init-param&gt;<br/>
+ *      &lt;param-name&gt;readonly&lt;/param-name&gt;<br/>
+ *      &lt;param-value&gt;false&lt;/param-value&gt;<br/>
+ *    &lt;/init-param&gt;<br/>
+ * </code>
+ * <p/>
+ * To make the content editable via a different URL, using the following
+ * mapping:<br/>
+ * <code>
+ *  &lt;servlet-mapping&gt;<br/>
+ *    &lt;servlet-name&gt;webdav&lt;/servlet-name&gt;<br/>
+ *    &lt;url-pattern&gt;/webdavedit/*&lt;/url-pattern&gt;<br/>
+ *  &lt;/servlet-mapping&gt;
+ * </code>
+ * <p/>
+ * Don't forget to secure access appropriately to the editing URLs. With this
+ * configuration the context will be accessible to normal users as before. Those
+ * users with the necessary access will be able to edit content available via
+ * http://host:port/context/content using
+ * http://host:port/context/webdavedit/content
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 600268 $ $Date: 2007-12-02 12:09:55 +0100 $
+ */
+
+public class WebdavServlet
+    extends DefaultServlet {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    private static final String METHOD_HEAD = "HEAD";
+    private static final String METHOD_PROPFIND = "PROPFIND";
+    private static final String METHOD_PROPPATCH = "PROPPATCH";
+    private static final String METHOD_MKCOL = "MKCOL";
+    private static final String METHOD_COPY = "COPY";
+    private static final String METHOD_MOVE = "MOVE";
+    private static final String METHOD_LOCK = "LOCK";
+    private static final String METHOD_UNLOCK = "UNLOCK";
+
+
+    /**
+     * Default depth is infite.
+     */
+    private static final int INFINITY = 3; // To limit tree browsing a bit
+
+
+    /**
+     * PROPFIND - Specify a property mask.
+     */
+    private static final int FIND_BY_PROPERTY = 0;
+
+
+    /**
+     * PROPFIND - Display all properties.
+     */
+    private static final int FIND_ALL_PROP = 1;
+
+
+    /**
+     * PROPFIND - Return property names.
+     */
+    private static final int FIND_PROPERTY_NAMES = 2;
+
+
+    /**
+     * Create a new lock.
+     */
+    private static final int LOCK_CREATION = 0;
+
+
+    /**
+     * Refresh lock.
+     */
+    private static final int LOCK_REFRESH = 1;
+
+
+    /**
+     * Default lock timeout value.
+     */
+    private static final int DEFAULT_TIMEOUT = 3600;
+
+
+    /**
+     * Maximum lock timeout.
+     */
+    private static final int MAX_TIMEOUT = 604800;
+
+
+    /**
+     * Default namespace.
+     */
+    protected static final String DEFAULT_NAMESPACE = "DAV:";
+
+
+    private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");
+
+
+    /**
+     * A ThreadLocal for simple date format for the creation date ISO representation (partial).
+     */
+    protected static final ThreadLocal<SimpleDateFormat> creationDateFormat =
+        new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+                f.setTimeZone(GMT_TIME_ZONE);
+                return f;
+            }
+        };
+
+
+     /**
+     * MD5 message digest provider.
+     */
+    protected static final MessageDigest sha256Helper;
+
+    static {
+        // Load the MD5 helper used to calculate signatures.
+        try {
+            sha256Helper = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException("No SHA-256");
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Repository of the locks put on single resources.
+     * <p>
+     * Key : path <br>
+     * Value : LockInfo
+     */
+    private Hashtable<String,LockInfo> resourceLocks =
+        new Hashtable<String,LockInfo>();
+
+
+    /**
+     * Repository of the lock-null resources.
+     * <p>
+     * Key : path of the collection containing the lock-null resource<br>
+     * Value : Vector of lock-null resource which are members of the
+     * collection. Each element of the Vector is the path associated with
+     * the lock-null resource.
+     */
+    private Hashtable<String,Vector<String>> lockNullResources =
+        new Hashtable<String,Vector<String>>();
+
+
+    /**
+     * Vector of the heritable locks.
+     * <p>
+     * Key : path <br>
+     * Value : LockInfo
+     */
+    private Vector<LockInfo> collectionLocks = new Vector<LockInfo>();
+
+
+    /**
+     * Secret information used to generate reasonably secure lock ids.
+     */
+    private String secret = "catalina";
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init()
+        throws ServletException {
+
+        super.init();
+
+        if (getServletConfig().getInitParameter("secret") != null)
+            secret = getServletConfig().getInitParameter("secret");
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return JAXP document builder instance.
+     */
+    protected DocumentBuilder getDocumentBuilder()
+        throws ServletException {
+        DocumentBuilder documentBuilder = null;
+        DocumentBuilderFactory documentBuilderFactory = null;
+        try {
+            documentBuilderFactory = DocumentBuilderFactory.newInstance();
+            documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            documentBuilderFactory.setValidating(true);
+            documentBuilderFactory.setNamespaceAware(true);
+            documentBuilderFactory.setExpandEntityReferences(false);
+            documentBuilder = documentBuilderFactory.newDocumentBuilder();
+            documentBuilder.setEntityResolver(
+                    new WebdavResolver(this.getServletContext()));
+        } catch(ParserConfigurationException e) {
+            throw new ServletException
+                (rb.getString(LogFacade.JAXP_INTI_FAILED));
+        }
+        return documentBuilder;
+    }
+
+
+    /**
+     * Handles the special WebDAV methods.
+     */
+    protected void service(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        String method = req.getMethod();
+
+        if (debug > 0) {
+            String path = getRelativePath(req);
+            log("[" + method + "] " + path);
+        }
+
+        if (method.equals(METHOD_PROPFIND)) {
+            doPropfind(req, resp);
+        } else if (method.equals(METHOD_PROPPATCH)) {
+            doProppatch(req, resp);
+        } else if (method.equals(METHOD_MKCOL)) {
+            doMkcol(req, resp);
+        } else if (method.equals(METHOD_COPY)) {
+            doCopy(req, resp);
+        } else if (method.equals(METHOD_MOVE)) {
+            doMove(req, resp);
+        } else if (method.equals(METHOD_LOCK)) {
+            doLock(req, resp);
+        } else if (method.equals(METHOD_UNLOCK)) {
+            doUnlock(req, resp);
+        } else {
+            // DefaultServlet processing
+            super.service(req, resp);
+        }
+
+    }
+
+
+    /**
+     * Check if the conditions specified in the optional If headers are
+     * satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes The resource information
+     * @return boolean true if the resource meets all the specified conditions,
+     * and false if any of the conditions is not satisfied, in which case
+     * request processing is stopped
+     */
+    protected boolean checkIfHeaders(HttpServletRequest request,
+                                     HttpServletResponse response,
+                                     ResourceAttributes resourceAttributes)
+        throws IOException {
+
+        if (!super.checkIfHeaders(request, response, resourceAttributes))
+            return false;
+
+        // TODO : Checking the WebDAV If header
+        return true;
+
+    }
+
+
+    /**
+     * Override the DefaultServlet implementation and only use the PathInfo. If
+     * the ServletPath is non-null, it will be because the WebDAV servlet has
+     * been mapped to a url other than /* to configure editing at different url
+     * than normal viewing.
+     *
+     * @param request The servlet request we are processing
+     */
+    protected String getRelativePath(HttpServletRequest request) {
+        // Are we being processed by a RequestDispatcher.include()?
+        if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
+            String result = (String) request.getAttribute(
+                RequestDispatcher.INCLUDE_PATH_INFO);
+            if (result == null || "".equals(result))
+                result = "/";
+            return result;
+        }
+
+        // No, extract the desired path directly from the request
+        String result = request.getPathInfo();
+        if (result == null || "".equals(result)) {
+            result = "/";
+        }
+        return result;
+
+    }
+
+
+    /**
+     * OPTIONS Method.
+     *
+     * @param req The request
+     * @param resp The response
+     * @throws ServletException If an error occurs
+     * @throws IOException If an IO error occurs
+     */
+    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        resp.addHeader("DAV", "1,2");
+
+        StringBuilder methodsAllowed = determineMethodsAllowed(resources,
+                                                              req);
+
+        resp.addHeader("Allow", methodsAllowed.toString());
+        resp.addHeader("MS-Author-Via", "DAV");
+
+    }
+
+
+    /**
+     * PROPFIND Method.
+     */
+    protected void doPropfind(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (!listings) {
+            // Get allowed methods
+            StringBuilder methodsAllowed = determineMethodsAllowed(resources,
+                                                                  req);
+
+            resp.addHeader("Allow", methodsAllowed.toString());
+            resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+        if (path.endsWith("/"))
+            path = path.substring(0, path.length() - 1);
+
+        if (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
+            path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        // Properties which are to be displayed.
+        Vector<String> properties = null;
+        // Propfind depth
+        int depth = INFINITY;
+        // Propfind type
+        int type = FIND_ALL_PROP;
+
+        String depthStr = req.getHeader("Depth");
+
+        if (depthStr == null) {
+            depth = INFINITY;
+        } else {
+            if ("0".equals(depthStr)) {
+                depth = 0;
+            } else if ("1".equals(depthStr)) {
+                depth = 1;
+            } else if ("infinity".equals(depthStr)) {
+                depth = INFINITY;
+            }
+        }
+
+        Node propNode = null;
+        
+        if (req.getInputStream().available() > 0) {
+            DocumentBuilder documentBuilder = getDocumentBuilder();
+    
+            try {
+                Document document = documentBuilder.parse
+                    (new InputSource(req.getInputStream()));
+    
+                // Get the root element of the document
+                Element rootElement = document.getDocumentElement();
+                NodeList childList = rootElement.getChildNodes();
+    
+                for (int i=0; i < childList.getLength(); i++) {
+                    Node currentNode = childList.item(i);
+                    switch (currentNode.getNodeType()) {
+                    case Node.TEXT_NODE:
+                        break;
+                    case Node.ELEMENT_NODE:
+                        if (currentNode.getNodeName().endsWith("prop")) {
+                            type = FIND_BY_PROPERTY;
+                            propNode = currentNode;
+                        }
+                        if (currentNode.getNodeName().endsWith("propname")) {
+                            type = FIND_PROPERTY_NAMES;
+                        }
+                        if (currentNode.getNodeName().endsWith("allprop")) {
+                            type = FIND_ALL_PROP;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                }
+            } catch (SAXException e) {
+                // Something went wrong - use the defaults.
+            } catch (IOException e) {
+                // Something went wrong - use the defaults.
+            }
+        }
+
+        if (type == FIND_BY_PROPERTY) {
+            properties = new Vector<String>();
+            NodeList childList = propNode.getChildNodes();
+
+            for (int i=0; i < childList.getLength(); i++) {
+                Node currentNode = childList.item(i);
+                switch (currentNode.getNodeType()) {
+                case Node.TEXT_NODE:
+                    break;
+                case Node.ELEMENT_NODE:
+                    String nodeName = currentNode.getNodeName();
+                    String propertyName = null;
+                    if (nodeName.indexOf(':') != -1) {
+                        propertyName = nodeName.substring
+                            (nodeName.indexOf(':') + 1);
+                    } else {
+                        propertyName = nodeName;
+                    }
+                    // href is a live property which is handled differently
+                    properties.addElement(propertyName);
+                    break;
+                default:
+                    break;
+                }
+            }
+
+        }
+
+        boolean exists = true;
+        Object object = null;
+        try {
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+            int slash = path.lastIndexOf('/');
+            if (slash != -1) {
+                String parentPath = path.substring(0, slash);
+                Vector<String> currentLockNullResources =
+                    lockNullResources.get(parentPath);
+                if (currentLockNullResources != null) {
+                    Enumeration<String> lockNullResourcesList =
+                        currentLockNullResources.elements();
+                    while (lockNullResourcesList.hasMoreElements()) {
+                        String lockNullPath =
+                            lockNullResourcesList.nextElement();
+                        if (lockNullPath.equals(path)) {
+                            resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
+                            resp.setContentType("text/xml; charset=UTF-8");
+                            // Create multistatus object
+                            XMLWriter generatedXML =
+                                new XMLWriter(resp.getWriter());
+                            generatedXML.writeXMLHeader();
+                            generatedXML.writeElement
+                                (null, "multistatus"
+                                 + generateNamespaceDeclarations(),
+                                 XMLWriter.OPENING);
+                            parseLockNullProperties
+                                (req, generatedXML, lockNullPath, type,
+                                 properties);
+                            generatedXML.writeElement(null, "multistatus",
+                                                      XMLWriter.CLOSING);
+                            generatedXML.sendData();
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!exists) {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+            return;
+        }
+
+        resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
+
+        resp.setContentType("text/xml; charset=UTF-8");
+
+        // Create multistatus object
+        XMLWriter generatedXML = new XMLWriter(resp.getWriter());
+        generatedXML.writeXMLHeader();
+
+        generatedXML.writeElement(null, "multistatus"
+                                  + generateNamespaceDeclarations(),
+                                  XMLWriter.OPENING);
+
+        if (depth == 0) {
+            parseProperties(req, generatedXML, path, type,
+                            properties);
+        } else {
+            // The stack always contains the object of the current level
+            Stack<String> stack = new Stack<String>();
+            stack.push(path);
+
+            // Stack of the objects one level below
+            Stack<String> stackBelow = new Stack<String>();
+
+            while (!stack.isEmpty() && depth >= 0) {
+
+                String currentPath = stack.pop();
+                parseProperties(req, generatedXML, currentPath,
+                                type, properties);
+
+                try {
+                    object = resources.lookup(currentPath);
+                } catch (NamingException e) {
+                    continue;
+                }
+
+                if (object instanceof DirContext && depth > 0) {
+
+                    try {
+                        NamingEnumeration<NameClassPair> enumeration =
+                            resources.list(currentPath);
+                        while (enumeration.hasMoreElements()) {
+                            NameClassPair ncPair = enumeration.nextElement();
+                            String newPath = currentPath;
+                            if (!newPath.endsWith("/"))
+                                newPath += "/";
+                            newPath += ncPair.getName();
+                            stackBelow.push(newPath);
+                        }
+                    } catch (NamingException e) {
+                        resp.sendError
+                            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                             path);
+                        return;
+                    }
+
+                    // Displaying the lock-null resources present in that
+                    // collection
+                    String lockPath = currentPath;
+                    if (lockPath.endsWith("/"))
+                        lockPath =
+                            lockPath.substring(0, lockPath.length() - 1);
+                    Vector<String> currentLockNullResources =
+                        lockNullResources.get(lockPath);
+                    if (currentLockNullResources != null) {
+                        Enumeration<String> lockNullResourcesList =
+                            currentLockNullResources.elements();
+                        while (lockNullResourcesList.hasMoreElements()) {
+                            String lockNullPath =
+                                lockNullResourcesList.nextElement();
+                            parseLockNullProperties
+                                (req, generatedXML, lockNullPath, type,
+                                 properties);
+                        }
+                    }
+
+                }
+
+                if (stack.isEmpty()) {
+                    depth--;
+                    stack = stackBelow;
+                    stackBelow = new Stack<String>();
+                }
+
+                generatedXML.sendData();
+
+            }
+        }
+
+        generatedXML.writeElement(null, "multistatus",
+                                  XMLWriter.CLOSING);
+
+        generatedXML.sendData();
+
+    }
+
+
+    /**
+     * PROPPATCH Method.
+     */
+    protected void doProppatch(HttpServletRequest req,
+                               HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
+
+    }
+
+
+    /**
+     * MKCOL Method.
+     */
+    protected void doMkcol(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        if (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
+            path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        boolean exists = true;
+        try {
+            resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        // Can't create a collection if a resource already exists at the given
+        // path
+        if (exists) {
+            // Get allowed methods
+            StringBuilder methodsAllowed = determineMethodsAllowed(resources,
+                                                                  req);
+
+            resp.addHeader("Allow", methodsAllowed.toString());
+
+            resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
+            return;
+        }
+
+        if (req.getInputStream().available() > 0) {
+            DocumentBuilder documentBuilder = getDocumentBuilder();
+            try {
+                documentBuilder.parse
+                    (new InputSource(req.getInputStream()));
+                // TODO : Process this request body
+                resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
+                return;
+
+            } catch(SAXException saxe) {
+                // Parse error - assume invalid content
+                resp.sendError(WebdavStatus.SC_BAD_REQUEST);
+                return;
+            }
+        }
+
+        boolean result = true;
+        try {
+            resources.createSubcontext(path);
+        } catch (NamingException e) {
+            result = false;
+        }
+
+        if (!result) {
+            resp.sendError(WebdavStatus.SC_CONFLICT,
+                           WebdavStatus.getStatusText
+                           (WebdavStatus.SC_CONFLICT));
+        } else {
+            resp.setStatus(WebdavStatus.SC_CREATED);
+            // Removing any lock-null resource which would be present
+            lockNullResources.remove(path);
+        }
+
+    }
+
+
+    /**
+     * DELETE Method.
+     */
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        deleteResource(req, resp);
+
+    }
+
+
+    /**
+     * Process a POST request for the specified resource.
+     *
+     * @param req The servlet request we are processing
+     * @param resp The servlet response we are creating
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet-specified error occurs
+     */
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        super.doPut(req, resp);
+
+        String path = getRelativePath(req);
+
+        // Removing any lock-null resource which would be present
+        lockNullResources.remove(path);
+
+    }
+
+    /**
+     * COPY Method.
+     */
+    protected void doCopy(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        copyResource(req, resp);
+
+    }
+
+
+    /**
+     * MOVE Method.
+     */
+    protected void doMove(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        if (copyResource(req, resp)) {
+            deleteResource(path, req, resp, false);
+        }
+
+    }
+
+
+    /**
+     * LOCK Method.
+     */
+    protected void doLock(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        LockInfo lock = new LockInfo();
+
+        // Parsing lock request
+
+        // Parsing depth header
+
+        String depthStr = req.getHeader("Depth");
+
+        if (depthStr == null) {
+            lock.depth = INFINITY;
+        } else {
+            if ("0".equals(depthStr)) {
+                lock.depth = 0;
+            } else {
+                lock.depth = INFINITY;
+            }
+        }
+
+        // Parsing timeout header
+
+        int lockDuration = DEFAULT_TIMEOUT;
+        String lockDurationStr = req.getHeader("Timeout");
+        if (lockDurationStr == null) {
+            lockDuration = DEFAULT_TIMEOUT;
+        } else {
+            int commaPos = lockDurationStr.indexOf(",");
+            // If multiple timeouts, just use the first
+            if (commaPos != -1) {
+                lockDurationStr = lockDurationStr.substring(0,commaPos);
+            }
+            if (lockDurationStr.startsWith("Second-")) {
+                lockDuration =
+                    Integer.parseInt(lockDurationStr.substring(7));
+            } else {
+                if ("infinity".equalsIgnoreCase(lockDurationStr)) {
+                    lockDuration = MAX_TIMEOUT;
+                } else {
+                    try {
+                        lockDuration =
+                            Integer.parseInt(lockDurationStr);
+                    } catch (NumberFormatException e) {
+                        lockDuration = MAX_TIMEOUT;
+                    }
+                }
+            }
+            if (lockDuration == 0) {
+                lockDuration = DEFAULT_TIMEOUT;
+            }
+            if (lockDuration > MAX_TIMEOUT) {
+                lockDuration = MAX_TIMEOUT;
+            }
+        }
+        lock.expiresAt = System.currentTimeMillis() + lockDuration * 1000L;
+
+        int lockRequestType = LOCK_CREATION;
+
+        Node lockInfoNode = null;
+
+        DocumentBuilder documentBuilder = getDocumentBuilder();
+
+        try {
+            Document document = documentBuilder.parse(new InputSource
+                (req.getInputStream()));
+
+            // Get the root element of the document
+            Element rootElement = document.getDocumentElement();
+            lockInfoNode = rootElement;
+        } catch (IOException e) {
+            lockRequestType = LOCK_REFRESH;
+        } catch (SAXException e) {
+            lockRequestType = LOCK_REFRESH;
+        }
+
+        if (lockInfoNode != null) {
+
+            // Reading lock information
+
+            NodeList childList = lockInfoNode.getChildNodes();
+            StringWriter strWriter = null;
+            DOMWriter domWriter = null;
+
+            Node lockScopeNode = null;
+            Node lockTypeNode = null;
+            Node lockOwnerNode = null;
+
+            for (int i=0; i < childList.getLength(); i++) {
+                Node currentNode = childList.item(i);
+                switch (currentNode.getNodeType()) {
+                case Node.TEXT_NODE:
+                    break;
+                case Node.ELEMENT_NODE:
+                    String nodeName = currentNode.getNodeName();
+                    if (nodeName.endsWith("lockscope")) {
+                        lockScopeNode = currentNode;
+                    }
+                    if (nodeName.endsWith("locktype")) {
+                        lockTypeNode = currentNode;
+                    }
+                    if (nodeName.endsWith("owner")) {
+                        lockOwnerNode = currentNode;
+                    }
+                    break;
+                default:
+                    break;
+                }
+            }
+
+            if (lockScopeNode != null) {
+
+                childList = lockScopeNode.getChildNodes();
+                for (int i=0; i < childList.getLength(); i++) {
+                    Node currentNode = childList.item(i);
+                    switch (currentNode.getNodeType()) {
+                    case Node.TEXT_NODE:
+                        break;
+                    case Node.ELEMENT_NODE:
+                        String tempScope = currentNode.getNodeName();
+                        if (tempScope.indexOf(':') != -1) {
+                            lock.scope = tempScope.substring
+                                (tempScope.indexOf(':') + 1);
+                        } else {
+                            lock.scope = tempScope;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                }
+
+                if (lock.scope == null) {
+                    // Bad request
+                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+                }
+
+            } else {
+                // Bad request
+                resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+            }
+
+            if (lockTypeNode != null) {
+
+                childList = lockTypeNode.getChildNodes();
+                for (int i=0; i < childList.getLength(); i++) {
+                    Node currentNode = childList.item(i);
+                    switch (currentNode.getNodeType()) {
+                    case Node.TEXT_NODE:
+                        break;
+                    case Node.ELEMENT_NODE:
+                        String tempType = currentNode.getNodeName();
+                        if (tempType.indexOf(':') != -1) {
+                            lock.type =
+                                tempType.substring(tempType.indexOf(':') + 1);
+                        } else {
+                            lock.type = tempType;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                }
+
+                if (lock.type == null) {
+                    // Bad request
+                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+                }
+
+            } else {
+                // Bad request
+                resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+            }
+
+            if (lockOwnerNode != null) {
+
+                childList = lockOwnerNode.getChildNodes();
+                for (int i=0; i < childList.getLength(); i++) {
+                    Node currentNode = childList.item(i);
+                    switch (currentNode.getNodeType()) {
+                    case Node.TEXT_NODE:
+                        lock.owner += currentNode.getNodeValue();
+                        break;
+                    case Node.ELEMENT_NODE:
+                        strWriter = new StringWriter();
+                        domWriter = new DOMWriter(strWriter, true);
+                        domWriter.setQualifiedNames(false);
+                        domWriter.print(currentNode);
+                        lock.owner += strWriter.toString();
+                        break;
+                    default:
+                        break;
+                    }
+                }
+
+                if (lock.owner == null) {
+                    // Bad request
+                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
+                }
+
+            } else {
+                lock.owner = "";
+            }
+
+        }
+
+        String path = getRelativePath(req);
+
+        lock.path = path;
+
+        boolean exists = true;
+        Object object = null;
+        try {
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        Enumeration<LockInfo> locksList = null;
+
+        if (lockRequestType == LOCK_CREATION) {
+
+            // Generating lock id
+            String lockTokenStr = req.getServletPath() + "-" + lock.type + "-"
+                + lock.scope + "-" + req.getUserPrincipal() + "-"
+                + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-"
+                + lock.expiresAt + "-" + System.currentTimeMillis() + "-"
+                + secret;
+            
+            byte[] digestBytes = null;
+            synchronized(sha256Helper) {
+                digestBytes = sha256Helper.digest(lockTokenStr.getBytes(
+                            Charset.defaultCharset()));
+            }
+            String lockToken = new String(digestBytes);
+
+            if ( exists && object instanceof DirContext &&
+                lock.depth == INFINITY) {
+
+                // Locking a collection (and all its member resources)
+
+                // Checking if a child resource of this collection is
+                // already locked
+                Vector<String> lockPaths = new Vector<String>();
+                locksList = collectionLocks.elements();
+                while (locksList.hasMoreElements()) {
+                    LockInfo currentLock = locksList.nextElement();
+                    if (currentLock.hasExpired()) {
+                        resourceLocks.remove(currentLock.path);
+                        continue;
+                    }
+                    if ( currentLock.path.startsWith(lock.path) &&
+                         (currentLock.isExclusive() ||
+                             lock.isExclusive()) ) {
+                        // A child collection of this collection is locked
+                        lockPaths.addElement(currentLock.path);
+                    }
+                }
+                locksList = resourceLocks.elements();
+                while (locksList.hasMoreElements()) {
+                    LockInfo currentLock = locksList.nextElement();
+                    if (currentLock.hasExpired()) {
+                        resourceLocks.remove(currentLock.path);
+                        continue;
+                    }
+                    if ( currentLock.path.startsWith(lock.path) &&
+                         (currentLock.isExclusive() ||
+                             lock.isExclusive()) ) {
+                        // A child resource of this collection is locked
+                        lockPaths.addElement(currentLock.path);
+                    }
+                }
+
+                if (!lockPaths.isEmpty()) {
+
+                    // One of the child paths was locked
+                    // We generate a multistatus error report
+
+                    Enumeration<String> lockPathsList = lockPaths.elements();
+
+                    resp.setStatus(WebdavStatus.SC_CONFLICT);
+
+                    XMLWriter generatedXML = new XMLWriter();
+                    generatedXML.writeXMLHeader();
+
+                    generatedXML.writeElement
+                        (null, "multistatus" + generateNamespaceDeclarations(),
+                         XMLWriter.OPENING);
+
+                    while (lockPathsList.hasMoreElements()) {
+                        generatedXML.writeElement(null, "response",
+                                                  XMLWriter.OPENING);
+                        generatedXML.writeElement(null, "href",
+                                                  XMLWriter.OPENING);
+                        generatedXML
+                            .writeText(lockPathsList.nextElement());
+                        generatedXML.writeElement(null, "href",
+                                                  XMLWriter.CLOSING);
+                        generatedXML.writeElement(null, "status",
+                                                  XMLWriter.OPENING);
+                        generatedXML
+                            .writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED
+                                       + " " + WebdavStatus
+                                       .getStatusText(WebdavStatus.SC_LOCKED));
+                        generatedXML.writeElement(null, "status",
+                                                  XMLWriter.CLOSING);
+
+                        generatedXML.writeElement(null, "response",
+                                                  XMLWriter.CLOSING);
+                    }
+
+                    generatedXML.writeElement(null, "multistatus",
+                                          XMLWriter.CLOSING);
+
+                    Writer writer = resp.getWriter();
+                    writer.write(generatedXML.toString());
+                    writer.close();
+
+                    return;
+
+                }
+
+                boolean addLock = true;
+
+                // Checking if there is already a shared lock on this path
+                locksList = collectionLocks.elements();
+                while (locksList.hasMoreElements()) {
+
+                    LockInfo currentLock = locksList.nextElement();
+                    if (currentLock.path.equals(lock.path)) {
+
+                        if (currentLock.isExclusive()) {
+                            resp.sendError(WebdavStatus.SC_LOCKED);
+                            return;
+                        } else {
+                            if (lock.isExclusive()) {
+                                resp.sendError(WebdavStatus.SC_LOCKED);
+                                return;
+                            }
+                        }
+
+                        currentLock.tokens.addElement(lockToken);
+                        lock = currentLock;
+                        addLock = false;
+
+                    }
+
+                }
+
+                if (addLock) {
+                    lock.tokens.addElement(lockToken);
+                    collectionLocks.addElement(lock);
+                }
+
+            } else {
+
+                // Locking a single resource
+
+                // Retrieving an already existing lock on that resource
+                LockInfo presentLock = resourceLocks.get(lock.path);
+                if (presentLock != null) {
+
+                    if (presentLock.isExclusive() || lock.isExclusive()) {
+                        // If either lock is exclusive, the lock can't be
+                        // granted
+                        resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
+                        return;
+                    } else {
+                        presentLock.tokens.addElement(lockToken);
+                        lock = presentLock;
+                    }
+
+                } else {
+
+                    lock.tokens.addElement(lockToken);
+                    resourceLocks.put(lock.path, lock);
+
+                    // Checking if a resource exists at this path
+                    exists = true;
+                    try {
+                        object = resources.lookup(path);
+                    } catch (NamingException e) {
+                        exists = false;
+                    }
+                    if (!exists) {
+
+                        // "Creating" a lock-null resource
+                        int slash = lock.path.lastIndexOf('/');
+                        String parentPath = lock.path.substring(0, slash);
+
+                        Vector<String> lockNulls =
+                            lockNullResources.get(parentPath);
+                        if (lockNulls == null) {
+                            lockNulls = new Vector<String>();
+                            lockNullResources.put(parentPath, lockNulls);
+                        }
+
+                        lockNulls.addElement(lock.path);
+
+                    }
+                    // Add the Lock-Token header as by RFC 2518 8.10.1
+                    // - only do this for newly created locks
+                    resp.addHeader("Lock-Token", "<opaquelocktoken:"
+                                   + lockToken + ">");
+                }
+
+            }
+
+        }
+
+        if (lockRequestType == LOCK_REFRESH) {
+
+            String ifHeader = req.getHeader("If");
+            if (ifHeader == null)
+                ifHeader = "";
+
+            // Checking resource locks
+
+            LockInfo toRenew = resourceLocks.get(path);
+            Enumeration<String> tokenList = null;
+            if (lock != null) {
+
+                // At least one of the tokens of the locks must have been given
+
+                tokenList = toRenew.tokens.elements();
+                while (tokenList.hasMoreElements()) {
+                    String token = tokenList.nextElement();
+                    if (ifHeader.indexOf(token) != -1) {
+                        toRenew.expiresAt = lock.expiresAt;
+                        lock = toRenew;
+                    }
+                }
+
+            }
+
+            // Checking inheritable collection locks
+
+            Enumeration<LockInfo> collectionLocksList =
+                collectionLocks.elements();
+            while (collectionLocksList.hasMoreElements()) {
+                toRenew = collectionLocksList.nextElement();
+                if (path.equals(toRenew.path)) {
+
+                    tokenList = toRenew.tokens.elements();
+                    while (tokenList.hasMoreElements()) {
+                        String token = tokenList.nextElement();
+                        if (ifHeader.indexOf(token) != -1) {
+                            toRenew.expiresAt = lock.expiresAt;
+                            lock = toRenew;
+                        }
+                    }
+
+                }
+            }
+
+        }
+
+        // Set the status, then generate the XML response containing
+        // the lock information
+        XMLWriter generatedXML = new XMLWriter();
+        generatedXML.writeXMLHeader();
+        generatedXML.writeElement(null, "prop"
+                                  + generateNamespaceDeclarations(),
+                                  XMLWriter.OPENING);
+
+        generatedXML.writeElement(null, "lockdiscovery",
+                                  XMLWriter.OPENING);
+
+        lock.toXML(generatedXML);
+
+        generatedXML.writeElement(null, "lockdiscovery",
+                                  XMLWriter.CLOSING);
+
+        generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+
+        resp.setStatus(WebdavStatus.SC_OK);
+        resp.setContentType("text/xml; charset=UTF-8");
+        Writer writer = resp.getWriter();
+        writer.write(generatedXML.toString());
+        writer.close();
+
+    }
+
+
+    /**
+     * UNLOCK Method.
+     */
+    protected void doUnlock(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        // Checking resource locks
+
+        LockInfo lock = resourceLocks.get(path);
+        Enumeration<String> tokenList = null;
+        if (lock != null) {
+
+            // At least one of the tokens of the locks must have been given
+
+            tokenList = lock.tokens.elements();
+            while (tokenList.hasMoreElements()) {
+                String token = tokenList.nextElement();
+                if (lockTokenHeader.indexOf(token) != -1) {
+                    lock.tokens.removeElement(token);
+                }
+            }
+
+            if (lock.tokens.isEmpty()) {
+                resourceLocks.remove(path);
+                // Removing any lock-null resource which would be present
+                lockNullResources.remove(path);
+            }
+
+        }
+
+        // Checking inheritable collection locks
+
+        Enumeration<LockInfo> collectionLocksList = collectionLocks.elements();
+        while (collectionLocksList.hasMoreElements()) {
+            lock = collectionLocksList.nextElement();
+            if (path.equals(lock.path)) {
+
+                tokenList = lock.tokens.elements();
+                while (tokenList.hasMoreElements()) {
+                    String token = tokenList.nextElement();
+                    if (lockTokenHeader.indexOf(token) != -1) {
+                        lock.tokens.removeElement(token);
+                        break;
+                    }
+                }
+
+                if (lock.tokens.isEmpty()) {
+                    collectionLocks.removeElement(lock);
+                    // Removing any lock-null resource which would be present
+                    lockNullResources.remove(path);
+                }
+
+            }
+        }
+
+        resp.setStatus(WebdavStatus.SC_NO_CONTENT);
+
+    }
+
+    // -------------------------------------------------------- Private Methods
+
+    /**
+     * Generate the namespace declarations.
+     */
+    private String generateNamespaceDeclarations() {
+        return " xmlns=\"" + DEFAULT_NAMESPACE + "\"";
+    }
+
+
+    /**
+     * Check to see if a resource is currently write locked. The method
+     * will look at the "If" header to make sure the client
+     * has give the appropriate lock tokens.
+     *
+     * @param req Servlet request
+     * @return boolean true if the resource is locked (and no appropriate
+     * lock token has been found for at least one of the non-shared locks which
+     * are present on the resource).
+     */
+    private boolean isLocked(HttpServletRequest req) {
+
+        String path = getRelativePath(req);
+
+        String ifHeader = req.getHeader("If");
+        if (ifHeader == null)
+            ifHeader = "";
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        return isLocked(path, ifHeader + lockTokenHeader);
+
+    }
+
+
+    /**
+     * Check to see if a resource is currently write locked.
+     *
+     * @param path Path of the resource
+     * @param ifHeader "If" HTTP header which was included in the request
+     * @return boolean true if the resource is locked (and no appropriate
+     * lock token has been found for at least one of the non-shared locks which
+     * are present on the resource).
+     */
+    private boolean isLocked(String path, String ifHeader) {
+
+        // Checking resource locks
+
+        LockInfo lock = resourceLocks.get(path);
+        Enumeration<String> tokenList = null;
+        if (lock != null && lock.hasExpired()) {
+            resourceLocks.remove(path);
+        } else if (lock != null) {
+
+            // At least one of the tokens of the locks must have been given
+
+            tokenList = lock.tokens.elements();
+            boolean tokenMatch = false;
+            while (tokenList.hasMoreElements()) {
+                String token = tokenList.nextElement();
+                if (ifHeader.indexOf(token) != -1)
+                    tokenMatch = true;
+            }
+            if (!tokenMatch)
+                return true;
+
+        }
+
+        // Checking inheritable collection locks
+
+        Enumeration<LockInfo> collectionLocksList = collectionLocks.elements();
+        while (collectionLocksList.hasMoreElements()) {
+            lock = collectionLocksList.nextElement();
+            if (lock.hasExpired()) {
+                collectionLocks.removeElement(lock);
+            } else if (path.startsWith(lock.path)) {
+
+                tokenList = lock.tokens.elements();
+                boolean tokenMatch = false;
+                while (tokenList.hasMoreElements()) {
+                    String token = tokenList.nextElement();
+                    if (ifHeader.indexOf(token) != -1)
+                        tokenMatch = true;
+                }
+                if (!tokenMatch)
+                    return true;
+
+            }
+        }
+
+        return false;
+
+    }
+
+
+    /**
+     * Copy a resource.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @return boolean true if the copy is successful
+     */
+    private boolean copyResource(HttpServletRequest req,
+                                 HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        // Parsing destination header
+
+        String destinationPath = req.getHeader("Destination");
+
+        if (destinationPath == null) {
+            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
+            return false;
+        }
+
+        // Remove url encoding from destination
+        destinationPath = RequestUtil.urlDecode(destinationPath, "UTF8");
+
+        int protocolIndex = destinationPath.indexOf("://");
+        if (protocolIndex >= 0) {
+            // if the Destination URL contains the protocol, we can safely
+            // trim everything upto the first "/" character after "://"
+            int firstSeparator =
+                destinationPath.indexOf("/", protocolIndex + 4);
+            if (firstSeparator < 0) {
+                destinationPath = "/";
+            } else {
+                destinationPath = destinationPath.substring(firstSeparator);
+            }
+        } else {
+            String hostName = req.getServerName();
+            if (hostName != null && destinationPath.startsWith(hostName)) {
+                destinationPath = destinationPath.substring(hostName.length());
+            }
+
+            int portIndex = destinationPath.indexOf(":");
+            if (portIndex >= 0) {
+                destinationPath = destinationPath.substring(portIndex);
+            }
+
+            if (destinationPath.startsWith(":")) {
+                int firstSeparator = destinationPath.indexOf("/");
+                if (firstSeparator < 0) {
+                    destinationPath = "/";
+                } else {
+                    destinationPath =
+                        destinationPath.substring(firstSeparator);
+                }
+            }
+        }
+
+        // Normalise destination path (remove '.' and '..')
+        destinationPath = RequestUtil.normalize(destinationPath);
+
+        String contextPath = req.getContextPath();
+        if (contextPath != null &&
+            destinationPath.startsWith(contextPath)) {
+            destinationPath = destinationPath.substring(contextPath.length());
+        }
+
+        String pathInfo = req.getPathInfo();
+        if (pathInfo != null) {
+            String servletPath = req.getServletPath();
+            if (servletPath != null &&
+                destinationPath.startsWith(servletPath)) {
+                destinationPath = destinationPath
+                    .substring(servletPath.length());
+            }
+        }
+
+        if (debug > 0)
+            log("Dest path :" + destinationPath);
+
+        if (destinationPath.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
+            destinationPath.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        String path = getRelativePath(req);
+
+        if (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
+            path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        if (destinationPath.equals(path)) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        // Parsing overwrite header
+
+        boolean overwrite = true;
+        String overwriteHeader = req.getHeader("Overwrite");
+
+        if (overwriteHeader != null) {
+            if ("T".equalsIgnoreCase(overwriteHeader)) {
+                overwrite = true;
+            } else {
+                overwrite = false;
+            }
+        }
+
+        // Overwriting the destination
+
+        boolean exists = true;
+        try {
+            resources.lookup(destinationPath);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        if (overwrite) {
+
+            // Delete destination resource, if it exists
+            if (exists) {
+                if (!deleteResource(destinationPath, req, resp, true)) {
+                    return false;
+                }
+            } else {
+                resp.setStatus(WebdavStatus.SC_CREATED);
+            }
+
+        } else {
+
+            // If the destination exists, then it's a conflict
+            if (exists) {
+                resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
+                return false;
+            }
+
+        }
+
+        // Copying source to destination
+
+        Hashtable<String,Integer> errorList = new Hashtable<String,Integer>();
+
+        boolean result = copyResource(resources, errorList,
+                                      path, destinationPath);
+
+        if (!result || !errorList.isEmpty()) {
+
+            sendReport(req, resp, errorList);
+            return false;
+
+        }
+
+        // Copy was successful
+        resp.setStatus(WebdavStatus.SC_CREATED);
+        
+        // Removing any lock-null resource which would be present at
+        // the destination path
+        lockNullResources.remove(destinationPath);
+
+        return true;
+
+    }
+
+
+    /**
+     * Copy a collection.
+     *
+     * @param resources Resources implementation to be used
+     * @param errorList Hashtable containing the list of errors which occurred
+     * during the copy operation
+     * @param source Path of the resource to be copied
+     * @param dest Destination path
+     */
+    private boolean copyResource(DirContext resources,
+            Hashtable<String,Integer> errorList, String source, String dest) {
+
+        if (debug > 1)
+            log("Copy: " + source + " To: " + dest);
+
+        Object object = null;
+        try {
+            object = resources.lookup(source);
+        } catch (NamingException e) {
+            // Ignore
+        }
+
+        if (object instanceof DirContext) {
+
+            try {
+                resources.createSubcontext(dest);
+            } catch (NamingException e) {
+                errorList.put
+                    (dest, Integer.valueOf(WebdavStatus.SC_CONFLICT));
+                return false;
+            }
+
+            try {
+                NamingEnumeration<NameClassPair> enumeration = resources.list(source);
+                while (enumeration.hasMoreElements()) {
+                    NameClassPair ncPair = enumeration.nextElement();
+                    String childDest = dest;
+                    if (!"/".equals(childDest))
+                        childDest += "/";
+                    childDest += ncPair.getName();
+                    String childSrc = source;
+                    if (!"/".equals(childSrc))
+                        childSrc += "/";
+                    childSrc += ncPair.getName();
+                    copyResource(resources, errorList, childSrc, childDest);
+                }
+            } catch (NamingException e) {
+                errorList.put
+                    (dest, Integer.valueOf(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                return false;
+            }
+
+        } else {
+
+            if (object instanceof Resource) {
+                try {
+                    resources.bind(dest, object);
+                } catch (NamingException e) {
+                    errorList.put
+                        (source,
+                         Integer.valueOf(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                    return false;
+                }
+            } else {
+                errorList.put
+                    (source,
+                     Integer.valueOf(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                return false;
+            }
+
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Delete a resource.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @return boolean true if the copy is successful
+     */
+    private boolean deleteResource(HttpServletRequest req,
+                                   HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        String path = getRelativePath(req);
+
+        return deleteResource(path, req, resp, true);
+
+    }
+
+
+    /**
+     * Delete a resource.
+     *
+     * @param path Path of the resource which is to be deleted
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @param setStatus Should the response status be set on successful
+     *                  completion
+     */
+    private boolean deleteResource(String path, HttpServletRequest req,
+                                   HttpServletResponse resp, boolean setStatus)
+        throws ServletException, IOException {
+
+        if (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
+            path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        String ifHeader = req.getHeader("If");
+        if (ifHeader == null)
+            ifHeader = "";
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        if (isLocked(path, ifHeader + lockTokenHeader)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return false;
+        }
+
+        boolean exists = true;
+        Object object = null;
+        try {
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        if (!exists) {
+            resp.sendError(WebdavStatus.SC_NOT_FOUND);
+            return false;
+        }
+
+        boolean collection = object instanceof DirContext;
+
+        if (!collection) {
+            try {
+                resources.unbind(path);
+            } catch (NamingException e) {
+                resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
+                return false;
+            }
+        } else {
+
+            Hashtable<String,Integer> errorList =
+                new Hashtable<String,Integer>();
+
+            deleteCollection(req, resources, path, errorList);
+            try {
+                resources.unbind(path);
+            } catch (NamingException e) {
+                errorList.put(path, Integer.valueOf
+                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+            }
+
+            if (!errorList.isEmpty()) {
+
+                sendReport(req, resp, errorList);
+                return false;
+
+            }
+
+        }
+        if (setStatus) {
+            resp.setStatus(WebdavStatus.SC_NO_CONTENT);
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Deletes a collection.
+     *
+     * @param resources Resources implementation associated with the context
+     * @param path Path to the collection to be deleted
+     * @param errorList Contains the list of the errors which occurred
+     */
+    private void deleteCollection(HttpServletRequest req,
+                                  DirContext resources,
+                                  String path,
+                                  Hashtable<String,Integer> errorList) {
+
+        if (debug > 1)
+            log("Delete:" + path);
+
+        if (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
+            path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")) {
+            errorList.put(path, Integer.valueOf(WebdavStatus.SC_FORBIDDEN));
+            return;
+        }
+
+        String ifHeader = req.getHeader("If");
+        if (ifHeader == null)
+            ifHeader = "";
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        Enumeration<NameClassPair> enumeration = null;
+        try {
+            enumeration = resources.list(path);
+        } catch (NamingException e) {
+            errorList.put(path, Integer.valueOf
+                (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+            return;
+        }
+
+        while (enumeration.hasMoreElements()) {
+            NameClassPair ncPair = enumeration.nextElement();
+            String childName = path;
+            if (!"/".equals(childName))
+                childName += "/";
+            childName += ncPair.getName();
+
+            if (isLocked(childName, ifHeader + lockTokenHeader)) {
+
+                errorList.put(childName, Integer.valueOf(WebdavStatus.SC_LOCKED));
+
+            } else {
+
+                try {
+                    Object object = resources.lookup(childName);
+                    if (object instanceof DirContext) {
+                        deleteCollection(req, resources, childName, errorList);
+                    }
+
+                    try {
+                        resources.unbind(childName);
+                    } catch (NamingException e) {
+                        if (!(object instanceof DirContext)) {
+                            // If it's not a collection, then it's an unknown
+                            // error
+                            errorList.put
+                                (childName, Integer.valueOf
+                                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                        }
+                    }
+                } catch (NamingException e) {
+                    errorList.put
+                        (childName, Integer.valueOf
+                            (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                }
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Send a multistatus element containing a complete error report to the
+     * client.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @param errorList List of error to be displayed
+     */
+    private void sendReport(HttpServletRequest req, HttpServletResponse resp,
+                            Hashtable<String,Integer> errorList)
+        throws ServletException, IOException {
+
+        resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
+
+        String absoluteUri = req.getRequestURI();
+        String relativePath = getRelativePath(req);
+
+        XMLWriter generatedXML = new XMLWriter();
+        generatedXML.writeXMLHeader();
+
+        generatedXML.writeElement(null, "multistatus"
+                                  + generateNamespaceDeclarations(),
+                                  XMLWriter.OPENING);
+
+        Enumeration<String> pathList = errorList.keys();
+        while (pathList.hasMoreElements()) {
+
+            String errorPath = pathList.nextElement();
+            int errorCode = errorList.get(errorPath).intValue();
+
+            generatedXML.writeElement(null, "response", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+            String toAppend = errorPath.substring(relativePath.length());
+            if (!toAppend.startsWith("/"))
+                toAppend = "/" + toAppend;
+            generatedXML.writeText(absoluteUri + toAppend);
+            generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML
+                .writeText("HTTP/1.1 " + errorCode + " "
+                           + WebdavStatus.getStatusText(errorCode));
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
+
+        }
+
+        generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING);
+
+        Writer writer = resp.getWriter();
+        writer.write(generatedXML.toString());
+        writer.close();
+
+    }
+
+
+    /**
+     * Propfind helper method.
+     *
+     * @param req The servlet request
+     * @param generatedXML XML response to the Propfind request
+     * @param path Path of the current resource
+     * @param type Propfind type
+     * @param propertiesVector If the propfind type is find properties by
+     * name, then this Vector contains those properties
+     */
+    private void parseProperties(HttpServletRequest req,
+                                 XMLWriter generatedXML,
+                                 String path, int type,
+                                 Vector<String> propertiesVector) {
+
+        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
+        // (the "toUpperCase()" avoids problems on Windows systems)
+        if (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
+            path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF"))
+            return;
+
+        CacheEntry cacheEntry = resources.lookupCache(path);
+
+        generatedXML.writeElement(null, "response", XMLWriter.OPENING);
+        String status = "HTTP/1.1 " + WebdavStatus.SC_OK + " "
+            + WebdavStatus.getStatusText(WebdavStatus.SC_OK);
+
+        // Generating href element
+        generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+
+        String href = req.getContextPath() + req.getServletPath();
+        if (href.endsWith("/") && path.startsWith("/"))
+            href += path.substring(1);
+        else
+            href += path;
+        if (cacheEntry.context != null && !href.endsWith("/"))
+            href += "/";
+
+        generatedXML.writeText(rewriteUrl(href));
+
+        generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+
+        String resourceName = path;
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash != -1)
+            resourceName = resourceName.substring(lastSlash + 1);
+
+        switch (type) {
+
+        case FIND_ALL_PROP :
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            generatedXML.writeProperty
+                (null, "creationdate",
+                 getISOCreationDate(cacheEntry.attributes.getCreation()));
+            generatedXML.writeElement(null, "displayname", XMLWriter.OPENING);
+            generatedXML.writeData(resourceName);
+            generatedXML.writeElement(null, "displayname", XMLWriter.CLOSING);
+            if (cacheEntry.resource != null) {
+                generatedXML.writeProperty
+                    (null, "getlastmodified", FastHttpDateFormat.formatDate
+                        (cacheEntry.attributes.getLastModified(), null));
+                generatedXML.writeProperty
+                    (null, "getcontentlength",
+                     String.valueOf(cacheEntry.attributes.getContentLength()));
+                String contentType = getServletContext().getMimeType
+                    (cacheEntry.name);
+                if (contentType != null) {
+                    generatedXML.writeProperty(null, "getcontenttype",
+                                               contentType);
+                }
+                generatedXML.writeProperty(null, "getetag",
+                                           cacheEntry.attributes.getETag());
+                generatedXML.writeElement(null, "resourcetype",
+                                          XMLWriter.NO_CONTENT);
+            } else {
+                generatedXML.writeElement(null, "resourcetype",
+                                          XMLWriter.OPENING);
+                generatedXML.writeElement(null, "collection",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "resourcetype",
+                                          XMLWriter.CLOSING);
+            }
+
+            generatedXML.writeProperty(null, "source", "");
+
+            String supportedLocks = "<lockentry>"
+                + "<lockscope><exclusive/></lockscope>"
+                + "<locktype><write/></locktype>"
+                + "</lockentry>" + "<lockentry>"
+                + "<lockscope><shared/></lockscope>"
+                + "<locktype><write/></locktype>"
+                + "</lockentry>";
+            generatedXML.writeElement(null, "supportedlock",
+                                      XMLWriter.OPENING);
+            generatedXML.writeText(supportedLocks);
+            generatedXML.writeElement(null, "supportedlock",
+                                      XMLWriter.CLOSING);
+
+            generateLockDiscovery(path, generatedXML);
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_PROPERTY_NAMES :
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "creationdate",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "displayname",
+                                      XMLWriter.NO_CONTENT);
+            if (cacheEntry.resource != null) {
+                generatedXML.writeElement(null, "getcontentlanguage",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "getcontentlength",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "getcontenttype",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "getetag",
+                                          XMLWriter.NO_CONTENT);
+                generatedXML.writeElement(null, "getlastmodified",
+                                          XMLWriter.NO_CONTENT);
+            }
+            generatedXML.writeElement(null, "resourcetype",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "source", XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "lockdiscovery",
+                                      XMLWriter.NO_CONTENT);
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_BY_PROPERTY :
+
+            Vector<String> propertiesNotFound = new Vector<String>();
+
+            // Parse the list of properties
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            Enumeration<String> properties = propertiesVector.elements();
+
+            while (properties.hasMoreElements()) {
+
+                String property = properties.nextElement();
+
+                if ("creationdate".equals(property)) {
+                    generatedXML.writeProperty
+                        (null, "creationdate",
+                         getISOCreationDate(cacheEntry.attributes.getCreation()));
+                } else if ("displayname".equals(property)) {
+                    generatedXML.writeElement
+                        (null, "displayname", XMLWriter.OPENING);
+                    generatedXML.writeData(resourceName);
+                    generatedXML.writeElement
+                        (null, "displayname", XMLWriter.CLOSING);
+                } else if ("getcontentlanguage".equals(property)) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeElement(null, "getcontentlanguage",
+                                                  XMLWriter.NO_CONTENT);
+                    }
+                } else if ("getcontentlength".equals(property)) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeProperty
+                            (null, "getcontentlength",
+                                String.valueOf(cacheEntry.attributes.getContentLength()));
+                    }
+                } else if ("getcontenttype".equals(property)) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeProperty
+                            (null, "getcontenttype",
+                             getServletContext().getMimeType
+                             (cacheEntry.name));
+                    }
+                } else if ("getetag".equals(property)) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeProperty
+                            (null, "getetag", cacheEntry.attributes.getETag());
+                    }
+                } else if ("getlastmodified".equals(property)) {
+                    if (cacheEntry.context != null) {
+                        propertiesNotFound.addElement(property);
+                    } else {
+                        generatedXML.writeProperty
+                            (null, "getlastmodified", FastHttpDateFormat.formatDate
+                                    (cacheEntry.attributes.getLastModified(), null));
+                    }
+                } else if ("resourcetype".equals(property)) {
+                    if (cacheEntry.context != null) {
+                        generatedXML.writeElement(null, "resourcetype",
+                                                  XMLWriter.OPENING);
+                        generatedXML.writeElement(null, "collection",
+                                                  XMLWriter.NO_CONTENT);
+                        generatedXML.writeElement(null, "resourcetype",
+                                                  XMLWriter.CLOSING);
+                    } else {
+                        generatedXML.writeElement(null, "resourcetype",
+                                                  XMLWriter.NO_CONTENT);
+                    }
+                } else if ("source".equals(property)) {
+                    generatedXML.writeProperty(null, "source", "");
+                } else if ("supportedlock".equals(property)) {
+                    supportedLocks = "<lockentry>"
+                        + "<lockscope><exclusive/></lockscope>"
+                        + "<locktype><write/></locktype>"
+                        + "</lockentry>" + "<lockentry>"
+                        + "<lockscope><shared/></lockscope>"
+                        + "<locktype><write/></locktype>"
+                        + "</lockentry>";
+                    generatedXML.writeElement(null, "supportedlock",
+                                              XMLWriter.OPENING);
+                    generatedXML.writeText(supportedLocks);
+                    generatedXML.writeElement(null, "supportedlock",
+                                              XMLWriter.CLOSING);
+                } else if ("lockdiscovery".equals(property)) {
+                    if (!generateLockDiscovery(path, generatedXML))
+                        propertiesNotFound.addElement(property);
+                } else {
+                    propertiesNotFound.addElement(property);
+                }
+
+            }
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            Enumeration<String> propertiesNotFoundList = propertiesNotFound.elements();
+
+            if (propertiesNotFoundList.hasMoreElements()) {
+
+                status = "HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
+                    + " " + WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND);
+
+                generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+                generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+                while (propertiesNotFoundList.hasMoreElements()) {
+                    generatedXML.writeElement
+                        (null, propertiesNotFoundList.nextElement(),
+                         XMLWriter.NO_CONTENT);
+                }
+
+                generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+                generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+                generatedXML.writeText(status);
+                generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+                generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            }
+
+            break;
+
+        default: // not possible
+            break;
+        }
+
+        generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
+
+    }
+
+
+    /**
+     * Propfind helper method. Displays the properties of a lock-null resource.
+     *
+     * @param generatedXML XML response to the Propfind request
+     * @param path Path of the current resource
+     * @param type Propfind type
+     * @param propertiesVector If the propfind type is find properties by
+     * name, then this Vector contains those properties
+     */
+    private void parseLockNullProperties(HttpServletRequest req,
+                                         XMLWriter generatedXML,
+                                         String path, int type,
+                                         Vector<String> propertiesVector) {
+
+        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
+        // (the "toUpperCase()" avoids problems on Windows systems)
+        if (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
+            path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF"))
+            return;
+
+        // Retrieving the lock associated with the lock-null resource
+        LockInfo lock = resourceLocks.get(path);
+
+        if (lock == null)
+            return;
+
+        generatedXML.writeElement(null, "response", XMLWriter.OPENING);
+        String status = "HTTP/1.1 " + WebdavStatus.SC_OK + " "
+            + WebdavStatus.getStatusText(WebdavStatus.SC_OK);
+
+        // Generating href element
+        generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+
+        String absoluteUri = req.getRequestURI();
+        String relativePath = getRelativePath(req);
+        String toAppend = path.substring(relativePath.length());
+        if (!toAppend.startsWith("/"))
+            toAppend = "/" + toAppend;
+
+        generatedXML.writeText(rewriteUrl(RequestUtil.normalize(
+                absoluteUri + toAppend)));
+
+        generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+
+        String resourceName = path;
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash != -1)
+            resourceName = resourceName.substring(lastSlash + 1);
+
+        switch (type) {
+
+        case FIND_ALL_PROP :
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            generatedXML.writeProperty
+                (null, "creationdate",
+                 getISOCreationDate(lock.creationDate.getTime()));
+            generatedXML.writeElement
+                (null, "displayname", XMLWriter.OPENING);
+            generatedXML.writeData(resourceName);
+            generatedXML.writeElement
+                (null, "displayname", XMLWriter.CLOSING);
+            generatedXML.writeProperty(null, "getlastmodified",
+                                       FastHttpDateFormat.formatDate
+                                       (lock.creationDate.getTime(), null));
+            generatedXML.writeProperty
+                (null, "getcontentlength", String.valueOf(0));
+            generatedXML.writeProperty(null, "getcontenttype", "");
+            generatedXML.writeProperty(null, "getetag", "");
+            generatedXML.writeElement(null, "resourcetype",
+                                      XMLWriter.OPENING);
+            generatedXML.writeElement(null, "lock-null", XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "resourcetype",
+                                      XMLWriter.CLOSING);
+
+            generatedXML.writeProperty(null, "source", "");
+
+            String supportedLocks = "<lockentry>"
+                + "<lockscope><exclusive/></lockscope>"
+                + "<locktype><write/></locktype>"
+                + "</lockentry>" + "<lockentry>"
+                + "<lockscope><shared/></lockscope>"
+                + "<locktype><write/></locktype>"
+                + "</lockentry>";
+            generatedXML.writeElement(null, "supportedlock",
+                                      XMLWriter.OPENING);
+            generatedXML.writeText(supportedLocks);
+            generatedXML.writeElement(null, "supportedlock",
+                                      XMLWriter.CLOSING);
+
+            generateLockDiscovery(path, generatedXML);
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_PROPERTY_NAMES :
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "creationdate",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "displayname",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getcontentlanguage",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getcontentlength",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getcontenttype",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getetag",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "getlastmodified",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "resourcetype",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "source",
+                                      XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "lockdiscovery",
+                                      XMLWriter.NO_CONTENT);
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_BY_PROPERTY :
+
+            Vector<String> propertiesNotFound = new Vector<String>();
+
+            // Parse the list of properties
+
+            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+            Enumeration<String> properties = propertiesVector.elements();
+
+            while (properties.hasMoreElements()) {
+
+                String property = properties.nextElement();
+
+                if ("creationdate".equals(property)) {
+                    generatedXML.writeProperty
+                        (null, "creationdate",
+                         getISOCreationDate(lock.creationDate.getTime()));
+                } else if ("displayname".equals(property)) {
+                    generatedXML.writeElement
+                        (null, "displayname", XMLWriter.OPENING);
+                    generatedXML.writeData(resourceName);
+                    generatedXML.writeElement
+                        (null, "displayname", XMLWriter.CLOSING);
+                } else if ("getcontentlanguage".equals(property)) {
+                    generatedXML.writeElement(null, "getcontentlanguage",
+                                              XMLWriter.NO_CONTENT);
+                } else if ("getcontentlength".equals(property)) {
+                    generatedXML.writeProperty
+                        (null, "getcontentlength", String.valueOf(0));
+                } else if ("getcontenttype".equals(property)) {
+                    generatedXML.writeProperty
+                        (null, "getcontenttype", "");
+                } else if ("getetag".equals(property)) {
+                    generatedXML.writeProperty(null, "getetag", "");
+                } else if ("getlastmodified".equals(property)) {
+                    generatedXML.writeProperty
+                        (null, "getlastmodified",
+                          FastHttpDateFormat.formatDate
+                         (lock.creationDate.getTime(), null));
+                } else if ("resourcetype".equals(property)) {
+                    generatedXML.writeElement(null, "resourcetype",
+                                              XMLWriter.OPENING);
+                    generatedXML.writeElement(null, "lock-null",
+                                              XMLWriter.NO_CONTENT);
+                    generatedXML.writeElement(null, "resourcetype",
+                                              XMLWriter.CLOSING);
+                } else if ("source".equals(property)) {
+                    generatedXML.writeProperty(null, "source", "");
+                } else if ("supportedlock".equals(property)) {
+                    supportedLocks = "<lockentry>"
+                        + "<lockscope><exclusive/></lockscope>"
+                        + "<locktype><write/></locktype>"
+                        + "</lockentry>" + "<lockentry>"
+                        + "<lockscope><shared/></lockscope>"
+                        + "<locktype><write/></locktype>"
+                        + "</lockentry>";
+                    generatedXML.writeElement(null, "supportedlock",
+                                              XMLWriter.OPENING);
+                    generatedXML.writeText(supportedLocks);
+                    generatedXML.writeElement(null, "supportedlock",
+                                              XMLWriter.CLOSING);
+                } else if ("lockdiscovery".equals(property)) {
+                    if (!generateLockDiscovery(path, generatedXML))
+                        propertiesNotFound.addElement(property);
+                } else {
+                    propertiesNotFound.addElement(property);
+                }
+
+            }
+
+            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML.writeText(status);
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            Enumeration<String> propertiesNotFoundList = propertiesNotFound.elements();
+
+            if (propertiesNotFoundList.hasMoreElements()) {
+
+                status = "HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
+                    + " " + WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND);
+
+                generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
+                generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
+
+                while (propertiesNotFoundList.hasMoreElements()) {
+                    generatedXML.writeElement
+                        (null, propertiesNotFoundList.nextElement(),
+                         XMLWriter.NO_CONTENT);
+                }
+
+                generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
+                generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+                generatedXML.writeText(status);
+                generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+                generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            }
+
+            break;
+
+        default: // not possible
+            break;
+        }
+
+        generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
+
+    }
+
+
+    /**
+     * Print the lock discovery information associated with a path.
+     *
+     * @param path Path
+     * @param generatedXML XML data to which the locks info will be appended
+     * @return true if at least one lock was displayed
+     */
+    private boolean generateLockDiscovery
+        (String path, XMLWriter generatedXML) {
+
+        LockInfo resourceLock = resourceLocks.get(path);
+        Enumeration<LockInfo> collectionLocksList = collectionLocks.elements();
+
+        boolean wroteStart = false;
+
+        if (resourceLock != null) {
+            wroteStart = true;
+            generatedXML.writeElement(null, "lockdiscovery",
+                                      XMLWriter.OPENING);
+            resourceLock.toXML(generatedXML);
+        }
+
+        while (collectionLocksList.hasMoreElements()) {
+            LockInfo currentLock = collectionLocksList.nextElement();
+            if (path.startsWith(currentLock.path)) {
+                if (!wroteStart) {
+                    wroteStart = true;
+                    generatedXML.writeElement(null, "lockdiscovery",
+                                              XMLWriter.OPENING);
+                }
+                currentLock.toXML(generatedXML);
+            }
+        }
+
+        if (wroteStart) {
+            generatedXML.writeElement(null, "lockdiscovery",
+                                      XMLWriter.CLOSING);
+        } else {
+            return false;
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Get creation date in ISO format.
+     */
+    private String getISOCreationDate(long creationDate) {
+        StringBuilder creationDateValue = new StringBuilder
+            (creationDateFormat.get().format
+             (new Date(creationDate)));
+        /*
+        int offset = Calendar.getInstance().getTimeZone().getRawOffset()
+            / 3600000; // FIXME ?
+        if (offset < 0) {
+            creationDateValue.append("-");
+            offset = -offset;
+        } else if (offset > 0) {
+            creationDateValue.append("+");
+        }
+        if (offset != 0) {
+            if (offset < 10)
+                creationDateValue.append("0");
+            creationDateValue.append(offset + ":00");
+        } else {
+            creationDateValue.append("Z");
+        }
+        */
+        return creationDateValue.toString();
+    }
+
+    /**
+     * Determines the methods normally allowed for the resource.
+     *
+     */
+    private StringBuilder determineMethodsAllowed(DirContext resources,
+                                                 HttpServletRequest req) {
+
+        StringBuilder methodsAllowed = new StringBuilder();
+        boolean exists = true;
+        Object object = null;
+        try {
+            String path = getRelativePath(req);
+
+            object = resources.lookup(path);
+        } catch (NamingException e) {
+            exists = false;
+        }
+
+        if (!exists) {
+            methodsAllowed.append("OPTIONS, MKCOL, PUT, LOCK");
+            return methodsAllowed;
+        }
+
+        methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE");
+        methodsAllowed.append(", PROPPATCH, COPY, MOVE, LOCK, UNLOCK");
+
+        if (listings) {
+            methodsAllowed.append(", PROPFIND");
+        }
+
+        if (!(object instanceof DirContext)) {
+            methodsAllowed.append(", PUT");
+        }
+
+        return methodsAllowed;
+    }
+
+    // --------------------------------------------------  LockInfo Inner Class
+
+
+    /**
+     * Holds a lock information.
+     */
+    private static class LockInfo {
+
+
+        // -------------------------------------------------------- Constructor
+
+
+        /**
+         * Constructor.
+         */
+        public LockInfo() {
+            // Ignore
+        }
+
+
+        // ------------------------------------------------- Instance Variables
+
+
+        String path = "/";
+        String type = "write";
+        String scope = "exclusive";
+        int depth = 0;
+        String owner = "";
+        Vector<String> tokens = new Vector<String>();
+        long expiresAt = 0;
+        Date creationDate = new Date();
+
+
+        // ----------------------------------------------------- Public Methods
+
+
+        /**
+         * Get a String representation of this lock token.
+         */
+        public String toString() {
+
+            StringBuilder result =  new StringBuilder("Type:");
+            result.append(type).append("\n");
+            result.append("Scope:").append(scope).append("\n");
+            result.append("Depth:").append(depth).append("\n");
+            result.append("Owner:").append(owner).append("\n");
+            result.append("Expiration:").append(
+                FastHttpDateFormat.formatDate(expiresAt, null)).append("\n");
+            Enumeration<String> tokensList = tokens.elements();
+            while (tokensList.hasMoreElements()) {
+                result.append("Token:").append(tokensList.nextElement()).append("\n");
+            }
+            return result.toString();
+
+        }
+
+
+        /**
+         * Return true if the lock has expired.
+         */
+        public boolean hasExpired() {
+            return System.currentTimeMillis() > expiresAt;
+        }
+
+
+        /**
+         * Return true if the lock is exclusive.
+         */
+        public boolean isExclusive() {
+
+            return "exclusive".equals(scope);
+
+        }
+
+
+        /**
+         * Get an XML representation of this lock token. This method will
+         * append an XML fragment to the given XML writer.
+         */
+        public void toXML(XMLWriter generatedXML) {
+
+            generatedXML.writeElement(null, "activelock", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "locktype", XMLWriter.OPENING);
+            generatedXML.writeElement(null, type, XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "locktype", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "lockscope", XMLWriter.OPENING);
+            generatedXML.writeElement(null, scope, XMLWriter.NO_CONTENT);
+            generatedXML.writeElement(null, "lockscope", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "depth", XMLWriter.OPENING);
+            if (depth == INFINITY) {
+                generatedXML.writeText("Infinity");
+            } else {
+                generatedXML.writeText("0");
+            }
+            generatedXML.writeElement(null, "depth", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "owner", XMLWriter.OPENING);
+            generatedXML.writeText(owner);
+            generatedXML.writeElement(null, "owner", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "timeout", XMLWriter.OPENING);
+            long timeout = (expiresAt - System.currentTimeMillis()) / 1000;
+            generatedXML.writeText("Second-" + timeout);
+            generatedXML.writeElement(null, "timeout", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "locktoken", XMLWriter.OPENING);
+            Enumeration<String> tokensList = tokens.elements();
+            while (tokensList.hasMoreElements()) {
+                generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+                generatedXML.writeText("opaquelocktoken:"
+                                       + tokensList.nextElement());
+                generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+            }
+            generatedXML.writeElement(null, "locktoken", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "activelock", XMLWriter.CLOSING);
+
+        }
+
+
+    }
+
+
+    // --------------------------------------------- WebdavResolver Inner Class
+    /**
+     * Work around for XML parsers that don't fully respect
+     * {@link DocumentBuilderFactory#setExpandEntityReferences(boolean)}. External
+     * references are filtered out for security reasons. See CVE-2007-5461.
+     */
+    private static class WebdavResolver implements EntityResolver {
+        private ServletContext context;
+        
+        public WebdavResolver(ServletContext theContext) {
+            context = theContext;
+        }
+     
+        public InputSource resolveEntity (String publicId, String systemId) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.IGNORED_EXTERNAL_ENTITY_INFO),
+                                              new Object[] {publicId, systemId});
+            context.log(msg);
+            return new InputSource(
+                    new StringReader("Ignored external entity"));
+        }
+    }
+}
+
+
+// --------------------------------------------------------  WebdavStatus Class
+
+
+/**
+ * Wraps the HttpServletResponse class to abstract the
+ * specific protocol used.  To support other protocols
+ * we would only need to modify this class and the
+ * WebDavRetCode classes.
+ *
+ * @author              Marc Eaddy
+ * @version             1.0, 16 Nov 1997
+ */
+class WebdavStatus {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * This Hashtable contains the mapping of HTTP and WebDAV
+     * status codes to descriptive text.  This is a static
+     * variable.
+     */
+    private static final Hashtable<Integer,String> mapStatusCodes = new Hashtable<Integer,String>();
+
+
+    // ------------------------------------------------------ HTTP Status Codes
+
+
+    /**
+     * Status code (200) indicating the request succeeded normally.
+     */
+    public static final int SC_OK = HttpServletResponse.SC_OK;
+
+
+    /**
+     * Status code (201) indicating the request succeeded and created
+     * a new resource on the server.
+     */
+    public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
+
+
+    /**
+     * Status code (202) indicating that a request was accepted for
+     * processing, but was not completed.
+     */
+    public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
+
+
+    /**
+     * Status code (204) indicating that the request succeeded but that
+     * there was no new information to return.
+     */
+    public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
+
+
+    /**
+     * Status code (301) indicating that the resource has permanently
+     * moved to a new location, and that future references should use a
+     * new URI with their requests.
+     */
+    public static final int SC_MOVED_PERMANENTLY =
+        HttpServletResponse.SC_MOVED_PERMANENTLY;
+
+
+    /**
+     * Status code (302) indicating that the resource has temporarily
+     * moved to another location, but that future references should
+     * still use the original URI to access the resource.
+     */
+    public static final int SC_MOVED_TEMPORARILY =
+        HttpServletResponse.SC_MOVED_TEMPORARILY;
+
+
+    /**
+     * Status code (304) indicating that a conditional GET operation
+     * found that the resource was available and not modified.
+     */
+    public static final int SC_NOT_MODIFIED =
+        HttpServletResponse.SC_NOT_MODIFIED;
+
+
+    /**
+     * Status code (400) indicating the request sent by the client was
+     * syntactically incorrect.
+     */
+    public static final int SC_BAD_REQUEST =
+        HttpServletResponse.SC_BAD_REQUEST;
+
+
+    /**
+     * Status code (401) indicating that the request requires HTTP
+     * authentication.
+     */
+    public static final int SC_UNAUTHORIZED =
+        HttpServletResponse.SC_UNAUTHORIZED;
+
+
+    /**
+     * Status code (403) indicating the server understood the request
+     * but refused to fulfill it.
+     */
+    public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
+
+
+    /**
+     * Status code (404) indicating that the requested resource is not
+     * available.
+     */
+    public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
+
+
+    /**
+     * Status code (500) indicating an error inside the HTTP service
+     * which prevented it from fulfilling the request.
+     */
+    public static final int SC_INTERNAL_SERVER_ERROR =
+        HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+
+
+    /**
+     * Status code (501) indicating the HTTP service does not support
+     * the functionality needed to fulfill the request.
+     */
+    public static final int SC_NOT_IMPLEMENTED =
+        HttpServletResponse.SC_NOT_IMPLEMENTED;
+
+
+    /**
+     * Status code (502) indicating that the HTTP server received an
+     * invalid response from a server it consulted when acting as a
+     * proxy or gateway.
+     */
+    public static final int SC_BAD_GATEWAY =
+        HttpServletResponse.SC_BAD_GATEWAY;
+
+
+    /**
+     * Status code (503) indicating that the HTTP service is
+     * temporarily overloaded, and unable to handle the request.
+     */
+    public static final int SC_SERVICE_UNAVAILABLE =
+        HttpServletResponse.SC_SERVICE_UNAVAILABLE;
+
+
+    /**
+     * Status code (100) indicating the client may continue with
+     * its request.  This interim response is used to inform the
+     * client that the initial part of the request has been
+     * received and has not yet been rejected by the server.
+     */
+    public static final int SC_CONTINUE = 100;
+
+
+    /**
+     * Status code (405) indicating the method specified is not
+     * allowed for the resource.
+     */
+    public static final int SC_METHOD_NOT_ALLOWED = 405;
+
+
+    /**
+     * Status code (409) indicating that the request could not be
+     * completed due to a conflict with the current state of the
+     * resource.
+     */
+    public static final int SC_CONFLICT = 409;
+
+
+    /**
+     * Status code (412) indicating the precondition given in one
+     * or more of the request-header fields evaluated to false
+     * when it was tested on the server.
+     */
+    public static final int SC_PRECONDITION_FAILED = 412;
+
+
+    /**
+     * Status code (413) indicating the server is refusing to
+     * process a request because the request entity is larger
+     * than the server is willing or able to process.
+     */
+    public static final int SC_REQUEST_TOO_LONG = 413;
+
+
+    /**
+     * Status code (415) indicating the server is refusing to service
+     * the request because the entity of the request is in a format
+     * not supported by the requested resource for the requested
+     * method.
+     */
+    public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+
+
+    // -------------------------------------------- Extended WebDav status code
+
+
+    /**
+     * Status code (207) indicating that the response requires
+     * providing status for multiple independent operations.
+     */
+    public static final int SC_MULTI_STATUS = 207;
+    // This one collides with HTTP 1.1
+    // "207 Partial Update OK"
+
+
+    /**
+     * Status code (418) indicating the entity body submitted with
+     * the PATCH method was not understood by the resource.
+     */
+    public static final int SC_UNPROCESSABLE_ENTITY = 418;
+    // This one collides with HTTP 1.1
+    // "418 Reauthentication Required"
+
+
+    /**
+     * Status code (419) indicating that the resource does not have
+     * sufficient space to record the state of the resource after the
+     * execution of this method.
+     */
+    public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
+    // This one collides with HTTP 1.1
+    // "419 Proxy Reauthentication Required"
+
+
+    /**
+     * Status code (420) indicating the method was not executed on
+     * a particular resource within its scope because some part of
+     * the method's execution failed causing the entire method to be
+     * aborted.
+     */
+    public static final int SC_METHOD_FAILURE = 420;
+
+
+    /**
+     * Status code (423) indicating the destination resource of a
+     * method is locked, and either the request did not contain a
+     * valid Lock-Info header, or the Lock-Info header identifies
+     * a lock held by another principal.
+     */
+    public static final int SC_LOCKED = 423;
+
+
+    // ------------------------------------------------------------ Initializer
+
+
+    static {
+        // HTTP 1.0 status Code
+        addStatusCodeMap(SC_OK, "OK");
+        addStatusCodeMap(SC_CREATED, "Created");
+        addStatusCodeMap(SC_ACCEPTED, "Accepted");
+        addStatusCodeMap(SC_NO_CONTENT, "No Content");
+        addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
+        addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
+        addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
+        addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
+        addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
+        addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
+        addStatusCodeMap(SC_NOT_FOUND, "Not Found");
+        addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
+        addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
+        addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
+        addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        addStatusCodeMap(SC_CONTINUE, "Continue");
+        addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
+        addStatusCodeMap(SC_CONFLICT, "Conflict");
+        addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
+        addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
+        addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
+        // WebDav Status Codes
+        addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
+        addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
+        addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
+                         "Insufficient Space On Resource");
+        addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
+        addStatusCodeMap(SC_LOCKED, "Locked");
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Returns the HTTP status text for the HTTP or WebDav status code
+     * specified by looking it up in the static mapping.  This is a
+     * static function.
+     *
+     * @param   nHttpStatusCode [IN] HTTP or WebDAV status code
+     * @return  A string with a short descriptive phrase for the
+     *                  HTTP status code (e.g., "OK").
+     */
+    public static String getStatusText(int nHttpStatusCode) {
+        Integer intKey = Integer.valueOf(nHttpStatusCode);
+
+        if (!mapStatusCodes.containsKey(intKey)) {
+            return "";
+        } else {
+            return mapStatusCodes.get(intKey);
+        }
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Adds a new status code -> status text mapping.  This is a static
+     * method because the mapping is a static variable.
+     *
+     * @param   nKey    [IN] HTTP or WebDAV status code
+     * @param   strVal  [IN] HTTP status text
+     */
+    private static void addStatusCodeMap(int nKey, String strVal) {
+        mapStatusCodes.put(Integer.valueOf(nKey), strVal);
+    }
+
+}
+
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/package.html b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/package.html
new file mode 100644
index 0000000..9a8d5b5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/servlets/package.html
@@ -0,0 +1,36 @@
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<body>
+
+<p>This package contains <code>Servlets</code> that implement some of the
+standard functionality provided by the Catalina servlet container.  Because
+these servlets are in the <code>org.apache.catalina</code> package hierarchy,
+they are in the privileged position of being able to reference internal server
+data structures, which application level servlets are prevented from
+accessing (by the application class loader implementation).</p>
+
+<p>To the extent that these servlets depend upon internal Catalina data
+structures, they are obviously not portable to other servlet container
+environments.  However, they can be used as models for creating application
+level servlets that provide similar capabilities -- most obviously the
+<a href="DefaultServlet.html">DefaultServlet</a> implementation, which
+serves static resources when Catalina runs stand-alone.</p>
+
+</body>
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/BaseSessionLocker.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/BaseSessionLocker.java
new file mode 100755
index 0000000..df61973
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/BaseSessionLocker.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * BaseSessionLocker.java
+ *
+ * Created on January 18, 2006, 4:46 PM
+ */
+
+package org.apache.catalina.session;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.SessionLocker;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+
+/**
+ *
+ * @author lwhite
+ */
+public class BaseSessionLocker implements SessionLocker {
+    
+    /** Creates a new instance of BaseSessionLocker */
+    public BaseSessionLocker() {
+    }
+    
+    public void init(Context context) {
+        _context = context;
+    }
+    
+    public boolean lockSession(ServletRequest req) throws ServletException {
+        return true;
+    }
+    
+    public void unlockSession(ServletRequest req) {
+    }
+    
+    protected Context _context = null;
+    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/Constants.java
new file mode 100644
index 0000000..403a45c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.session</code>
+ * package.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public class Constants {
+
+    public static final String Package = "org.apache.catalina.session";
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/CookiePersistentManager.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/CookiePersistentManager.java
new file mode 100644
index 0000000..ec1783b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/CookiePersistentManager.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.session;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Session;
+import org.apache.catalina.core.StandardContext;
+
+import java.io.*;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.servlet.http.*;
+
+/**
+ * Session manager for cookie-based persistence, where cookies carry session state.
+ *
+ * With cookie-based persistence, only session attribute values of type String are supported.
+ */
+
+public class CookiePersistentManager extends StandardManager {
+
+    private static final String LAST_ACCESSED_TIME = "lastAccessedTime=";
+    private static final String MAX_INACTIVE_INTERVAL = "maxInactiveInterval=";
+    private static final String IS_VALID = "isValid=";
+
+    private final Set<String> sessionIds = new HashSet<String>();
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    // The name of the cookies that carry session state
+    private String cookieName;
+
+    public void setCookieName(String cookieName) {
+        this.cookieName = cookieName;
+    }
+
+    @Override
+    public void add(Session session) {
+        synchronized (sessionIds) {
+            if (!sessionIds.add(session.getIdInternal())) {
+                throw new IllegalArgumentException("Session with id " + session.getIdInternal() +
+                        " already present");
+            }
+            int size = sessionIds.size();
+            if (size > maxActive) {
+                maxActive = size;
+            }
+        }
+    }
+
+    @Override
+    public Session findSession(String id, HttpServletRequest request) throws IOException {
+        if (cookieName == null) {
+            return null;
+        }
+        Cookie[] cookies = request.getCookies();
+        if (cookies == null) {
+            return null;
+        }
+        String value = null;
+        for (Cookie cookie : cookies) {
+            if (cookieName.equals(cookie.getName())) {
+                return parseSession(cookie.getValue(), request.getRequestedSessionId());
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void clearSessions() {
+        synchronized (sessionIds) {
+            sessionIds.clear();
+        }
+    }
+
+    @Override
+    public Session[] findSessions() {
+        return null;
+    }
+
+    @Override
+    public void remove(Session session) {
+        synchronized (sessionIds) {
+            sessionIds.remove(session.getIdInternal());
+        }
+    }
+
+    @Override
+    public Cookie toCookie(Session session) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        sb.append(IS_VALID + session.isValid() + ';');
+        sb.append(LAST_ACCESSED_TIME + session.getLastAccessedTime() + ';');
+        sb.append(MAX_INACTIVE_INTERVAL + session.getMaxInactiveInterval() + ';');
+        synchronized (session.getAttributes()) {
+            Set<Map.Entry<String, Object>> entries = session.getAttributes().entrySet();
+            int numElements = entries.size();
+            int i = 0;
+            for (Map.Entry<String,Object> entry : entries) {
+                sb.append(entry.getKey() + "=" + entry.getValue());
+                if (i++ < numElements-1) {
+                    sb.append(',');
+                }
+            }
+        }
+
+        remove(session);
+
+        return new Cookie(cookieName, sb.toString());
+    }
+
+    @Override
+    public void checkSessionAttribute(String name, Object value) {
+        if (!(value instanceof String)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SET_SESSION_ATTRIBUTE_EXCEPTION), name);
+            throw new IllegalArgumentException(msg);
+        }
+    }
+
+    /*
+     * Parses the given string into a session, and returns it.
+     * 
+     * The given string is supposed to contain a session encoded using toCookie().
+     */
+    private Session parseSession(String value, String sessionId) throws IOException {
+        String[] components = value.split(";");
+        if (components.length != 4) {
+            throw new IllegalArgumentException("Invalid session encoding");
+        }
+
+        StandardSession session = (StandardSession) createSession(sessionId);
+
+        // 1st component: isValid
+        int index = components[0].indexOf('=');
+        if (index < 0) {
+            throw new IllegalArgumentException("Missing separator for isValid");
+        }
+        session.setValid(Boolean.parseBoolean(components[0].substring(index+1, components[0].length())));
+
+        // 2nd component: lastAccessedTime
+        index = components[1].indexOf('=');
+        if (index < 0) {
+            throw new IllegalArgumentException("Missing separator for lastAccessedTime");
+        }
+        session.setLastAccessedTime(Long.parseLong(components[1].substring(index+1, components[1].length())));
+
+        // 3rd component: maxInactiveInterval
+        index = components[2].indexOf('=');
+        if (index < 0) {
+            throw new IllegalArgumentException("Missing separator for maxInactiveInterval");
+        }
+        session.setMaxInactiveInterval(Integer.parseInt(
+                components[2].substring(index+1, components[2].length())));
+
+        // 4th component: comma-separated sequence of name-value pairs
+        String[] entries = components[3].split(",");
+        for (String entry : entries) {
+            index = entry.indexOf('=');
+            if (index < 0) {
+                throw new IllegalArgumentException("Missing session attribute key-value separator");
+            }
+            session.setAttribute(entry.substring(0, index), entry.substring(index+1, entry.length()));
+        }
+
+        return session;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/FileStore.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/FileStore.java
new file mode 100644
index 0000000..6482bcb
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/FileStore.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Loader;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Session;
+import org.apache.catalina.core.StandardContext;
+
+import javax.servlet.ServletContext;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Concrete implementation of the <b>Store</b> interface that utilizes
+ * a file per saved Session in a configured directory.  Sessions that are
+ * saved are still subject to being expired based on inactivity.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.4 $ $Date: 2007/01/04 01:31:57 $
+ */
+
+public final class FileStore extends StoreBase {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------- Constants
+
+
+    /**
+     * The extension to use for serialized session filenames.
+     */
+    private static final String FILE_EXT = ".session";
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The pathname of the directory in which Sessions are stored.
+     * This may be an absolute pathname, or a relative path that is
+     * resolved against the temporary work directory for this application.
+     */
+    private String directory = ".";
+
+
+    /**
+     * A File representing the directory in which Sessions are stored.
+     */
+    private File directoryFile = null;
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "FileStore/1.0";
+
+    /**
+     * Name to register for this Store, used for logging.
+     */
+    private static final String storeName = "fileStore";
+
+    /**
+     * Name to register for the background thread.
+     */
+    private static final String threadName = "FileStore";
+    
+    /**
+    * Our write-through cache of session objects
+    * HERCULES: addition
+    */
+    private Hashtable<String, Session> sessions = new Hashtable<String, Session>();     
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the directory path for this Store.
+     */
+    public String getDirectory() {
+
+        return (directory);
+
+    }
+
+
+    /**
+     * Set the directory path for this Store.
+     *
+     * @param path The new directory path
+     */
+    public void setDirectory(String path) {
+
+        String oldDirectory = this.directory;
+        this.directory = path;
+        this.directoryFile = null;
+        support.firePropertyChange("directory", oldDirectory,
+                                   this.directory);
+
+    }
+    
+
+    /**
+     * Return descriptive information about this Store implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * Return the thread name for this Store.
+     */
+    public String getThreadName() {
+        return(threadName);
+    }
+
+    /**
+     * Return the name for this Store, used for logging.
+     */
+    public String getStoreName() {
+        return(storeName);
+    }
+
+
+    /**
+     * Return the number of Sessions present in this Store.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public int getSize() throws IOException {
+
+        // Acquire the list of files in our storage directory
+        File file = directory();
+        if (file == null) {
+            return (0);
+        }
+        String files[] = file.list();
+
+        // Figure out which files are sessions
+        int keycount = 0;
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].endsWith(FILE_EXT)) {
+                keycount++;
+            }
+        }
+        return (keycount);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Remove all of the Sessions in this Store.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void clear()
+        throws IOException {
+
+        String[] keys = keys();
+        for (int i = 0; i < keys.length; i++) {
+            remove(keys[i]);
+        }
+
+    }
+
+
+    /**
+     * Return an array containing the session identifiers of all Sessions
+     * currently saved in this Store.  If there are no such Sessions, a
+     * zero-length array is returned.
+     *
+     * @exception IOException if an input/output error occurred
+     */
+    public String[] keys() throws IOException {
+
+        // Acquire the list of files in our storage directory
+        File file = directory();
+        if (file == null) {
+            return (new String[0]);
+        }
+        String files[] = file.list();
+
+        // Build and return the list of session identifiers
+        ArrayList<String> list = new ArrayList<String>();
+        int n = FILE_EXT.length();
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].endsWith(FILE_EXT)) {
+                list.add(files[i].substring(0, files[i].length() - n));
+            }
+        }
+        return list.toArray(new String[list.size()]);
+
+    }
+
+
+    /**
+     * Load and return the Session associated with the specified session
+     * identifier from this Store, without removing it.  If there is no
+     * such stored Session, return <code>null</code>.
+     *
+     * @param id Session identifier of the session to load
+     *
+     * @exception ClassNotFoundException if a deserialization error occurs
+     * @exception IOException if an input/output error occurs
+     */
+    public Session load(String id)
+        throws ClassNotFoundException, IOException {
+            
+        //HERCULES:addition
+        // Check to see if it's in our cache first
+        Session sess = sessions.get(id);
+        if ( sess != null ) {
+            return sess;
+        }
+        //HERCULES:addition            
+
+        // Open an input stream to the specified pathname, if any
+        File file = file(id);
+        if (file == null) {
+            return (null);
+        }
+
+        if (! file.exists()) {
+            return (null);
+        }
+        if (debug >= 1) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.LOADING_SESSION_FROM_FILE),
+                                              new Object[] {id, file.getAbsolutePath()});
+            log(msg);
+        }
+
+        FileInputStream fis = null;
+        BufferedInputStream bis = null;
+        ObjectInputStream ois = null;
+        Loader loader = null;
+        ClassLoader classLoader = null;
+        try {
+            fis = new FileInputStream(file.getAbsolutePath());
+            bis = new BufferedInputStream(fis);
+            Container container = manager.getContainer();
+            if (container != null) {
+                ois = ((StandardContext)container).createObjectInputStream(bis);
+            } else {
+                ois = new ObjectInputStream(bis);
+            }
+            //end HERCULES:mod
+        } catch (FileNotFoundException e) {
+            if (debug >= 1)
+                log("No persisted data file found");
+            return (null);
+        } catch (IOException e) {
+            if (bis != null) {
+                try {
+                    bis.close();
+                } catch (IOException f) {
+                    // Ignore
+                }
+            }
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException f) {
+                    // Ignore
+                }
+            }
+            throw e;
+        }
+
+        try {
+            StandardSession session =
+                StandardSession.deserialize(ois, manager);
+            session.setManager(manager);
+            //HERCULES: addition
+            // Put it in the cache
+            sessions.put(session.getIdInternal(), session);     
+            //HERCULES: addition            
+            return (session);
+        } finally {
+            // Close the input stream
+            if (ois != null) {
+                try {
+                    ois.close();
+                } catch (IOException f) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Remove the Session with the specified session identifier from
+     * this Store, if present.  If no such Session is present, this method
+     * takes no action.
+     *
+     * @param id Session identifier of the Session to be removed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void remove(String id) throws IOException {
+
+        File file = file(id);
+        if (file == null) {
+            return;
+        }
+        if (debug >= 1) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.REMOVING_SESSION_FROM_FILE),
+                                              new Object[] {id, file.getAbsolutePath()});
+            log(msg);
+        }
+        //HERCULES: addition
+        // Take it out of the cache 
+        sessions.remove(id);        
+        //HERCULES: addition
+        if (!file.delete() && log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Cannot delete file: " + file);
+        }
+    }
+
+
+    /**
+     * Save the specified Session into this Store.  Any previously saved
+     * information for the associated session identifier is replaced.
+     *
+     * @param session Session to be saved
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void save(Session session) throws IOException {
+
+        // Open an output stream to the specified pathname, if any
+        File file = file(session.getIdInternal());
+        if (file == null) {
+            return;
+        }
+        if (debug >= 1) {
+
+            String msg = MessageFormat.format(rb.getString(LogFacade.SAVING_SESSION_TO_FILE),
+                                              new Object[] {session.getIdInternal(), file.getAbsolutePath()});
+            log(msg);
+        }
+
+        ObjectOutputStream oos = null;
+        try (FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
+             BufferedOutputStream bos = new BufferedOutputStream(fos);
+        ){
+            Container container = manager.getContainer();
+            if (container != null) {
+                oos = ((StandardContext) container).createObjectOutputStream(bos);
+            } else {
+                oos = new ObjectOutputStream(bos);
+            }
+            oos.writeObject(session);
+        } catch (IOException e) {
+            throw e;
+        } finally {
+            if (oos != null ) {
+                try {
+                    oos.close();
+                } catch (IOException ioe) {
+                    //ignore
+                }
+            }
+        }
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Return a File object representing the pathname to our
+     * session persistence directory, if any.  The directory will be
+     * created if it does not already exist.
+     */
+    private File directory() throws IOException {
+
+        if (this.directory == null) {
+            return (null);
+        }
+        if (this.directoryFile != null) {
+            // NOTE:  Race condition is harmless, so do not synchronize
+            return (this.directoryFile);
+        }
+        File file = new File(this.directory);
+        if (!file.isAbsolute()) {
+            Container container = manager.getContainer();
+            if (container instanceof Context) {
+                ServletContext servletContext =
+                    ((Context) container).getServletContext();
+                File work = (File)
+                    servletContext.getAttribute(ServletContext.TEMPDIR);
+                file = new File(work, this.directory);
+            } else {
+                throw new IllegalArgumentException
+                    ("Parent Container is not a Context");
+            }
+        }
+        if (!file.exists() || !file.isDirectory()) {
+            if (!file.delete() && file.exists()) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_DELETE_FILE_EXCEPTION),
+                                                  file);
+                throw new IOException(msg);
+            }
+            if (!file.mkdirs() && !file.isDirectory()) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_CREATE_DIR_EXCEPTION),
+                                                  file);
+                throw new IOException(msg);
+            }
+        }
+        this.directoryFile = file;
+        return (file);
+
+    }
+
+
+    /**
+     * Return a File object representing the pathname to our
+     * session persistence file, if any.
+     *
+     * @param id The ID of the Session to be retrieved. This is
+     *    used in the file naming.
+     */
+    private File file(String id) throws IOException {
+
+        if (this.directory == null) {
+            return (null);
+        }
+        String filename = id + FILE_EXT;
+        File file = new File(directory(), filename);
+        return (file);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/JDBCStore.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/JDBCStore.java
new file mode 100644
index 0000000..7ff5ff3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/JDBCStore.java
@@ -0,0 +1,867 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Loader;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Session;
+import org.apache.catalina.util.CustomObjectInputStream;
+
+import java.io.*;
+import java.sql.*;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+
+/**
+ * Implementation of the <code>Store</code> interface that stores
+ * serialized session objects in a database.  Sessions that are
+ * saved are still subject to being expired based on inactivity.
+ *
+ * @author Bip Thelin
+ * @version $Revision: 1.4 $, $Date: 2006/11/09 01:12:51 $
+ */
+
+public class JDBCStore extends StoreBase {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info = "JDBCStore/1.0";
+
+    /**
+     * Context name associated with this Store
+     */
+    private String name = null;
+
+    /**
+     * Name to register for this Store, used for logging.
+     */
+    protected static final String storeName = "JDBCStore";
+
+    /**
+     * Name to register for the background thread.
+     */
+    protected String threadName = "JDBCStore";
+
+    /**
+     * Connection string to use when connecting to the DB.
+     */
+    protected String connString = null;
+
+    /**
+     * The database connection.
+     */
+    private Connection conn = null;
+
+    /**
+     * Driver to use.
+     */
+    protected String driverName = null;
+
+    // ------------------------------------------------------------- Table & cols
+
+    /**
+     * Table to use.
+     */
+    protected String sessionTable = "tomcat$sessions";
+
+    /**
+     * Column to use for /Engine/Host/Context name
+     */
+    protected String sessionAppCol = "app";
+
+    /**
+     * Id column to use.
+     */
+    protected String sessionIdCol = "id";
+
+    /**
+     * Data column to use.
+     */
+    protected String sessionDataCol = "data";
+
+    /**
+     * Is Valid column to use.
+     */
+    protected String sessionValidCol = "valid";
+
+    /**
+     * Max Inactive column to use.
+     */
+    protected String sessionMaxInactiveCol = "maxinactive";
+
+    /**
+     * Last Accessed column to use.
+     */
+    protected String sessionLastAccessedCol = "lastaccess";
+
+    // ------------------------------------------------------------- SQL Variables
+
+    /**
+     * Variable to hold the <code>getSize()</code> prepared statement.
+     */
+    protected PreparedStatement preparedSizeSql = null;
+
+    /**
+     * Variable to hold the <code>keys()</code> prepared statement.
+     */
+    protected PreparedStatement preparedKeysSql = null;
+
+    /**
+     * Variable to hold the <code>save()</code> prepared statement.
+     */
+    protected PreparedStatement preparedSaveSql = null;
+
+    /**
+     * Variable to hold the <code>clear()</code> prepared statement.
+     */
+    protected PreparedStatement preparedClearSql = null;
+
+    /**
+     * Variable to hold the <code>remove()</code> prepared statement.
+     */
+    protected PreparedStatement preparedRemoveSql = null;
+
+    /**
+     * Variable to hold the <code>load()</code> prepared statement.
+     */
+    protected PreparedStatement preparedLoadSql = null;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the info for this Store.
+     */
+    public String getInfo() {
+        return(info);
+    }
+
+    /**
+     * Return the name for this instance (built from container name)
+     */
+    public String getName() {
+        if (name == null) {
+            Container container = manager.getContainer();
+            String contextName = container.getName();
+            String hostName = "";
+            String engineName = "";
+
+            if (container.getParent() != null) {
+                Container host = container.getParent();
+                hostName = host.getName();
+                if (host.getParent() != null) {
+                    engineName = host.getParent().getName();
+                }
+            }
+            name = "/" + engineName + "/" + hostName + contextName;
+        }
+        return name;
+    }
+
+    /**
+     * Return the thread name for this Store.
+     */
+    public String getThreadName() {
+        return(threadName);
+    }
+
+    /**
+     * Return the name for this Store, used for logging.
+     */
+    public String getStoreName() {
+        return(storeName);
+    }
+
+    /**
+     * Set the driver for this Store.
+     *
+     * @param driverName The new driver
+     */
+    public void setDriverName(String driverName) {
+        String oldDriverName = this.driverName;
+        this.driverName = driverName;
+        support.firePropertyChange("driverName",
+                                   oldDriverName,
+                                   this.driverName);
+        this.driverName = driverName;
+    }
+
+    /**
+     * Return the driver for this Store.
+     */
+    public String getDriverName() {
+        return(this.driverName);
+    }
+
+    /**
+     * Set the Connection URL for this Store.
+     *
+     * @param connectionURL The new Connection URL
+     */
+    public void setConnectionURL(String connectionURL) {
+        String oldConnString = this.connString;
+        this.connString = connectionURL;
+        support.firePropertyChange("connString",
+                                   oldConnString,
+                                   this.connString);
+    }
+
+    /**
+     * Return the Connection URL for this Store.
+     */
+    public String getConnectionURL() {
+        return(this.connString);
+    }
+
+    /**
+     * Set the table for this Store.
+     *
+     * @param sessionTable The new table
+     */
+    public void setSessionTable(String sessionTable) {
+        String oldSessionTable = this.sessionTable;
+        this.sessionTable = sessionTable;
+        support.firePropertyChange("sessionTable",
+                                   oldSessionTable,
+                                   this.sessionTable);
+    }
+
+    /**
+     * Return the table for this Store.
+     */
+    public String getSessionTable() {
+        return(this.sessionTable);
+    }
+
+    /**
+     * Set the App column for the table.
+     *
+     * @param sessionAppCol the column name
+     */
+    public void setSessionAppCol(String sessionAppCol) {
+        String oldSessionAppCol = this.sessionAppCol;
+        this.sessionAppCol = sessionAppCol;
+        support.firePropertyChange("sessionAppCol",
+                                   oldSessionAppCol,
+                                   this.sessionAppCol);
+    }
+
+    /**
+     * Return the web application name column for the table.
+     */
+    public String getSessionAppCol() {
+        return(this.sessionAppCol);
+    }
+
+    /**
+     * Set the Id column for the table.
+     *
+     * @param sessionIdCol the column name
+     */
+    public void setSessionIdCol(String sessionIdCol) {
+        String oldSessionIdCol = this.sessionIdCol;
+        this.sessionIdCol = sessionIdCol;
+        support.firePropertyChange("sessionIdCol",
+                                   oldSessionIdCol,
+                                   this.sessionIdCol);
+    }
+
+    /**
+     * Return the Id column for the table.
+     */
+    public String getSessionIdCol() {
+        return(this.sessionIdCol);
+    }
+
+    /**
+     * Set the Data column for the table
+     *
+     * @param sessionDataCol the column name
+     */
+    public void setSessionDataCol(String sessionDataCol) {
+        String oldSessionDataCol = this.sessionDataCol;
+        this.sessionDataCol = sessionDataCol;
+        support.firePropertyChange("sessionDataCol",
+                                   oldSessionDataCol,
+                                   this.sessionDataCol);
+    }
+
+    /**
+     * Return the data column for the table
+     */
+    public String getSessionDataCol() {
+        return(this.sessionDataCol);
+    }
+
+    /**
+     * Set the Is Valid column for the table
+     *
+     * @param sessionValidCol The column name
+     */
+    public void setSessionValidCol(String sessionValidCol) {
+        String oldSessionValidCol = this.sessionValidCol;
+        this.sessionValidCol = sessionValidCol;
+        support.firePropertyChange("sessionValidCol",
+                                   oldSessionValidCol,
+                                   this.sessionValidCol);
+    }
+
+    /**
+     * Return the Is Valid column
+     */
+    public String getSessionValidCol() {
+        return(this.sessionValidCol);
+    }
+
+    /**
+     * Set the Max Inactive column for the table
+     *
+     * @param sessionMaxInactiveCol The column name
+     */
+    public void setSessionMaxInactiveCol(String sessionMaxInactiveCol) {
+        String oldSessionMaxInactiveCol = this.sessionMaxInactiveCol;
+        this.sessionMaxInactiveCol = sessionMaxInactiveCol;
+        support.firePropertyChange("sessionMaxInactiveCol",
+                                   oldSessionMaxInactiveCol,
+                                   this.sessionMaxInactiveCol);
+    }
+
+    /**
+     * Return the Max Inactive column
+     */
+    public String getSessionMaxInactiveCol() {
+        return(this.sessionMaxInactiveCol);
+    }
+
+    /**
+     * Set the Last Accessed column for the table
+     *
+     * @param sessionLastAccessedCol The column name
+     */
+    public void setSessionLastAccessedCol(String sessionLastAccessedCol) {
+        String oldSessionLastAccessedCol = this.sessionLastAccessedCol;
+        this.sessionLastAccessedCol = sessionLastAccessedCol;
+        support.firePropertyChange("sessionLastAccessedCol",
+                                   oldSessionLastAccessedCol,
+                                   this.sessionLastAccessedCol);
+    }
+
+    /**
+     * Return the Last Accessed column
+     */
+    public String getSessionLastAccessedCol() {
+        return(this.sessionLastAccessedCol);
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Return an array containing the session identifiers of all Sessions
+     * currently saved in this Store.  If there are no such Sessions, a
+     * zero-length array is returned.
+     *
+     * @exception IOException if an input/output error occurred
+     */
+    public String[] keys() throws IOException {
+        String keysSql =
+            "SELECT " + sessionIdCol + " FROM " + sessionTable +
+            " WHERE " + sessionAppCol + " = ?";
+        ResultSet rst = null;
+        String keys[] = null;
+        int i;
+
+        synchronized(this) {
+            Connection _conn = getConnection();
+
+            if(_conn == null) {
+                return(new String[0]);
+            }
+
+            try {
+                if(preparedKeysSql == null) {
+                    preparedKeysSql = _conn.prepareStatement(keysSql);
+                }
+
+                preparedKeysSql.setString(1, getName());
+                rst = preparedKeysSql.executeQuery();
+                ArrayList<String> tmpkeys = new ArrayList<String>();
+                if (rst != null) {
+                    while(rst.next()) {
+                        tmpkeys.add(rst.getString(1));
+                    }
+                }
+                keys = tmpkeys.toArray(new String[tmpkeys.size()]);
+            } catch(SQLException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SQL_ERROR),
+                                                  e);
+                log(msg);
+            } finally {
+                try {
+                    if(rst != null) {
+                        rst.close();
+                    }
+                } catch(SQLException e) {
+                    // Ignore
+                }
+
+                release(_conn);
+            }
+        }
+
+        return(keys);
+    }
+
+    /**
+     * Return an integer containing a count of all Sessions
+     * currently saved in this Store.  If there are no Sessions,
+     * <code>0</code> is returned.
+     *
+     * @exception IOException if an input/output error occurred
+     */
+    public int getSize() throws IOException {
+        int size = 0;
+        String sizeSql = 
+            "SELECT COUNT(" + sessionIdCol + ") FROM " + sessionTable +
+            " WHERE " + sessionAppCol + " = ?";
+        ResultSet rst = null;
+
+        synchronized(this) {
+            Connection _conn = getConnection();
+
+            if(_conn == null) {
+                return(size);
+            }
+
+            try {
+                if(preparedSizeSql == null) {
+                    preparedSizeSql = _conn.prepareStatement(sizeSql);
+                }
+
+                preparedSizeSql.setString(1, getName());
+                rst = preparedSizeSql.executeQuery();
+                if (rst.next()) {
+                    size = rst.getInt(1);
+                }
+            } catch(SQLException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SQL_ERROR),
+                                                  e);
+                log(msg);
+            } finally {
+                try {
+                    if(rst != null)
+                        rst.close();
+                } catch(SQLException e) {
+                    // Ignore
+                }
+
+                release(_conn);
+            }
+        }
+        return(size);
+    }
+
+    /**
+     * Load the Session associated with the id <code>id</code>.
+     * If no such session is found <code>null</code> is returned.
+     *
+     * @param id a value of type <code>String</code>
+     * @return the stored <code>Session</code>
+     * @exception ClassNotFoundException if an error occurs
+     * @exception IOException if an input/output error occurred
+     */
+    public Session load(String id)
+        throws ClassNotFoundException, IOException {
+        ResultSet rst = null;
+        StandardSession _session = null;
+        Loader loader = null;
+        ClassLoader classLoader = null;
+        ObjectInputStream ois = null;
+        BufferedInputStream bis = null;
+        Container container = manager.getContainer();
+        String loadSql =
+            "SELECT " + sessionIdCol + ", " + sessionDataCol + " FROM " +
+            sessionTable + " WHERE " + sessionIdCol + " = ? AND " +
+            sessionAppCol + " = ?";
+
+        synchronized(this) {
+            Connection _conn = getConnection();
+            if(_conn == null) {
+                return(null);
+            }
+
+            try {
+                if(preparedLoadSql == null) {
+                    preparedLoadSql = _conn.prepareStatement(loadSql);
+                }
+
+                preparedLoadSql.setString(1, id);
+                preparedLoadSql.setString(2, getName());
+                rst = preparedLoadSql.executeQuery();
+                if (rst.next()) {
+                    bis = new BufferedInputStream(rst.getBinaryStream(2));
+
+                    if (container != null) {
+                        loader = container.getLoader();
+                    }
+                    if (loader != null) {
+                        classLoader = loader.getClassLoader();
+                    }
+                    if (classLoader != null) {
+                        ois = new CustomObjectInputStream(bis,
+                                                          classLoader);
+                    } else {
+                        ois = new ObjectInputStream(bis);
+                    }
+
+                    if (debug > 0) {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.LOADING_SESSION_FROM_DATABASE),
+                                                          new Object[] {id, sessionTable});
+                        log(msg);
+                    }
+
+                    _session = StandardSession.deserialize(ois, manager);
+                    _session.setManager(manager);
+
+                } else if (debug > 0) {
+                    log(getStoreName()+": No persisted data object found");
+                }
+            } catch(SQLException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SQL_ERROR),
+                                                  e);
+                log(msg);
+            } finally {
+                try {
+                    if(rst != null) {
+                        rst.close();
+                    }
+                } catch(SQLException e) {
+                    // Ignore
+                }
+                if (ois != null) {
+                    try {
+                        ois.close();
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                }
+                release(_conn);
+            }
+        }
+
+        return(_session);
+    }
+
+    /**
+     * Remove the Session with the specified session identifier from
+     * this Store, if present.  If no such Session is present, this method
+     * takes no action.
+     *
+     * @param id Session identifier of the Session to be removed
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void remove(String id) throws IOException {
+        String removeSql =
+            "DELETE FROM " + sessionTable + " WHERE " + sessionIdCol +
+            " = ?  AND " + sessionAppCol + " = ?";
+
+        synchronized(this) {
+            Connection _conn = getConnection();
+
+            if(_conn == null) {
+                return;
+            }
+
+            try {
+                if(preparedRemoveSql == null) {
+                    preparedRemoveSql = _conn.prepareStatement(removeSql);
+                }
+
+                preparedRemoveSql.setString(1, id);
+                preparedRemoveSql.setString(2, getName());
+                preparedRemoveSql.execute();
+            } catch(SQLException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SQL_ERROR),
+                                                  e);
+                log(msg);
+            } finally {
+                release(_conn);
+            }
+        }
+
+        if (debug > 0) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.REMOVING_SESSION_FROM_DATABASE),
+                                              new Object[] {id, sessionTable});
+            log(msg);
+        }
+    }
+
+    /**
+     * Remove all of the Sessions in this Store.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void clear() throws IOException {
+        String clearSql =
+            "DELETE FROM " + sessionTable + " WHERE " + sessionAppCol + " = ?";
+
+        synchronized(this) {
+            Connection _conn = getConnection();
+            if(_conn == null) {
+                return;
+            }
+
+            try {
+                if(preparedClearSql == null) {
+                    preparedClearSql = _conn.prepareStatement(clearSql);
+                }
+
+                preparedClearSql.setString(1, getName());
+                preparedClearSql.execute();
+            } catch(SQLException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SQL_ERROR),
+                                                  e);
+                log(msg);
+            } finally {
+                release(_conn);
+            }
+        }
+    }
+
+    /**
+     * Save a session to the Store.
+     *
+     * @param session the session to be stored
+     * @exception IOException if an input/output error occurs
+     */
+    public void save(Session session) throws IOException {
+        String saveSql =
+            "INSERT INTO " + sessionTable + " (" + sessionIdCol + ", " +
+            sessionAppCol + ", " +
+            sessionDataCol + ", " +
+            sessionValidCol + ", " +
+            sessionMaxInactiveCol + ", " +
+            sessionLastAccessedCol + ") VALUES (?, ?, ?, ?, ?, ?)";
+        ObjectOutputStream oos = null;
+        ByteArrayOutputStream bos = null;
+        ByteArrayInputStream bis = null;
+        InputStream in = null;
+
+        synchronized(this) {
+            Connection _conn = getConnection();
+            if(_conn == null) {
+                return;
+            }
+
+            // If sessions already exist in DB, remove and insert again.
+            // TODO:
+            // * Check if ID exists in database and if so use UPDATE.
+            remove(session.getIdInternal());
+
+            try {
+                bos = new ByteArrayOutputStream();
+                oos = new ObjectOutputStream(new BufferedOutputStream(bos));
+
+                oos.writeObject(session);
+                oos.close();
+
+                byte[] obs = bos.toByteArray();
+                int size = obs.length;
+                bis = new ByteArrayInputStream(obs, 0, size);
+                in = new BufferedInputStream(bis, size);
+
+                if(preparedSaveSql == null) {
+                    preparedSaveSql = _conn.prepareStatement(saveSql);
+                }
+
+                preparedSaveSql.setString(1, session.getIdInternal());
+                preparedSaveSql.setString(2, getName());
+                preparedSaveSql.setBinaryStream(3, in, size);
+                preparedSaveSql.setString(4, session.isValid()?"1":"0");
+                preparedSaveSql.setInt(5, session.getMaxInactiveInterval());
+                preparedSaveSql.setLong(6, session.getLastAccessedTime());
+                preparedSaveSql.execute();
+            } catch(SQLException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SQL_ERROR),
+                                                  e);
+                log(msg);
+            } catch (IOException e) {
+                // Ignore
+            } finally {
+                if (oos != null) {
+                    oos.close();
+                }
+                if(bis != null) {
+                    bis.close();
+                }
+                if(in != null) {
+                    in.close();
+                }
+
+                release(_conn);
+            }
+        }
+
+        if (debug > 0) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SAVING_SESSION_TO_DATABASE),
+                                              new Object[] {session.getIdInternal(), sessionTable});
+            log(msg);
+        }
+    }
+
+    // --------------------------------------------------------- Protected Methods
+
+    /**
+     * Check the connection associated with this store, if it's
+     * <code>null</code> or closed try to reopen it.
+     * Returns <code>null</code> if the connection could not be established.
+     *
+     * @return <code>Connection</code> if the connection succeeded
+     */
+    protected Connection getConnection(){
+        try {
+            if(conn == null || conn.isClosed()) {
+                Class.forName(driverName);
+                String databaseConnClosedMsg = rb.getString(LogFacade.DATABASE_CONNECTION_CLOSED);
+                log(databaseConnClosedMsg);
+                conn = DriverManager.getConnection(connString);
+                conn.setAutoCommit(true);
+
+                if(conn == null || conn.isClosed()) {
+                    String openDatabaseFailedMsg = rb.getString(LogFacade.RE_OPEN_DATABASE_FAILED);
+                    log(openDatabaseFailedMsg);
+                }
+            }
+        } catch (SQLException ex){
+            String msg = MessageFormat.format(rb.getString(LogFacade.SQL_EXCEPTION),
+                                              ex.toString());
+            log(msg);
+        } catch (ClassNotFoundException ex) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.JDBC_DRIVER_CLASS_NOT_FOUND),
+                                              ex.toString());
+            log(msg);
+        }
+
+        return conn;
+    }
+
+    /**
+     * Release the connection, not needed here since the
+     * connection is not associated with a connection pool.
+     *
+     * @param conn The connection to be released
+     */
+    protected void release(Connection conn) {
+        // NOOP
+    }
+
+    /**
+     * Called once when this Store is first started.
+     */
+    public void start() throws LifecycleException {
+        super.start();
+
+        // Open connection to the database
+        this.conn = getConnection();
+    }
+
+    /**
+     * Gracefully terminate everything associated with our db.
+     * Called once when this Store is stoping.
+     *
+     */
+    public void stop() throws LifecycleException {
+        super.stop();
+
+        // Close and release everything associated with our db.
+        if(conn != null) {
+            try {
+                conn.commit();
+            } catch (SQLException e) {
+                // Ignore
+            }
+
+            if( preparedSizeSql != null ) {
+                try {
+                    preparedSizeSql.close();
+                } catch (SQLException e) {
+                    // Ignore
+                }
+            }
+
+            if( preparedKeysSql != null ) { 
+                try {
+                    preparedKeysSql.close();
+                } catch (SQLException e) {
+                    // Ignore
+                }
+            }
+
+            if( preparedSaveSql != null ) { 
+                try {
+                    preparedSaveSql.close();
+                } catch (SQLException e) {
+                    // Ignore
+                }
+            }
+
+            if( preparedClearSql != null ) { 
+                try {
+                    preparedClearSql.close();
+                } catch (SQLException e) {
+                    // Ignore
+                }
+            }
+
+            if( preparedRemoveSql != null ) { 
+                try {
+                    preparedRemoveSql.close();
+                } catch (SQLException e) {
+                    // Ignore
+                }
+            }
+
+            if( preparedLoadSql != null ) { 
+                try {
+                    preparedLoadSql.close();
+                } catch (SQLException e) {
+                    // Ignore
+                }
+            }
+
+            try {
+                conn.close();
+            } catch (SQLException e) {
+                // Ignore
+            }
+
+            this.preparedSizeSql = null;
+            this.preparedKeysSql = null;
+            this.preparedSaveSql = null;
+            this.preparedClearSql = null;
+            this.preparedRemoveSql = null;
+            this.preparedLoadSql = null;
+            this.conn = null;
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/ManagerBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/ManagerBase.java
new file mode 100644
index 0000000..c008775
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/ManagerBase.java
@@ -0,0 +1,1258 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+import com.sun.enterprise.util.uuid.UuidGenerator;
+import com.sun.enterprise.util.uuid.UuidGeneratorImpl;
+import org.apache.catalina.*;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.*;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+//end HERCULES:added
+
+
+/**
+ * Minimal implementation of the <b>Manager</b> interface that supports
+ * no session persistence or distributable capabilities.  This class may
+ * be subclassed to create more sophisticated Manager implementations.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.23.2.3 $ $Date: 2008/04/17 18:37:20 $
+ */
+
+public abstract class ManagerBase implements Manager {
+    protected static final Logger log = LogFacade.getLogger();
+    protected static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------- Instance Variables
+
+    protected DataInputStream randomIS=null;
+    protected String devRandomSource="/dev/urandom";
+
+    /**
+     * The Container with which this Manager is associated.
+     */
+    protected Container container;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    protected int debug = 0;
+
+
+    /**
+     * The distributable flag for Sessions created by this Manager.  If this
+     * flag is set to <code>true</code>, any user attributes added to a
+     * session controlled by this Manager must be Serializable.
+     */
+    protected boolean distributable;
+
+
+    /**
+     * A String initialization parameter used to increase the entropy of
+     * the initialization of our random number generator.
+     */
+    protected String entropy = null;
+    
+    //START OF 6364900
+    /**
+     * A SessionLocker used to lock sessions (curently only
+     * in the request dispatcher forward/include use case)
+     */
+    protected SessionLocker sessionLocker = new BaseSessionLocker();
+    //END OF 6364900    
+
+    /**
+     * The descriptive information string for this implementation.
+     */
+    private static final String info = "ManagerBase/1.0";
+
+
+    /**
+     * The default maximum inactive interval for Sessions created by
+     * this Manager.
+     */
+    protected int maxInactiveInterval = 60;
+
+
+    /**
+     * The session id length of Sessions created by this Manager.
+     */
+    protected int sessionIdLength = 16;
+
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    protected static final String name = "ManagerBase";
+
+
+    /**
+     * A random number generator to use when generating session identifiers.
+     */
+    private Random random = null;
+    
+    
+    /**
+     * The Uuid Generator to be used
+     * when generating universally unique session identifiers.
+     * HERCULES: add
+     */
+    protected UuidGenerator uuidGenerator = new UuidGeneratorImpl();     
+
+
+    /**
+     * The Java class name of the random number generator class to be used
+     * when generating session identifiers.
+     */
+    protected String randomClass = "java.security.SecureRandom";
+
+
+    /**
+     * The longest time (in seconds) that an expired session had been alive.
+     */
+    protected int sessionMaxAliveTime;
+
+
+    /**
+     * Average time (in seconds) that expired sessions had been alive.
+     */
+    protected int sessionAverageAliveTime;
+
+
+    /**
+     * Number of sessions that have expired.
+     */
+    protected int expiredSessions = 0;
+
+
+    /**
+     * The set of currently active Sessions for this Manager, keyed by
+     * session identifier.
+     */
+    protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
+    
+    // Number of sessions created by this manager
+    protected int sessionCounter=0;
+
+    protected volatile int maxActive=0;
+    
+    protected final Object maxActiveUpdateLock = new Object();
+
+    // number of duplicated session ids - anything >0 means we have problems
+    protected int duplicates=0;
+
+    protected boolean initialized=false;
+
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+    
+    /**
+     * Number of times a session was not created because the maximum number
+     * of active sessions had been reached.
+     */
+    protected int rejectedSessions = 0;
+
+
+    // ------------------------------------------------------- Security classes
+    private class PrivilegedSetRandomFile implements PrivilegedAction<DataInputStream>{
+        
+        public DataInputStream run(){               
+            FileInputStream fileInputStream = null;
+            try {
+                File f=new File( devRandomSource );
+                if( ! f.exists() ) return null;
+                fileInputStream = new FileInputStream(f);
+                randomIS= new DataInputStream( fileInputStream );
+                randomIS.readLong();
+                if( log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Opening " + devRandomSource);
+                return randomIS;
+            } catch (IOException ex){
+                return null;
+            } finally{
+                try{
+                    if ( fileInputStream != null )
+                        fileInputStream.close();
+                } catch (IOException ex){
+                    ;
+                }
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------- Properties
+    
+    /**
+     * Return the UuidGenerator for this Manager.
+     * HERCULES:added
+     */
+    public UuidGenerator getUuidGenerator() {
+        return uuidGenerator;
+    }
+    
+    /**
+     * Set the UuidGenerator for this Manager.
+     * HERCULES:added
+     */
+    public void setUuidGenerator(UuidGenerator aUuidGenerator) {
+        uuidGenerator = aUuidGenerator;
+    }
+
+
+    /**
+     * Return the Container with which this Manager is associated.
+     */
+    public Container getContainer() {
+        return container;
+    }
+
+
+    /**
+     * Set the Container with which this Manager is associated.
+     *
+     * @param container The newly associated Container
+     */
+    public void setContainer(Container container) {
+        Container oldContainer = this.container;
+        this.container = container;
+        support.firePropertyChange("container", oldContainer, this.container);
+        // TODO: find a good scheme for the log names
+        //log=LogFactory.getLog("tomcat.manager." + container.getName());
+    }
+
+
+    /**
+     * 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;
+    }
+
+    /** Returns the name of the implementation class.
+     */
+    public String getClassName() {
+        return this.getClass().getName();
+    }
+
+
+    /**
+     * Return the distributable flag for the sessions supported by
+     * this Manager.
+     */
+    public boolean getDistributable() {
+        return distributable;
+    }
+
+
+    /**
+     * Set the distributable flag for the sessions supported by this
+     * Manager.  If this flag is set, all user data objects added to
+     * sessions associated with this manager must implement Serializable.
+     *
+     * @param distributable The new distributable flag
+     */
+    public void setDistributable(boolean distributable) {
+        boolean oldDistributable = this.distributable;
+        this.distributable = distributable;
+        support.firePropertyChange("distributable",
+                                   Boolean.valueOf(oldDistributable),
+                                   Boolean.valueOf(this.distributable));
+    }
+
+
+    /**
+     * Return the entropy increaser value, or compute a semi-useful value
+     * if this String has not yet been set.
+     */
+    public String getEntropy() {
+        // Calculate a semi-useful value if this has not been set
+        if (this.entropy == null)
+            setEntropy(this.toString());
+        return (this.entropy);
+    }
+
+
+    /**
+     * Set the entropy increaser value.
+     *
+     * @param entropy The new entropy increaser value
+     */
+    public void setEntropy(String entropy) {
+        String oldEntropy = entropy;
+        this.entropy = entropy;
+        support.firePropertyChange("entropy", oldEntropy, this.entropy);
+    }
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return info;
+    }
+
+
+    /**
+     * Same as getMaxInactiveIntervalSeconds
+     */
+    public int getMaxInactiveInterval() {
+        return getMaxInactiveIntervalSeconds();
+    }
+
+
+    /**
+     * Return the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     */
+    public int getMaxInactiveIntervalSeconds() {
+        return maxInactiveInterval;
+    }
+
+
+    /**
+     * Same as setMaxInactiveIntervalSeconds
+     */
+    public void setMaxInactiveInterval(int interval) {
+        setMaxInactiveIntervalSeconds(interval);
+    }
+
+
+    /**
+     * Set the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     *
+     * @param interval The new default value
+     */
+    public void setMaxInactiveIntervalSeconds(int interval) {
+        int oldMaxInactiveInterval = this.maxInactiveInterval;
+        this.maxInactiveInterval = interval;
+        support.firePropertyChange("maxInactiveInterval",
+                                   Integer.valueOf(oldMaxInactiveInterval),
+                                   Integer.valueOf(this.maxInactiveInterval));
+    }
+
+
+    /**
+     * Gets the session id length (in bytes) of Sessions created by
+     * this Manager.
+     *
+     * @return The session id length
+     */
+    public int getSessionIdLength() {
+        return sessionIdLength;
+    }
+
+
+    /**
+     * Sets the session id length (in bytes) for Sessions created by this
+     * Manager.
+     *
+     * @param idLength The session id length
+     */
+    public void setSessionIdLength(int idLength) {
+
+        int oldSessionIdLength = this.sessionIdLength;
+        this.sessionIdLength = idLength;
+        support.firePropertyChange("sessionIdLength",
+                                   Integer.valueOf(oldSessionIdLength),
+                                   Integer.valueOf(this.sessionIdLength));
+
+    }
+
+
+    /**
+     * Gets the number of session creations that failed due to
+     * maxActiveSessions
+     *
+     * @return number of session creations that failed due to
+     * maxActiveSessions
+     */
+    public int getRejectedSessions() {
+        return rejectedSessions;
+    }
+
+
+    /**
+     * Sets the number of sessions that were not created because the maximum
+     * number of active sessions was reached.
+     *
+     * @param rejectedSessions Number of rejected sessions
+     */
+    public void setRejectedSessions(int rejectedSessions) {
+        this.rejectedSessions = rejectedSessions;
+    }
+
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+
+    /**
+     * Use /dev/random-type special device. This is new code, but may reduce
+     * the big delay in generating the random.
+     *
+     *  You must specify a path to a random generator file. Use /dev/urandom
+     *  for linux ( or similar ) systems. Use /dev/random for maximum security
+     *  ( it may block if not enough "random" exist ). You can also use
+     *  a pipe that generates random.
+     *
+     *  The code will check if the file exists, and default to java Random
+     *  if not found. There is a significant performance difference, very
+     *  visible on the first call to getSession ( like in the first JSP )
+     *  - so use it if available.
+     */
+    public void setRandomFile( String s ) {
+    // as a hack, you can use a static file - and genarate the same
+    // session ids ( good for strange debugging )
+        if (Globals.IS_SECURITY_ENABLED){
+            randomIS = AccessController.doPrivileged(new PrivilegedSetRandomFile());
+        } else {
+            FileInputStream fileInputStream = null;
+            try{
+                devRandomSource=s;
+                File f=new File( devRandomSource );
+                if( ! f.exists() ) return;
+                fileInputStream = new FileInputStream(f);
+                randomIS= new DataInputStream( fileInputStream);
+                randomIS.readLong();
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE,  "Opening " + devRandomSource );
+            } catch( IOException ex ) {
+                randomIS=null;
+            } finally {
+                try{
+                    if ( fileInputStream != null )
+                        fileInputStream.close();
+                } catch (IOException ex){
+                    ;
+                }
+            }
+        }
+    }
+
+    public String getRandomFile() {
+        return devRandomSource;
+    }
+
+
+    /**
+     * Return the random number generator instance we should use for
+     * generating session identifiers.  If there is no such generator
+     * currently defined, construct and seed a new one.
+     */
+    public synchronized Random getRandom() {
+        if (this.random == null) {
+            // Calculate the new random number generator seed
+            long seed = System.currentTimeMillis();
+            long t1 = seed;
+            char entropy[] = getEntropy().toCharArray();
+            for (int i = 0; i < entropy.length; i++) {
+                 long update = ((byte) entropy[i]) << ((i % 8) * 8);
+                 seed ^= update;
+            }
+            try {
+                 // Construct and seed a new random number generator
+                 Class<?> clazz = Class.forName(randomClass);
+                 this.random = (Random) clazz.newInstance();
+                 this.random.setSeed(seed);
+            } catch (Exception e) {
+                 // Fall back to the simple case
+                String msg = MessageFormat.format(rb.getString(LogFacade.INIT_RANDOM_NUMBER_GENERATOR_EXCEPTION),
+                                                  randomClass);
+                 log.log(Level.SEVERE, msg, e);
+                 this.random = new java.util.Random();
+                 this.random.setSeed(seed);
+            }
+            long t2=System.currentTimeMillis();
+            if( (t2-t1) > 100 )
+                 if (log.isLoggable(Level.FINE)) {
+                     String msg = MessageFormat.format(rb.getString(LogFacade.SEEDING_RANDOM_NUMBER_GENERATOR_CLASS),
+                                                       randomClass);
+                     log.log(Level.FINE, msg + " " + (t2-t1));
+                 }
+        }
+
+        return (this.random);
+    }
+
+    /**
+     * Reset the random number generator instance to null.
+     */
+    protected synchronized void resetRandom() {
+        this.random = null;
+    }
+
+
+    /**
+     * Return the random number generator class name.
+     */
+    public String getRandomClass() {
+        return randomClass;
+    }
+
+
+    /**
+     * Set the random number generator class name.
+     *
+     * @param randomClass The new random number generator class name
+     */
+    public void setRandomClass(String randomClass) {
+        String oldRandomClass = this.randomClass;
+        this.randomClass = randomClass;
+        support.firePropertyChange("randomClass", oldRandomClass,
+                                   this.randomClass);
+    }
+
+
+    /**
+     * Gets the number of sessions that have expired.
+     *
+     * @return Number of sessions that have expired
+     */
+    public int getExpiredSessions() {
+        return expiredSessions;
+    }
+
+
+    /**
+     * Sets the number of sessions that have expired.
+     *
+     * @param expiredSessions Number of sessions that have expired
+     */
+    public void setExpiredSessions(int expiredSessions) {
+        this.expiredSessions = expiredSessions;
+    }
+
+    //START OF 6364900
+    /**
+     * set the pluggable sessionLocker for this manager
+     * by default it is pre-set to no-op BaseSessionLocker
+     */    
+    public void setSessionLocker(SessionLocker sessLocker) {
+        sessionLocker = sessLocker;
+    }
+    //END OF 6364900
+    
+    // --------------------------------------------------------- Public Methods
+    public void destroy() {
+        if (randomIS!=null) {
+            try {
+                randomIS.close();
+            } catch (IOException ioe) {
+                if (log.isLoggable(Level.WARNING)) {
+                    log.log(Level.WARNING, LogFacade.FAILED_CLOSE_RANDOMIS_EXCEPTION);
+                }
+            }
+            randomIS=null;
+        }
+        initialized=false;
+        oname = null;
+    }
+    
+    public void init() {
+        if( initialized ) return;
+        initialized=true;        
+        
+        if( oname==null ) {
+            try {
+                StandardContext ctx=(StandardContext)this.getContainer();
+                domain=ctx.getEngineName();
+                distributable = ctx.getDistributable();
+                StandardHost hst=(StandardHost)ctx.getParent();
+                String path = ctx.getEncodedPath();
+                if (path.equals("")) {
+                    path = "/";
+                }   
+                oname=new ObjectName(domain + ":type=Manager,path="
+                + path + ",host=" + hst.getName());
+            } catch (Exception e) {
+                log.log(Level.SEVERE, LogFacade.ERROR_REGISTERING_EXCEPTION_SEVERE, e);
+            }
+        }
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Registering " + oname );
+        }
+    }
+
+
+    /**
+     * Add this Session to the set of active Sessions for this Manager.
+     *
+     * @param session Session to be added
+     */
+    public void add(Session session) {
+        sessions.put(session.getIdInternal(), session);
+        int size = sessions.size();
+        if (size > maxActive) {
+            synchronized(maxActiveUpdateLock) {
+                if( size > maxActive ) {
+                    maxActive = size;
+                }
+            }
+        }
+    }
+    
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        support.addPropertyChangeListener(listener);
+    }
+
+
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id will be assigned by this method, and available via the getId()
+     * method of the returned session.  If a new session cannot be created
+     * for any reason, return <code>null</code>.
+     * Hercules: modified
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    public Session createSession() {
+        
+        // Recycle or create a Session instance
+        Session session = null;
+        session = createEmptySession();
+        //always lock
+        session.lockForeground(); 
+
+        // Initialize the properties of the new session and return it
+        session.setNew(true);
+        session.setValid(true);
+        session.setCreationTime(System.currentTimeMillis());
+        session.setMaxInactiveInterval(this.maxInactiveInterval);
+        String sessionId = generateSessionId(session);
+
+        session.setId(sessionId);
+        sessionCounter++;
+
+        return (session);
+
+    }
+
+
+    // START S1AS8PE 4817642
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties, using the specified
+     * session id.
+     *
+     * IMPLEMENTATION NOTE: This method must be kept in sync with the
+     * createSession method that takes no arguments.
+     *
+     * @param sessionId the session id to assign to the new session
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     *
+     * @return the new session, or <code>null</code> if a session with the
+     * requested id already exists
+     */
+    public Session createSession(String sessionId) {
+
+        // Recycle or create a Session instance
+        Session session = createEmptySession();
+
+        // Initialize the properties of the new session and return it
+        session.setNew(true);
+        session.setValid(true);
+        session.setCreationTime(System.currentTimeMillis());
+        session.setMaxInactiveInterval(this.maxInactiveInterval);
+
+        //START OF 6364900
+        //always lock
+        session.lockForeground();        
+        //END OF 6364900        
+
+        session.setId(sessionId);
+        sessionCounter++;
+
+        return (session);
+
+    }
+    // END S1AS8PE 4817642
+
+
+    /**
+     * Get a session from the recycled ones or create a new empty one.
+     * The PersistentManager manager does not need to create session data
+     * because it reads it from the Store.
+     */
+    public Session createEmptySession() {
+        return (getNewSession());
+    }
+
+    @Override
+    public void checkSessionAttribute(String name, Object value) {
+        if (getDistributable() && !StandardSession.isSerializable(value)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.NON_SERIALIZABLE_ATTRIBUTE_EXCEPTION),
+                                              name);
+            throw new IllegalArgumentException(msg);
+        }
+    }
+
+    /**
+     * Return the active Session, associated with this Manager, with the
+     * specified session id (if any); otherwise return <code>null</code>.
+     *
+     * @param id The session id for the session to be returned
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    @Override
+    public Session findSession(String id) throws IOException {
+        if (id == null) {
+            return (null);
+        }
+        return sessions.get(id);
+    }
+
+    @Override
+    public Session findSession(String id, HttpServletRequest request) throws IOException {
+        return findSession(id);
+    }
+
+    /**
+     * Finds and returns the session with the given id that also satisfies
+     * the given version requirement.
+     *
+     * This overloaded version of findSession() will be invoked only if
+     * isSessionVersioningSupported() returns true. By default, this method
+     * delegates to the version of findSession() that does not take any
+     * session version number.
+     *
+     * @param id The session id to match
+     * @param version The session version requirement to satisfy
+     *
+     * @return The session that matches the given id and also satisfies the
+     * given version requirement, or null if no such session could be found
+     * by this session manager
+     *
+     * @exception IOException if an IO error occurred
+     */
+    @Override
+    public Session findSession(String id, String version) throws IOException {
+        return findSession(id);
+    }
+
+    /**
+     * Returns true if this session manager supports session versioning, false
+     * otherwise.
+     *
+     * @return true if this session manager supports session versioning, false
+     * otherwise.
+     */
+    @Override
+    public boolean isSessionVersioningSupported() {
+        return false;
+    }
+
+    /**
+     * clear out the sessions cache
+     * HERCULES:added
+     */
+    public void clearSessions() {
+        sessions.clear();
+    }    
+
+
+    /**
+     * Return the set of active Sessions associated with this Manager.
+     * If this Manager has no active Sessions, a zero-length array is returned.
+     */
+    public Session[] findSessions() {
+        // take a snapshot
+        Collection<Session> sessionsValues = sessions.values();
+        List<Session> list = new ArrayList<Session>(sessionsValues.size());
+        for (Session session : sessionsValues) {
+            list.add(session);
+        }
+        return list.toArray(new Session[list.size()]);
+    }
+
+
+    /**
+     * Remove this Session from the active Sessions for this Manager.
+     *
+     * @param session Session to be removed
+     */
+    public void remove(Session session) {
+        sessions.remove(session.getIdInternal());
+    }
+
+    @Override
+    public Cookie toCookie(Session session) throws IOException {
+        return null;
+    }
+                                 
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        support.removePropertyChangeListener(listener);
+    }
+
+
+    /**
+     * Change the session ID of the current session to a new randomly generated
+     * session ID.
+     * 
+     * @param session   The session to change the session ID for
+     */
+    public void changeSessionId(Session session) {
+        session.setId(generateSessionId());
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Get new session class to be used in the doLoad() method.
+     */
+    protected StandardSession getNewSession() {
+        return new StandardSession(this);
+    }
+
+
+    protected void getRandomBytes( byte bytes[] ) {
+        // Generate a byte array containing a session identifier
+        if( devRandomSource!=null && randomIS==null ) {
+            setRandomFile( devRandomSource );
+        }
+        if(randomIS!=null ) {
+            try {
+                int len=randomIS.read( bytes );
+                if( len==bytes.length ) {
+                    return;
+                }
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, "Got " + len + " " + bytes.length);
+                }
+            } catch( Exception ex ) {
+            }
+            devRandomSource=null;
+            randomIS=null;
+        }
+        getRandom().nextBytes(bytes);
+    }
+    
+    
+    /**
+     * Generate and return a new session identifier.
+     * Hercules:added
+     */
+    protected synchronized String generateSessionId(Object obj) {
+        return uuidGenerator.generateUuid(obj);
+    }   
+    
+    /**
+     * Generate and return a new session identifier.
+     * Hercules:modified
+     */
+    protected synchronized String generateSessionId() {
+        return generateSessionId(new Object());
+    }    
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Retrieve the enclosing Engine for this Manager.
+     *
+     * @return an Engine object (or null).
+     */
+    public Engine getEngine() {
+        Engine e = null;
+        for (Container c = getContainer(); e == null && c != null ; c = c.getParent()) {
+            if (c instanceof Engine) {
+                e = (Engine)c;
+            }
+        }
+        return e;
+    }
+
+
+    /**
+     * Retrieve the JvmRoute for the enclosing Engine.
+     * @return the JvmRoute or null.
+     */
+    public String getJvmRoute() {
+        Engine e = getEngine();
+        return e == null ? null : e.getJvmRoute();
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @deprecated
+     */
+    protected void log(String message) {
+        log.log(Level.INFO, neutralizeForLog(message));
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @param throwable Associated exception
+     * @deprecated
+     */
+    protected void log(String message, Throwable throwable) {
+        log.log(Level.INFO, neutralizeForLog(message), throwable);
+    }
+
+
+    /**
+     * Same as setSessionCount
+     */
+    public void setSessionCounter(int sessionCounter) {
+        setSessionCount(sessionCounter);
+    }
+
+   
+    public void setSessionCount(int sessionCounter) {
+        this.sessionCounter = sessionCounter;
+    }
+
+
+    /** 
+     * Same as getSessionCount
+     */
+    public int getSessionCounter() {
+        return getSessionCount();
+    }
+
+
+    /** 
+     * Total sessions created by this manager.
+     *
+     * @return sessions created
+     */
+    public int getSessionCount() {
+        return sessionCounter;
+    }
+
+
+    /** 
+     * Number of duplicated session IDs generated by the random source.
+     * Anything bigger than 0 means problems.
+     *
+     * @return
+     */
+    public int getDuplicates() {
+        return duplicates;
+    }
+
+
+    public void setDuplicates(int duplicates) {
+        this.duplicates = duplicates;
+    }
+
+
+    /** 
+     * Returns the number of active sessions
+     *
+     * @return number of sessions active
+     */
+    public int getActiveSessions() {
+        return sessions.size();
+    }
+
+
+    /**
+     * Max number of concurent active sessions
+     *
+     * @return
+     */
+    public int getMaxActive() {
+        return maxActive;
+    }
+
+
+    public void setMaxActive(int maxActive) {
+        synchronized (maxActiveUpdateLock) {
+            this.maxActive = maxActive;
+        }
+    }
+
+
+    /**
+     * Same as getSessionMaxAliveTimeSeconds
+     */
+    public int getSessionMaxAliveTime() {
+        return getSessionMaxAliveTimeSeconds();
+    }
+
+
+    /**
+     * Gets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @return Longest time (in seconds) that an expired session had been
+     * alive.
+     */
+    public int getSessionMaxAliveTimeSeconds() {
+        return sessionMaxAliveTime;
+    }
+
+
+    /**
+     * Same as setSessionMaxAliveTimeSeconds
+     */
+    public void setSessionMaxAliveTime(int sessionMaxAliveTime) {
+        setSessionMaxAliveTimeSeconds(sessionMaxAliveTime);
+    }
+
+
+    /**
+     * Sets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @param sessionMaxAliveTime Longest time (in seconds) that an expired
+     * session had been alive.
+     */
+    public void setSessionMaxAliveTimeSeconds(int sessionMaxAliveTime) {
+        this.sessionMaxAliveTime = sessionMaxAliveTime;
+    }
+
+
+    /**
+     * Same as getSessionAverageAliveTimeSeconds
+     */
+    public int getSessionAverageAliveTime() {
+        return getSessionAverageAliveTimeSeconds();
+    }
+
+
+    /**
+     * Gets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @return Average time (in seconds) that expired sessions had been
+     * alive.
+     */
+    public int getSessionAverageAliveTimeSeconds() {
+        return sessionAverageAliveTime;
+    }
+
+
+    /**
+     * Same as setSessionAverageAliveTimeSeconds
+     */
+    public void setSessionAverageAliveTime(int sessionAverageAliveTime) {
+        setSessionAverageAliveTimeSeconds(sessionAverageAliveTime);
+    }
+
+
+    /**
+     * Sets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @param sessionAverageAliveTime Average time (in seconds) that expired
+     * sessions had been alive.
+     */
+    public void setSessionAverageAliveTimeSeconds(int sessionAverageAliveTime) {
+        this.sessionAverageAliveTime = sessionAverageAliveTime;
+    }
+
+
+    /** 
+     * For debugging: return a list of all session ids currently active
+     *
+     */
+    public String listSessionIds() {
+        StringBuilder sb=new StringBuilder();
+        Iterator<String> keys=sessions.keySet().iterator();
+        while( keys.hasNext() ) {
+            sb.append(keys.next()).append(" ");
+        }
+        return sb.toString();
+    }
+
+
+    /** 
+     * For debugging: get a session attribute
+     *
+     * @param sessionId
+     * @param key
+     * @return
+     */
+    public String getSessionAttribute( String sessionId, String key ) {
+        Session s = sessions.get(sessionId);
+        if( s==null ) {
+            /*
+            Do not log session ID
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.SESSION_NOT_FOUND, sessionId);
+            }
+            */
+            return null;
+        }
+        Object o=s.getSession().getAttribute(key);
+        if( o==null ) return null;
+        return o.toString();
+    }
+
+
+    public void expireSession( String sessionId ) {
+        Session s=sessions.get(sessionId);
+        if( s==null ) {
+            /*
+            Do not log session ID
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.SESSION_NOT_FOUND, sessionId);
+            }
+            */
+            return;
+        }
+        s.expire();
+    }
+
+
+    public String getLastAccessedTimeMillis( String sessionId ) {
+        Session s=sessions.get(sessionId);
+        if( s==null ) {
+            /*
+            Do not log session ID
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.SESSION_NOT_FOUND, sessionId);
+            }
+            */
+            return "";
+        }
+        return new Date(s.getLastAccessedTime()).toString();
+    }
+
+    
+    //PWC Extension
+    //START OF RIMOD# 4820359 -- Support for iWS6.0 session managers
+    /**
+     * Perform any operations when the request is finished.
+     */
+    public void update(HttpSession session) throws Exception {
+        return;
+    }
+    //END OF RIMOD# 4820359
+
+    // -------------------- JMX and Registration  --------------------
+    protected String domain;
+    protected ObjectName oname;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+    
+    //START OF 6364900
+    public void postRequestDispatcherProcess(ServletRequest request, ServletResponse response) {
+        //deliberate no-op
+        return;
+    }
+    
+    public void preRequestDispatcherProcess(ServletRequest request, ServletResponse response) {
+        //deliberate no-op
+        return;
+    }    
+    
+    public boolean lockSession(ServletRequest request) throws ServletException {
+        boolean result = false;
+        if(sessionLocker != null) {
+            result = sessionLocker.lockSession(request);
+        }        
+        return result;
+    }
+    
+    public void unlockSession(ServletRequest request) {
+        if(sessionLocker != null) {
+            sessionLocker.unlockSession(request);
+        }
+    }
+    //END OF 6364900    
+
+
+    /*
+     * Releases any resources held by this session manager.
+     */
+    public void release() {
+        clearSessions();
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/PersistentManager.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/PersistentManager.java
new file mode 100644
index 0000000..a2ce95c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/PersistentManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+/**
+ * Implementation of the <b>Manager</b> interface that makes use of
+ * a Store to swap active Sessions to disk. It can be configured to
+ * achieve several different goals:
+ *
+ * <li>Persist sessions across restarts of the Container</li>
+ * <li>Fault tolerance, keep sessions backed up on disk to allow
+ *     recovery in the event of unplanned restarts.</li>
+ * <li>Limit the number of active sessions kept in memory by
+ *     swapping less active sessions out to disk.</li>
+ *
+ * @version $Revision: 1.3 $
+ * @author Kief Morris (kief@kief.com)
+ */
+
+public final class PersistentManager extends PersistentManagerBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "PersistentManager/1.0";
+
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    private static final String name = "PersistentManager";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    public String getName() {
+
+        return (name);
+
+    }
+ }
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/PersistentManagerBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/PersistentManagerBase.java
new file mode 100644
index 0000000..4bc5a27
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/PersistentManagerBase.java
@@ -0,0 +1,1396 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+import org.apache.catalina.*;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.LifecycleSupport;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+
+/**
+ * Extends the <b>ManagerBase</b> class to implement most of the
+ * functionality required by a Manager which supports any kind of
+ * persistence, even if only for restarts.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>:  Correct behavior of session storing and
+ * reloading depends upon external calls to the <code>start()</code> and
+ * <code>stop()</code> methods of this class at the correct times.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @version $Revision: 1.16 $ $Date: 2007/05/05 05:32:19 $
+ */
+
+public abstract class PersistentManagerBase
+    extends ManagerBase
+    implements Lifecycle, PropertyChangeListener {
+
+    // ---------------------------------------------------- Security Classes
+    private class PrivilegedStoreClear
+        implements PrivilegedExceptionAction<Void> {
+
+        PrivilegedStoreClear() {            
+            // NOOP
+        }
+
+        public Void run() throws Exception{
+           store.clear();
+           return null;
+        }                       
+    }   
+     
+     private class PrivilegedStoreRemove
+        implements PrivilegedExceptionAction<Void> {
+
+        private String id;    
+            
+        PrivilegedStoreRemove(String id) {     
+            this.id = id;
+        }
+
+        public Void run() throws Exception{
+           store.remove(id);
+           return null;
+        }                       
+    }   
+     
+    private class PrivilegedStoreLoad
+        implements PrivilegedExceptionAction<Session> {
+
+        private String id;    
+            
+        PrivilegedStoreLoad(String id) {     
+            this.id = id;
+        }
+
+        public Session run() throws Exception{
+           return store.load(id);
+        }                       
+    }   
+          
+    private class PrivilegedStoreSave
+        implements PrivilegedExceptionAction<Void> {
+
+        private Session session;    
+            
+        PrivilegedStoreSave(Session session) {     
+            this.session = session;
+        }
+
+        public Void run() throws Exception{
+           store.save(session);
+           return null;
+        }                       
+    }   
+     
+    private class PrivilegedStoreKeys
+        implements PrivilegedExceptionAction<String[]> {
+
+        PrivilegedStoreKeys() {     
+            // NOOP
+        }
+
+        public String[] run() throws Exception{
+           return store.keys();
+        }                       
+    }   
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "PersistentManagerBase/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The maximum number of active Sessions allowed, or -1 for no limit.
+     */
+    private int maxActiveSessions = -1;
+
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    protected static final String name = "PersistentManagerBase";
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    /**
+     * Store object which will manage the Session store.
+     */
+    private Store store = null;
+
+
+    /**
+     * Whether to save and reload sessions when the Manager <code>unload</code>
+     * and <code>load</code> methods are called.
+     */
+    private boolean saveOnRestart = true;
+
+
+    /**
+     * How long a session must be idle before it should be backed up.
+     * -1 means sessions won't be backed up.
+     */
+    private int maxIdleBackup = -1;
+
+
+    /**
+     * Minimum time a session must be idle before it is swapped to disk.
+     * This overrides maxActiveSessions, to prevent thrashing if there are lots
+     * of active sessions. Setting to -1 means it's ignored.
+     */
+    private int minIdleSwap = -1;
+
+    /**
+     * The maximum time a session may be idle before it should be swapped
+     * to file just on general principle. Setting this to -1 means sessions
+     * should not be forced out.
+     */
+    private int maxIdleSwap = -1;
+
+
+    // START SJSAS 6406580
+    /**
+     * The set of invalidated Sessions for this Manager, keyed by
+     * session identifier.
+     */
+    protected ConcurrentHashMap<String, Long> invalidatedSessions
+        = new ConcurrentHashMap<String, Long>();
+
+    // Specifies for how long we're going to remember invalidated session ids
+    private long rememberInvalidatedSessionIdMilliSecs = 60000L;
+    // END SJSAS 6406580
+
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Perform the background processes for this Manager
+     */
+    public void backgroundProcess() {
+        this.processExpires();
+        this.processPersistenceChecks();
+        // START SJSAS 6406580
+        this.processInvalidatedSessions();
+        // END SJSAS 6406580
+        if ((this.getStore() != null)
+            && (this.getStore() instanceof StoreBase)) {
+            ((StoreBase) this.getStore()).processExpires();
+        }
+    }
+
+    /**
+     * Indicates how many seconds old a session can get, after its last
+     * use in a request, before it should be backed up to the store. -1
+     * means sessions are not backed up.
+     */
+    public int getMaxIdleBackup() {
+
+        return maxIdleBackup;
+
+    }
+
+
+    /**
+     * Sets the option to back sessions up to the Store after they
+     * are used in a request. Sessions remain available in memory
+     * after being backed up, so they are not passivated as they are
+     * when swapped out. The value set indicates how old a session
+     * may get (since its last use) before it must be backed up: -1
+     * means sessions are not backed up.
+     * <p>
+     * Note that this is not a hard limit: sessions are checked
+     * against this age limit periodically according to <b>checkInterval</b>.
+     * This value should be considered to indicate when a session is
+     * ripe for backing up.
+     * <p>
+     * So it is possible that a session may be idle for maxIdleBackup +
+     * checkInterval seconds, plus the time it takes to handle other
+     * session expiration, swapping, etc. tasks.
+     *
+     * @param backup The number of seconds after their last accessed
+     * time when they should be written to the Store.
+     */
+    public void setMaxIdleBackup (int backup) {
+
+        if (backup == this.maxIdleBackup)
+            return;
+        int oldBackup = this.maxIdleBackup;
+        this.maxIdleBackup = backup;
+        support.firePropertyChange("maxIdleBackup",
+                                   Integer.valueOf(oldBackup),
+                                   Integer.valueOf(this.maxIdleBackup));
+
+    }
+
+
+    /**
+     * The time in seconds after which a session should be swapped out of
+     * memory to disk.
+     */
+    public int getMaxIdleSwap() {
+
+        return maxIdleSwap;
+
+    }
+
+
+    /**
+     * Sets the time in seconds after which a session should be swapped out of
+     * memory to disk.
+     */
+    public void setMaxIdleSwap(int max) {
+
+        if (max == this.maxIdleSwap)
+            return;
+        int oldMaxIdleSwap = this.maxIdleSwap;
+        this.maxIdleSwap = max;
+        support.firePropertyChange("maxIdleSwap",
+                                   Integer.valueOf(oldMaxIdleSwap),
+                                   Integer.valueOf(this.maxIdleSwap));
+
+    }
+
+
+    /**
+     * The minimum time in seconds that a session must be idle before
+     * it can be swapped out of memory, or -1 if it can be swapped out
+     * at any time.
+     */
+    public int getMinIdleSwap() {
+
+        return minIdleSwap;
+
+    }
+
+
+    /**
+     * Sets the minimum time in seconds that a session must be idle before
+     * it can be swapped out of memory due to maxActiveSession. Set it to -1
+     * if it can be swapped out at any time.
+     */
+    public void setMinIdleSwap(int min) {
+
+        if (this.minIdleSwap == min)
+            return;
+        int oldMinIdleSwap = this.minIdleSwap;
+        this.minIdleSwap = min;
+        support.firePropertyChange("minIdleSwap",
+                                   Integer.valueOf(oldMinIdleSwap),
+                                   Integer.valueOf(this.minIdleSwap));
+
+    }
+
+
+    /**
+     * Set the Container with which this Manager has been associated.  If
+     * it is a Context (the usual case), listen for changes to the session
+     * timeout property.
+     *
+     * @param container The associated Container
+     */
+    public void setContainer(Container container) {
+
+        // De-register from the old Container (if any)
+        if ((this.container != null) && (this.container instanceof Context))
+            ((Context) this.container).removePropertyChangeListener(this);
+
+        // Default processing provided by our superclass
+        super.setContainer(container);
+
+        // Register with the new Container (if any)
+        if ((this.container != null) && (this.container instanceof Context)) {
+            setMaxInactiveIntervalSeconds
+                ( ((Context) this.container).getSessionTimeout()*60 );
+            ((Context) this.container).addPropertyChangeListener(this);
+        }
+
+        // START SJSAS 6406580
+        if (container instanceof StandardContext) {
+            // Determine for how long we're going to remember invalidated
+            // session ids
+            StandardContext ctx = (StandardContext) container;
+            int frequency = ctx.getManagerChecksFrequency();
+            int reapIntervalSeconds = ctx.getBackgroundProcessorDelay();
+            rememberInvalidatedSessionIdMilliSecs
+                = frequency * reapIntervalSeconds * 1000L * 2;
+            if (rememberInvalidatedSessionIdMilliSecs <= 0) {
+                rememberInvalidatedSessionIdMilliSecs = 60000L;
+            }
+        }
+        // END SJSAS 6406580
+    }
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    /**
+     * Return true, if the session id is loaded in memory
+     * otherwise false is returned
+     *
+     * @param id The session id for the session to be searched for
+     *
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    public boolean isLoaded( String id ){
+        try {
+            if ( super.findSession(id) != null )
+                return true;
+        } catch (IOException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CHECKING_IS_LOADED_EXCEPTION),
+                                              new Object[] {id, e.getMessage()});
+            log.log(Level.SEVERE, msg, e);
+        }
+        return false;
+    }
+
+
+    /**
+     * Return the maximum number of active Sessions allowed, or -1 for
+     * no limit.
+     */
+    public int getMaxActiveSessions() {
+
+        return (this.maxActiveSessions);
+
+    }
+
+
+    /**
+     * Set the maximum number of active Sessions allowed, or -1 for
+     * no limit.
+     *
+     * @param max The new maximum number of sessions
+     */
+    public void setMaxActiveSessions(int max) {
+
+        int oldMaxActiveSessions = this.maxActiveSessions;
+        this.maxActiveSessions = max;
+        support.firePropertyChange("maxActiveSessions",
+                                   Integer.valueOf(oldMaxActiveSessions),
+                                   Integer.valueOf(this.maxActiveSessions));
+    }
+
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    public String getName() {
+        return (name);
+    }
+
+
+    /**
+     * Get the started status.
+     */
+    protected boolean isStarted() {
+        return started;
+    }
+
+
+    /**
+     * Set the started flag
+     */
+    protected void setStarted(boolean started) {
+        this.started = started;
+    }
+
+
+    /**
+     * Set the Store object which will manage persistent Session
+     * storage for this Manager.
+     *
+     * @param store the associated Store
+     */
+    public void setStore(Store store) {
+        this.store = store;
+        store.setManager(this);
+    }
+
+
+    /**
+     * Return the Store object which manages persistent Session
+     * storage for this Manager.
+     */
+    public Store getStore() {
+
+        return (this.store);
+
+    }
+
+
+
+    /**
+     * Indicates whether sessions are saved when the Manager is shut down
+     * properly. This requires the unload() method to be called.
+     */
+    public boolean getSaveOnRestart() {
+
+        return saveOnRestart;
+
+    }
+
+
+    /**
+     * Set the option to save sessions to the Store when the Manager is
+     * shut down, then loaded when the Manager starts again. If set to
+     * false, any sessions found in the Store may still be picked up when
+     * the Manager is started again.
+     *
+     * @param saveOnRestart true if sessions should be saved on restart, false if
+     *     they should be ignored.
+     */
+    public void setSaveOnRestart(boolean saveOnRestart) {
+
+        if (saveOnRestart == this.saveOnRestart)
+            return;
+
+        boolean oldSaveOnRestart = this.saveOnRestart;
+        this.saveOnRestart = saveOnRestart;
+        support.firePropertyChange("saveOnRestart",
+                                   Boolean.valueOf(oldSaveOnRestart),
+                                   Boolean.valueOf(this.saveOnRestart));
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /*
+     * Releases any resources held by this session manager.
+     */
+    public void release() {
+        super.release();
+        clearStore();
+    }
+
+
+    /**
+     * Clear all sessions from the Store.
+     */
+    public void clearStore() {
+
+        if (store == null)
+            return;
+
+        try {     
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    AccessController.doPrivileged(new PrivilegedStoreClear());
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.log(Level.SEVERE, LogFacade.CLEARING_STORE_EXCEPTION, exception);
+                }
+            } else {
+                store.clear();
+            }
+        } catch (IOException e) {
+            log.log(Level.SEVERE, LogFacade.CLEARING_STORE_EXCEPTION, e);
+        }
+
+    }
+    
+    
+    /**
+     * Invalidate all sessions that have expired.
+     * Hercules: modified method
+     */
+    protected void processExpires() {
+
+        if (!started)
+            return;
+
+        Session sessions[] = findSessions();
+
+        for (int i = 0; i < sessions.length; i++) {
+            StandardSession session = (StandardSession) sessions[i];
+            /* START CR 6363689
+            if (!session.isValid()) {
+            */
+            // START CR 6363689
+            if(!session.getIsValid() || session.hasExpired()) {
+            // END CR 6363689            
+                if(session.lockBackground()) { 
+                    try {
+                        session.expire();
+                    } finally {
+                        session.unlockBackground();
+                    }
+                }                                
+	    }            
+        }
+    }        
+
+
+    /**
+     * Called by the background thread after active sessions have
+     * been checked for expiration, to allow sessions to be
+     * swapped out, backed up, etc.
+     */
+    public void processPersistenceChecks() {
+
+            processMaxIdleSwaps();
+            processMaxActiveSwaps();
+            processMaxIdleBackups();
+
+    }
+
+
+    /**
+     * Purges those session ids from the map of invalidated session ids whose
+     * time has come up
+     */
+    protected void processInvalidatedSessions() {
+
+        if (!started) {
+            return;
+        }
+
+        long timeNow = System.currentTimeMillis();
+        for (Map.Entry<String, Long> e : invalidatedSessions.entrySet()) {
+            String id = e.getKey();
+            Long timeAdded = e.getValue();
+            if ((timeAdded == null)
+                    || (timeNow - timeAdded.longValue() >
+                                    rememberInvalidatedSessionIdMilliSecs)) {
+                removeFromInvalidatedSessions(id);
+            }
+        }
+    }        
+
+
+    /**
+     * Return a new session object as long as the number of active
+     * sessions does not exceed <b>maxActiveSessions</b>. If there
+     * aren't too many active sessions, or if there is no limit,
+     * a session is created or retrieved from the recycled pool.
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    public Session createSession() {
+
+        if ((maxActiveSessions >= 0) &&
+                (sessions.size() >= maxActiveSessions))
+            throw new IllegalStateException
+                (rb.getString(LogFacade.CREATE_SESSION_EXCEPTION));
+
+        return (super.createSession());
+
+    }
+
+
+    // START S1AS8PE 4817642
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties, using the specified
+     * session id.
+     *
+     * IMPLEMENTATION NOTE: This method must be kept in sync with the
+     * createSession method that takes no arguments.
+     *
+     * @param sessionId the session id to assign to the new session
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     *
+     * @return the new session, or <code>null</code> if a session with the
+     * requested id already exists
+     */
+    public Session createSession(String sessionId) {
+
+        if ((maxActiveSessions >= 0) &&
+                (sessions.size() >= maxActiveSessions))
+            throw new IllegalStateException
+                (rb.getString(LogFacade.CREATE_SESSION_EXCEPTION));
+
+        return (super.createSession(sessionId));
+    }
+    // END S1AS8PE 4817642
+
+
+    /**
+     * Return the active Session, associated with this Manager, with the
+     * specified session id (if any); otherwise return <code>null</code>.
+     * This method checks the persistence store if persistence is enabled,
+     * otherwise just uses the functionality from ManagerBase.
+     *
+     * @param id The session id for the session to be returned
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    public Session findSession(String id) throws IOException {
+        
+        //6406580 START
+        if(!this.isSessionIdValid(id)) {
+            return null;
+        }
+        //6406580 END        
+
+        Session session = super.findSession(id);
+        if (session != null)
+            return (session);
+
+        // See if the Session is in the Store
+        session = swapIn(id);
+        return (session);
+
+    }       
+    
+    /**
+     * Return the active Session, associated with this Manager, with the
+     * specified session id (if any); otherwise return <code>null</code>.
+     * This method first removes the cached copy if removeCachedCopy = true.
+     * Then this method checks the persistence store if persistence is enabled,
+     * otherwise just uses the functionality from ManagerBase.
+     *
+     * @param id The session id for the session to be returned
+     * @param removeCachedCopy
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    public Session findSession(String id, boolean removeCachedCopy) throws IOException {
+
+        Session theSession = super.findSession(id);
+        if (theSession != null) {
+            if(removeCachedCopy) {
+                //remove from manager cache
+                removeSuper(theSession);
+                //remove from store cache if it exists
+                if ((this.getStore() != null)
+                    && (this.getStore() instanceof StoreBase)) {                
+                    ((StoreBase) this.getStore()).removeFromStoreCache(id);
+                }
+                theSession = null;
+            } else {
+                return (theSession);
+            }
+        } 
+        //now do full findSession
+        theSession = findSession(id);   
+        return theSession;
+
+    }     
+    
+    /**
+     * used by subclasses of PersistentManagerBase
+     * Hercules: added method
+     */    
+    protected Session superFindSession(String id) throws IOException {
+        return super.findSession(id);
+    }     
+
+    /**
+     * Remove this Session from the active Sessions for this Manager,
+     * but not from the Store. (Used by the PersistentValve)
+     *
+     * @param session Session to be removed
+     */
+    public void removeSuper(Session session) {
+        super.remove (session);
+    }
+
+    /**
+     * Load all sessions found in the persistence mechanism, assuming
+     * they are marked as valid and have not passed their expiration
+     * limit. If persistence is not supported, this method returns
+     * without doing anything.
+     * <p>
+     * Note that by default, this method is not called by the MiddleManager
+     * class. In order to use it, a subclass must specifically call it,
+     * for example in the start() and/or processPersistenceChecks() methods.
+     */
+    public void load() {
+
+        // Initialize our internal data structures
+        sessions.clear();
+
+        if (store == null)
+            return;
+
+        String[] ids = null;
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    ids = AccessController.doPrivileged(
+                            new PrivilegedStoreKeys());
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.log(Level.SEVERE, LogFacade.STORE_LOADING_EXCEPTION, exception);
+                }
+            } else {
+                ids = store.keys();
+            }
+        } catch (IOException e) {
+            log.log(Level.SEVERE, LogFacade.CANNOT_LOAD_SESSION_EXCEPTION, e);
+            return;
+        }
+
+        int n = ids.length;
+        if (n == 0)
+            return;
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, LogFacade.LOADING_PERSISTED_SESSIONS, String.valueOf(n));
+        }
+        for (int i = 0; i < n; i++)
+            try {
+                swapIn(ids[i]);
+            } catch (IOException e) {
+                log.log(Level.SEVERE, LogFacade.FAILED_LOAD_SESSION_EXCEPTION, e);
+            }
+
+    }
+    
+    /**
+     * Remove this Session from the active Sessions for this Manager,
+     * and from the Store.
+     *
+     * @param session Session to be removed
+     */
+    public void remove(Session session) {
+        remove(session, true);
+    }
+    
+    /**
+     * Remove this Session from the active Sessions for this Manager,
+     * and from the Store.
+     *
+     * @param session Session to be removed
+     * @param  persistentRemove - do we remove persistent session too
+     */
+    public void remove(Session session, boolean persistentRemove) {
+
+        super.remove (session);
+
+        if (persistentRemove && store != null){
+            removeSession(session.getIdInternal());
+        }
+    }    
+    
+    /**
+     * Remove this Session from the active Sessions for this Manager,
+     * and from the Store.
+     *
+     * @param id Session's id to be removed
+     */    
+    private void removeSession(String id){
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    AccessController.doPrivileged(new PrivilegedStoreRemove(id));
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.log(Level.SEVERE, LogFacade.STORE_REMOVE_SESSION_EXCEPTION, exception);
+                }
+            } else {
+                 store.remove(id);
+            }               
+        } catch (IOException e) {
+            log.log(Level.SEVERE, LogFacade.REMOVING_SESSION_EXCEPTION, e);
+        }        
+    }
+
+
+    // START SJSAS 6406580
+    /**
+     * Add this Session id to the set of invalidated Session ids for this
+     * Manager.
+     *
+     * @param sessionId session id to be added
+     */
+    public void addToInvalidatedSessions(String sessionId) {
+        invalidatedSessions.put(sessionId,
+                                Long.valueOf(System.currentTimeMillis()));
+    }
+
+    
+    /**
+     * Removes the given session id from the map of invalidated session ids.
+     *
+     * @param sessionId The session id to remove
+     */
+    public void removeFromInvalidatedSessions(String sessionId) {       
+        invalidatedSessions.remove(sessionId);
+    }    
+    
+
+    /**
+     * @return true if the given session id is not contained in the map of
+     * invalidated session ids, false otherwise
+     */
+    public boolean isSessionIdValid(String sessionId) {       
+        return (!invalidatedSessions.containsKey(sessionId));
+    }
+    // END SJSAS 6406580    
+
+
+    /**
+     * Save all currently active sessions in the appropriate persistence
+     * mechanism, if any.  If persistence is not supported, this method
+     * returns without doing anything.
+     * <p>
+     * Note that by default, this method is not called by the MiddleManager
+     * class. In order to use it, a subclass must specifically call it,
+     * for example in the stop() and/or processPersistenceChecks() methods.
+     */
+    public void unload() {
+
+        if (store == null)
+            return;
+
+        Session sessions[] = findSessions();
+        int n = sessions.length;
+        if (n == 0)
+            return;
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, LogFacade.SAVING_PERSISTED_SESSION, String.valueOf(n));
+        }
+        for (int i = 0; i < n; i++)
+            try {
+                swapOut(sessions[i]);
+            } catch (IOException e) {
+                // This is logged in writeSession()
+            }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * Look for a session in the Store and, if found, restore
+     * it in the Manager's list of active sessions if appropriate.
+     * The session will be removed from the Store after swapping
+     * in, but will not be added to the active session list if it
+     * is invalid or past its expiration.
+     */
+    protected Session swapIn(String id) throws IOException {
+        return swapIn(id, null);
+    }
+
+    /**
+     * Look for a session in the Store and, if found, restore
+     * it in the Manager's list of active sessions if appropriate.
+     * The session will be removed from the Store after swapping
+     * in, but will not be added to the active session list if it
+     * is invalid or past its expiration.
+     *
+     * @param id The session id
+     * @param version The requested session version
+     */
+    protected Session swapIn(String id, String version) throws IOException {
+
+        ClassLoader webappCl = null;
+        ClassLoader curCl = null;
+
+        if (getContainer() != null
+                    && getContainer().getLoader() != null) {
+            webappCl = getContainer().getLoader().getClassLoader();
+            curCl = Thread.currentThread().getContextClassLoader();
+        }
+
+        Session sess = null;
+
+        if (webappCl != null && curCl != webappCl) {
+            try {
+                Thread.currentThread().setContextClassLoader(webappCl);
+                sess = doSwapIn(id, version);
+            } finally {
+                Thread.currentThread().setContextClassLoader(curCl);
+            }
+        } else {
+            sess = doSwapIn(id, version);
+        }
+
+        return sess;
+    }
+
+    /**
+     * Look for a session in the Store and, if found, restore
+     * it in the Manager's list of active sessions if appropriate.
+     * The session will be removed from the Store after swapping
+     * in, but will not be added to the active session list if it
+     * is invalid or past its expiration.
+     */
+    private Session doSwapIn(String id, String version) throws IOException {
+
+        if (store == null)
+            return null;
+        
+        Session session = null;
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    session = AccessController.doPrivileged(
+                            new PrivilegedStoreLoad(id));
+                }catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.log(Level.SEVERE, LogFacade.STORE_SWAP_IN_EXCEPTION, exception);
+                    if (exception instanceof IOException){
+                        throw (IOException)exception;
+                    } else if (exception instanceof ClassNotFoundException) {
+                        throw (ClassNotFoundException)exception;
+                    }
+                }
+            } else {
+		if (version != null) {
+                     session = ((StoreBase) store).load(id, version);
+                } else {
+                     session = store.load(id);
+                }
+            }   
+        } catch (ClassNotFoundException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.DESERILIZING_SESSION_EXCEPTION),
+                                              new Object[] {id, e});
+            log.log(Level.SEVERE, msg);
+            throw new IllegalStateException(msg);
+        }
+
+        if (session == null)
+            return (null);
+
+        if (!session.isValid()) {
+            log.log(Level.SEVERE, LogFacade.INVALID_EXPIRED_SESSION_EXCEPTION);
+            //6406580 START
+            /* - these lines are calling remove on store redundantly
+            session.expire();
+            removeSession(id);
+             */
+            //6406580 END            
+            return (null);
+        }
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, LogFacade.SWAPPING_SESSION_FROM_STORE, id);
+        }
+        session.setManager(this);
+        // make sure the listeners know about it.
+        ((StandardSession)session).tellNew();
+        add(session);
+        ((StandardSession)session).activate();
+
+        return (session);
+
+    }
+
+
+    /**
+     * Remove the session from the Manager's list of active
+     * sessions and write it out to the Store. If the session
+     * is past its expiration or invalid, this method does
+     * nothing.
+     *
+     * @param session The Session to write out.
+     */
+    protected void swapOut(Session session) throws IOException {
+
+        if (store == null || !session.isValid()) {
+            return;
+        }
+
+        ((StandardSession)session).passivate();
+        writeSession(session);
+        super.remove(session);
+        session.recycle();
+
+    }
+
+
+    /**
+     * Write the provided session to the Store without modifying
+     * the copy in memory or triggering passivation events. Does
+     * nothing if the session is invalid or past its expiration.
+     */
+    protected void writeSession(Session session) throws IOException {
+        if (store == null || !session.isValid()) {
+            return;
+        }
+
+        ((StandardContext)getContainer()).sessionPersistedStartEvent(
+            (StandardSession) session);
+
+        // If the given session is being persisted after a lock has been
+        // acquired out-of-band, its version needs to be incremented
+        // here (otherwise, it will have already been incremented at the
+        // time the session was acquired via HttpServletRequest.getSession())
+        if (isSessionVersioningSupported()
+                && ((StandardSession) session).hasNonHttpLockOccurred()) {
+            ((StandardSession) session).incrementVersion();
+        }
+
+        try {
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    AccessController.doPrivileged(new PrivilegedStoreSave(session));
+                } catch(PrivilegedActionException ex){
+                    Exception exception = ex.getException();
+                    log.log(Level.SEVERE, LogFacade.STORE_WRITE_SESSION_EXCEPTION,
+                            exception);
+                }
+            } else {
+                 store.save(session);
+            }   
+        } catch (IOException e) {
+            log.log(Level.SEVERE,LogFacade.SERIALIZING_SESSION_EXCEPTION, new Object[] {session.getIdInternal(), e});
+            throw e;
+        } finally {
+            ((StandardContext)getContainer()).sessionPersistedEndEvent(
+                (StandardSession) session);
+        }
+    }
+
+
+    // -------------------------------------------------- Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this session manager.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.MANAGER_STARTED_INFO);
+            }
+            return;
+        }
+        if( ! initialized )
+            init();
+        
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Force initialization of the random number generator
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Force random number initialization starting");
+        generateSessionId();
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Force random number initialization completed");
+
+        if (store == null)
+            log.log(Level.SEVERE, LogFacade.NO_STORE_CONFIG_EXCEPTION);
+        else if (store instanceof Lifecycle)
+            ((Lifecycle)store).start();
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+   public void stop() throws LifecycleException {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Stopping");
+
+        // Validate and update our current component state
+        if (!isStarted()) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO,LogFacade. MANAGER_NOT_STARTED_INFO);
+            }
+            return;
+        }
+        
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        setStarted(false);
+
+        if (getStore() != null && saveOnRestart) {
+            unload();
+        } else {
+            // Expire all active sessions
+            Session sessions[] = findSessions();
+            for (int i = 0; i < sessions.length; i++) {
+                StandardSession session = (StandardSession) sessions[i];
+                if (!session.isValid())
+                    continue;
+                session.expire();
+            }
+        }
+
+        if (getStore() != null && getStore() instanceof Lifecycle)
+            ((Lifecycle)getStore()).stop();
+
+        // Require a new random number generator if we are restarted
+        resetRandom();
+
+        if( initialized )
+            destroy();
+
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Process property change events from our associated Context.
+     *
+     * @param event The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        // Validate the source of this event
+        if (!(event.getSource() instanceof Context))
+            return;
+
+        // Process a relevant property change
+        if (event.getPropertyName().equals("sessionTimeout")) {
+            try {
+                setMaxInactiveIntervalSeconds
+                    ( ((Integer) event.getNewValue()).intValue()*60 );
+            } catch (NumberFormatException e) {
+                log.log(Level.SEVERE, LogFacade.INVALID_SESSION_TIMEOUT_SETTING_EXCEPTION, event.getNewValue().toString());
+            }
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Swap idle sessions out to Store if they are idle too long.
+     */
+    protected void processMaxIdleSwaps() {
+
+        if (!isStarted() || maxIdleSwap < 0)
+            return;
+
+        Session sessions[] = findSessions();
+        long timeNow = System.currentTimeMillis();
+
+        // Swap out all sessions idle longer than maxIdleSwap
+        // FIXME: What's preventing us from mangling a session during
+        // a request?
+        if (maxIdleSwap >= 0) {
+            for (int i = 0; i < sessions.length; i++) {
+                StandardSession session = (StandardSession) sessions[i];
+                if (!session.isValid())
+                    continue;
+                int timeIdle = // Truncate, do not round up
+                    (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+                if (timeIdle > maxIdleSwap && timeIdle > minIdleSwap) {
+                    if (log.isLoggable(Level.FINE)) {
+                        log.log(Level.FINE, LogFacade.SWAPPING_SESSION_TO_STORE, new Object[] {session.getIdInternal(),
+                                Integer.valueOf(timeIdle)});
+                    }
+                    try {
+                        swapOut(session);
+                    } catch (IOException e) {
+                        // This is logged in writeSession()
+                    }
+                }
+            }
+        }
+
+    }
+    
+    
+    /**
+     * Swap idle sessions out to Store if too many are active
+     * Hercules: modified method
+     */
+    protected void processMaxActiveSwaps() {
+        
+        if (!isStarted() || getMaxActiveSessions() < 0)
+            return;
+
+        Session sessions[] = findSessions();
+
+        // FIXME: Smarter algorithm (LRU)
+        if (getMaxActiveSessions() >= sessions.length)
+            return;
+
+        if(log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, LogFacade.TOO_MANY_ACTIVE_SESSION, Integer.valueOf(sessions.length));
+        }
+        int toswap = sessions.length - getMaxActiveSessions();
+        long timeNow = System.currentTimeMillis();
+
+        for (int i = 0; i < sessions.length && toswap > 0; i++) {
+            int timeIdle = // Truncate, do not round up
+                (int) ((timeNow - sessions[i].getLastAccessedTime()) / 1000L);
+            if (timeIdle > minIdleSwap) {
+                StandardSession session = (StandardSession) sessions[i];
+                //skip the session if it cannot be locked
+                if(session.lockBackground()) {
+                    if(log.isLoggable(Level.FINE)) {
+                        log.log(Level.FINE, LogFacade.SWAP_OUT_SESSION, new Object[] {session.getIdInternal(),
+                                Integer.valueOf(timeIdle)});
+                    }
+                    try {
+                        swapOut(session);
+                    } catch (java.util.ConcurrentModificationException e1) {
+                        // This is logged in writeSession()                           
+                    } catch (IOException e) {
+                        // This is logged in writeSession()
+                    } catch (Exception e) {
+                        // This is logged in writeSession()                        
+                    } finally {
+                        session.unlockBackground();
+                    }
+                    toswap--;
+                }
+            }
+        }
+
+    }        
+    
+    
+    /**
+     * Back up idle sessions.
+     * Hercules: modified method
+     */
+    protected void processMaxIdleBackups() {
+        
+        if (!isStarted() || maxIdleBackup < 0)
+            return;
+
+        Session sessions[] = findSessions();
+        long timeNow = System.currentTimeMillis();
+
+        // Back up all sessions idle longer than maxIdleBackup
+        if (maxIdleBackup >= 0) {
+            for (int i = 0; i < sessions.length; i++) {
+                StandardSession session = (StandardSession) sessions[i];
+                if (!session.isValid())
+                    continue;                
+                int timeIdle = // Truncate, do not round up
+                    (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+                if (timeIdle > maxIdleBackup) { 
+                    //if session cannot be background locked then skip it
+                    if (session.lockBackground()) {                         
+                        if (log.isLoggable(Level.FINE)) {
+                            log.log(Level.FINE, LogFacade.BACKUP_SESSION_TO_STORE, new Object[] {session.getIdInternal(),
+                                    Integer.valueOf(timeIdle)});
+                        }
+                        try {
+                            writeSession(session);
+                        } catch (java.util.ConcurrentModificationException e1) {
+                            // This is logged in writeSession()                            
+                        } catch (IOException e) {
+                            // This is logged in writeSession()
+                        } catch (Exception e) {
+                            // This is logged in writeSession()                                
+                        } finally {
+                            session.unlockBackground();
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+    
+    public String getMonitorAttributeValues() {
+        //FIXME if desired for monitoring 'file'
+        return "";
+    }    
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/SessionLock.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/SessionLock.java
new file mode 100755
index 0000000..0e5977f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/SessionLock.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+
+public class SessionLock {
+    
+    private static final String BACKGROUND_LOCK = "background_lock";
+    private static final String FOREGROUND_LOCK = "foreground_lock";
+    
+    /** Creates a new instance of SessionLock */
+    public SessionLock() {
+    }
+
+    /**
+     * get the lock type
+     *
+     */     
+    public String getLockType() {
+        return _lockType;
+    }
+
+    /**
+     * set the lock type - lockType must be BACKGROUND_LOCK or FOREGROUND_LOCK
+     *
+     * @param lockType the type of the lock
+     */    
+    public void setLockType(String lockType) {
+        _lockType = lockType;
+        // If resetting lock then also reset the _hasNonHttpLockOccurred
+        if (lockType == null) {
+            _hasNonHttpLockOccurred = false;
+        }
+    }
+    
+    /**
+     * get the foregroundRefCount
+     *
+     */     
+    public int getForegroundRefCount() {
+        return _foregroundRefCount;
+    }
+    
+    /**
+     * set the foregroundRefCount
+     *
+     * @param foregroundRefCount
+     */    
+    public void setForegroundRefCount(int foregroundRefCount) {
+        _foregroundRefCount = foregroundRefCount;
+    }
+    
+    /**
+     * increment the foregroundRefCount
+     *
+     */      
+    public void incrementForegroundRefCount() {
+        _foregroundRefCount++;
+    }
+    
+    /**
+     * decrement the foregroundRefCount
+     *
+     */    
+    public void decrementForegroundRefCount() {
+        _foregroundRefCount--;
+    }    
+    
+    /**
+     * return whether lock is background locked
+     *
+     */    
+    public boolean isBackgroundLocked() {
+        if(_lockType == null) {
+            return false;
+        }
+        return (_lockType.equals(BACKGROUND_LOCK));
+    }
+    
+    /**
+     * return whether lock is foreground locked
+     *
+     */     
+    public boolean isForegroundLocked() {
+        if(_lockType == null) {
+            return false;
+        }
+        return (_lockType.equals(FOREGROUND_LOCK));
+    }
+    
+    /**
+     * return whether lock is locked (either foreground or background)
+     *
+     */       
+    public boolean isLocked() {
+        return (_lockType != null);
+    }
+   
+    /**
+     * @return true if lock has been locked by non-http request, false otherwise
+     */       
+    public boolean hasNonHttpLockOccurred() {    
+        return _hasNonHttpLockOccurred;
+    }
+
+    /**
+     * unlock the lock
+     * if background locked the lock will become fully unlocked
+     * if foreground locked the lock will become fully unlocked
+     * if foregroundRefCount was 1; otherwise it will
+     * decrement the foregroundRefCount and the lock will remain foreground locked
+     *
+     */    
+    public void unlock() {
+        if(!isLocked())
+            return;
+        if(isBackgroundLocked()) {
+            this.setLockType(null);
+            this.setForegroundRefCount(0);
+            return;
+        }
+        if(isForegroundLocked()) {
+            decrementForegroundRefCount();
+            if(_foregroundRefCount == 0) {
+                this.setLockType(null);
+            }
+        }                        
+    } 
+    
+    /**
+     * unlock the lock for the foreground locked case
+     * the lock will be unlocked
+     * if foregroundRefCount was 1; otherwise it will
+     * decrement the foregroundRefCount and the lock will remain foreground locked
+     *
+     */    
+    public void unlockForeground() {
+        //unlock if the lock is foreground locked
+        //else do nothing        
+        if(!isLocked())
+            return;
+        if(isForegroundLocked()) {
+            decrementForegroundRefCount();
+            if(_foregroundRefCount == 0) {
+                this.setLockType(null);
+            }
+        }                        
+    }  
+    
+    /**
+     * unlock the lock
+     * this is a force unlock; foregroundRefCount is ignored
+     *
+     */      
+    public void unlockForegroundCompletely() {
+        //unlock completely if the lock is foreground locked
+        //else do nothing        
+        if(!isLocked())
+            return;
+        if(isForegroundLocked()) {
+            this.setForegroundRefCount(0);
+            this.setLockType(null);
+        }                        
+    }      
+    
+    /**
+     * unlock the lock for the background locked case
+     * the lock will be unlocked
+     *
+     */     
+    public void unlockBackground() {
+        //unlock if the lock is background locked
+        //else do nothing
+        if(!isLocked())
+            return;
+        if(isBackgroundLocked()) {
+            this.setLockType(null);
+            this.setForegroundRefCount(0);
+            return;
+        }                        
+    }     
+    
+    /**
+     * if possible, the lock will be foreground locked
+     * if it was already foreground locked; it will
+     * remain so and the foregroundRefCount will be incremented
+     *
+     * if the lock is already background locked the method
+     * will return false and the lock remains background locked 
+     * (i.e. lock failed) otherwise it will return true (lock succeeded)
+     */     
+    public synchronized boolean lockForeground() {
+        return lockForeground(true);
+    }
+
+    public synchronized boolean lockForeground(boolean isHttp) {
+        if (!isHttp) {
+            _hasNonHttpLockOccurred = true;
+        }
+        if (isBackgroundLocked()) {
+            return false;
+        }
+        if (isForegroundLocked()) {
+            incrementForegroundRefCount();
+        } else {
+            setForegroundRefCount(1);
+        }
+        setLockType(FOREGROUND_LOCK);
+
+        return true;
+    }
+    
+    /**
+     * if possible, the lock will be background locked
+     *
+     * if the lock is already foreground locked the method
+     * will return false and the lock remains foreground locked 
+     * (i.e. lock failed) otherwise it will return true (lock succeeded)
+     */      
+    public synchronized boolean lockBackground() {
+        if (isForegroundLocked()) {
+            return false;
+        } 
+        setLockType(BACKGROUND_LOCK);
+        setForegroundRefCount(0);
+
+        return true;
+    }
+    
+    /**
+     * returns String representation of the state of the lock
+     */     
+    public String toString() {
+        StringBuilder sb = new StringBuilder(50);
+        sb.append("_lockType= " + _lockType);
+        sb.append("\n" + "foregroundRefCount= " + _foregroundRefCount);
+        return sb.toString();
+    }
+    
+    private String _lockType = null;
+    private int _foregroundRefCount = 0;
+    private boolean _hasNonHttpLockOccurred = false;    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardManager.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardManager.java
new file mode 100644
index 0000000..488567a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardManager.java
@@ -0,0 +1,970 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+import org.apache.catalina.*;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.LifecycleSupport;
+
+import javax.servlet.ServletContext;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.*;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.logging.Level;
+
+/**
+ * Standard implementation of the <b>Manager</b> interface that provides
+ * simple session persistence across restarts of this component (such as
+ * when the entire server is shut down and restarted, or when a particular
+ * web application is reloaded.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>:  Correct behavior of session storing and
+ * reloading depends upon external calls to the <code>start()</code> and
+ * <code>stop()</code> methods of this class at the correct times.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @version $Revision: 1.14.6.2 $ $Date: 2008/04/17 18:37:20 $
+ */
+
+public class StandardManager
+    extends ManagerBase
+    implements Lifecycle, PropertyChangeListener {
+
+    // ---------------------------------------------------- Security Classes
+    private class PrivilegedDoLoadFromFile
+        implements PrivilegedExceptionAction<Void> {
+
+        PrivilegedDoLoadFromFile() {
+            // NOOP
+        }
+
+        public Void run() throws Exception{
+           doLoadFromFile();
+           return null;
+        }                       
+    }
+        
+    private class PrivilegedDoUnload
+        implements PrivilegedExceptionAction<Void> {
+
+        private boolean expire;
+        private boolean isShutdown;
+
+        PrivilegedDoUnload(boolean expire, boolean shutDown) {
+            this.expire = expire;
+            isShutdown = shutDown;
+        }
+
+        public Void run() throws Exception{
+            doUnload(expire, isShutdown);
+            return null;
+        }            
+           
+    }        
+
+    
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "StandardManager/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The maximum number of active Sessions allowed, or -1 for no limit.
+     */
+    private int maxActiveSessions = -1;
+
+
+    /**
+     * The descriptive name of this Manager implementation (for logging).
+     */
+    protected static final String name = "StandardManager";
+
+
+    /**
+     * Path name of the disk file in which active sessions are saved
+     * when we stop, and from which these sessions are loaded when we start.
+     * A <code>null</code> value indicates that no persistence is desired.
+     * If this pathname is relative, it will be resolved against the
+     * temporary working directory provided by our context, available via
+     * the <code>javax.servlet.context.tempdir</code> context attribute.
+     */
+    private String pathname = "SESSIONS.ser";
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+    // START SJSAS 6359401
+    /*
+     * The absolute path name of the file where sessions are persisted.
+     */
+    private String absPathName;
+    // END SJSAS 6359401
+
+    long processingTime=0;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the Container with which this Manager has been associated.  If
+     * it is a Context (the usual case), listen for changes to the session
+     * timeout property.
+     *
+     * @param container The associated Container
+     */
+    @Override
+    public void setContainer(Container container) {
+
+        // De-register from the old Container (if any)
+        if ((this.container != null) && (this.container instanceof Context))
+            ((Context) this.container).removePropertyChangeListener(this);
+
+        // Default processing provided by our superclass
+        super.setContainer(container);
+
+        // Register with the new Container (if any)
+        if ((this.container != null) && (this.container instanceof Context)) {
+            setMaxInactiveIntervalSeconds
+                ( ((Context) this.container).getSessionTimeout()*60 );
+            ((Context) this.container).addPropertyChangeListener(this);
+        }
+
+    }
+
+
+    /**
+     * Return descriptive information about this Manager implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    @Override
+    public String getInfo() {
+        return info;
+    }
+
+
+    /**
+     * Return the maximum number of active Sessions allowed, or -1 for
+     * no limit.
+     */
+    public int getMaxActiveSessions() {
+        return maxActiveSessions;
+    }
+
+
+    public long getProcessingTime() {
+        return processingTime;
+    }
+
+
+    public void setProcessingTime(long processingTime) {
+        this.processingTime = processingTime;
+    }
+
+
+    /**
+     * Set the maximum number of active Sessions allowed, or -1 for
+     * no limit.
+     *
+     * @param max The new maximum number of sessions
+     */
+    public void setMaxActiveSessions(int max) {
+        int oldMaxActiveSessions = this.maxActiveSessions;
+        this.maxActiveSessions = max;
+        support.firePropertyChange("maxActiveSessions",
+                                   Integer.valueOf(oldMaxActiveSessions),
+                                   Integer.valueOf(this.maxActiveSessions));
+    }
+
+
+    /**
+     * Return the descriptive short name of this Manager implementation.
+     */
+    @Override
+    public String getName() {
+        return name;
+    }
+
+
+    /**
+     * Return the session persistence pathname, if any.
+     */
+    public String getPathname() {
+        return pathname;
+    }
+
+
+    /**
+     * Set the session persistence pathname to the specified value.  If no
+     * persistence support is desired, set the pathname to <code>null</code>.
+     *
+     * @param pathname New session persistence pathname
+     */
+    public void setPathname(String pathname) {
+        String oldPathname = this.pathname;
+        this.pathname = pathname;
+        support.firePropertyChange("pathname", oldPathname, this.pathname);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id will be assigned by this method, and available via the getId()
+     * method of the returned session.  If a new session cannot be created
+     * for any reason, return <code>null</code>.
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    @Override
+    public Session createSession() {
+        if ((maxActiveSessions >= 0) &&
+                (sessions.size() >= maxActiveSessions)) {
+            rejectedSessions++;
+            ((StandardContext)container).sessionRejectedEvent(
+                maxActiveSessions);
+            throw new IllegalStateException
+                (rb.getString(LogFacade.TOO_MANY_ACTIVE_SESSION_EXCEPTION));
+        }
+
+        return (super.createSession());
+    }
+
+    // START S1AS8PE 4817642
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties, using the specified
+     * session id.
+     *
+     * IMPLEMENTATION NOTE: This method must be kept in sync with the
+     * createSession method that takes no arguments.
+     *
+     * @param sessionId the session id to assign to the new session
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     *
+     * @return the new session, or <code>null</code> if a session with the
+     * requested id already exists
+     */
+    @Override
+    public Session createSession(String sessionId) {
+        if ((maxActiveSessions >= 0) &&
+                (sessions.size() >= maxActiveSessions)) {
+            rejectedSessions++;
+            throw new IllegalStateException
+                (rb.getString(LogFacade.TOO_MANY_ACTIVE_SESSION_EXCEPTION));
+        }
+
+        return (super.createSession(sessionId));
+    }
+    // END S1AS8PE 4817642
+
+    /*
+     * Releases any resources held by this session manager.
+     */
+    @Override
+    public void release() {
+        super.release();
+        clearStore();
+    }
+
+
+    // START SJSAS 6359401
+    /*
+     * Deletes the persistent session storage file.
+     */
+    public void clearStore() {
+        File file = file();
+        if (file != null && file.exists()) {
+            deleteFile(file);
+        }
+    }
+    // END SJSAS 6359401
+
+
+    /**
+     * Loads any currently active sessions that were previously unloaded
+     * to the appropriate persistence mechanism, if any.  If persistence is not
+     * supported, this method returns without doing anything.
+     *
+     * @exception ClassNotFoundException if a serialized class cannot be
+     * found during the reload
+     * @exception IOException if a read error occurs
+     */
+    public void load() throws ClassNotFoundException, IOException {
+        if (SecurityUtil.isPackageProtectionEnabled()){   
+            try{
+                AccessController.doPrivileged(new PrivilegedDoLoadFromFile());
+            } catch (PrivilegedActionException ex){
+                Exception exception = ex.getException();
+                if (exception instanceof ClassNotFoundException){
+                    throw (ClassNotFoundException)exception;
+                } else if (exception instanceof IOException) {
+                    throw (IOException)exception;
+                }
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, "Unreported exception in load() "
+                            + exception);
+                }
+            }
+        } else {
+            doLoadFromFile();
+        }       
+    }
+
+
+    /**
+     * Loads any currently active sessions that were previously unloaded
+     * to file
+     *
+     * @exception ClassNotFoundException if a serialized class cannot be
+     * found during the reload
+     * @exception IOException if a read error occurs
+     */
+    private void doLoadFromFile() throws ClassNotFoundException, IOException {
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Start: Loading persisted sessions");
+        }
+
+        // Open an input stream to the specified pathname, if any
+        File file = file();
+        if (file == null || !file.exists() || file.length() == 0) {
+            return;
+        }
+        if (log.isLoggable(Level.FINE)) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.LOADING_PERSISTED_SESSION), pathname);
+            log.log(Level.FINE, msg);
+        }
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(file.getAbsolutePath());
+            readSessions(fis);
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Finish: Loading persisted sessions");
+            }
+        } catch (FileNotFoundException e) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "No persisted data file found");
+            }
+        } finally {
+            try {
+                if (fis != null) {
+                    fis.close();
+                }
+            } catch (IOException f) {
+                // ignore
+            }
+            // Delete the persistent storage file
+            deleteFile(file);
+        }
+    }
+
+    private void deleteFile(File file) {
+        if (!file.delete() && log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Cannot delete file: " + file);
+        }
+    }
+
+    /*
+     * Reads any sessions from the given input stream, and initializes the
+     * cache of active sessions with them.
+     *
+     * @param is the input stream from which to read the sessions
+     *
+     * @exception ClassNotFoundException if a serialized class cannot be
+     * found during the reload
+     * @exception IOException if a read error occurs
+     */
+    public void readSessions(InputStream is)
+            throws ClassNotFoundException, IOException {
+
+        // Initialize our internal data structures
+        sessions.clear();
+
+        ObjectInputStream ois = null;
+        try {
+            BufferedInputStream bis = new BufferedInputStream(is);
+            if (container != null) {
+                ois = ((StandardContext)container).createObjectInputStream(bis);
+            } else {
+                ois = new ObjectInputStream(bis);
+            }
+        } catch (IOException ioe) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.LOADING_PERSISTED_SESSION_IO_EXCEPTION),
+                                              ioe);
+
+            log.log(Level.SEVERE, msg, ioe);
+            if (ois != null) {
+                try {
+                    ois.close();
+                } catch (IOException f) {
+                     // Ignore
+                }
+                ois = null;
+            }
+            throw ioe;
+        }
+
+        synchronized (sessions) {
+            try {
+                Integer count = (Integer) ois.readObject();
+                int n = count.intValue();
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Loading " + n + " persisted sessions");
+                for (int i = 0; i < n; i++) {
+                    StandardSession session =
+                        StandardSession.deserialize(ois, this);
+                    session.setManager(this);
+                    sessions.put(session.getIdInternal(), session);
+                    session.activate();
+                }
+            } catch (ClassNotFoundException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CLASS_NOT_FOUND_EXCEPTION),
+                                                  e);
+                log.log(Level.SEVERE, msg, e);
+                if (ois != null) {
+                    try {
+                        ois.close();
+                    } catch (IOException f) {
+                        // Ignore
+                    }
+                    ois = null;
+                }
+                throw e;
+            } catch (IOException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.LOADING_PERSISTED_SESSION_IO_EXCEPTION),
+                                                  e);
+              log.log(Level.SEVERE, msg, e);
+                if (ois != null) {
+                    try {
+                        ois.close();
+                    } catch (IOException f) {
+                        // Ignore
+                    }
+                    ois = null;
+                }
+                throw e;
+            } finally {
+                // Close the input stream
+                try {
+                    if (ois != null) {
+                        ois.close();
+                    }
+                } catch (IOException f) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Save any currently active sessions in the appropriate persistence
+     * mechanism, if any.  If persistence is not supported, this method
+     * returns without doing anything.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void unload() throws IOException {
+        unload(true, false);
+    }
+
+
+    /**
+     * Writes all active sessions to the given output stream.
+     *
+     * @param os the output stream to which to write
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void writeSessions(OutputStream os) throws IOException {
+        writeSessions(os, true);
+    }
+
+
+    /**
+     * Save any currently active sessions in the appropriate persistence
+     * mechanism, if any.  If persistence is not supported, this method
+     * returns without doing anything.
+     *
+     * @doExpire true if the unloaded sessions are to be expired, false
+     * otherwise
+     * @param isShutdown true if this manager is being stopped as part of a
+     * domain shutdown (as opposed to an undeployment), and false otherwise
+     *
+     * @exception IOException if an input/output error occurs
+     */        
+    protected void unload(boolean doExpire, boolean isShutdown) throws IOException {
+        if (SecurityUtil.isPackageProtectionEnabled()){       
+            try {
+                AccessController.doPrivileged(
+                    new PrivilegedDoUnload(doExpire, isShutdown));
+            } catch (PrivilegedActionException ex){
+                Exception exception = ex.getException();
+                if (exception instanceof IOException){
+                    throw (IOException)exception;
+                }
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Unreported exception in unLoad() " + exception);
+            }
+        } else {
+            doUnload(doExpire, isShutdown);
+        }       
+    }
+        
+
+    /**
+     * Saves any currently active sessions to file.
+     *
+     * @doExpire true if the unloaded sessions are to be expired, false
+     * otherwise
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private void doUnload(boolean doExpire, boolean isShutdown) throws IOException {
+        if(isShutdown) {
+            if(log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Unloading persisted sessions");
+            }
+            // Open an output stream to the specified pathname, if any
+            File file = file();
+            if(file == null || !isDirectoryValidFor(file.getAbsolutePath())) {
+                return;
+            }
+            if(log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, LogFacade.SAVING_PERSISTED_SESSION_PATH, pathname);
+            }
+            FileOutputStream fos = null;
+            try {
+                fos = new FileOutputStream(file.getAbsolutePath());
+                writeSessions(fos, doExpire);
+                if(log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, "Unloading complete");
+                }
+            } catch(IOException ioe) {
+                if(fos != null) {
+                    try {
+                        fos.close();
+                    } catch(IOException f) {
+                        ;
+                    }
+                    fos = null;
+                }
+                throw ioe;
+            } finally {
+                try {
+                    if(fos != null) {
+                        fos.close();
+                    }
+                } catch(IOException f) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /*
+     * Writes all active sessions to the given output stream.
+     *
+     * @param os the output stream to which to write the sessions
+     * @param doExpire true if the sessions that were written should also be
+     * expired, false otherwise
+     */
+    public void writeSessions(OutputStream os, boolean doExpire) 
+            throws IOException {
+        ObjectOutputStream oos = null;
+        try {
+            if (container != null) {
+                oos = ((StandardContext) container).createObjectOutputStream(
+                        new BufferedOutputStream(os));
+            } else {
+                oos = new ObjectOutputStream(new BufferedOutputStream(os));
+            }
+        } catch (IOException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.SAVING_PERSISTED_SESSION_IO_EXCEPTION),
+                                              e);
+            log.log(Level.SEVERE, msg, e);
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException f) {
+                    // Ignore
+                }
+                oos = null;
+            }
+            throw e;
+        }
+
+        // Write the number of active sessions, followed by the details
+        StandardSession[] currentStandardSessions = null;
+        synchronized (sessions) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "Unloading " + sessions.size() + " sessions");
+            try {
+                // START SJSAS 6375689
+                for (Session actSession : findSessions()) {
+                    StandardSession session = (StandardSession) actSession;
+                    session.passivate();
+                }
+                // END SJSAS 6375689
+                Session[] currentSessions = findSessions();
+                int size = currentSessions.length;
+                currentStandardSessions = new StandardSession[size];
+                oos.writeObject(Integer.valueOf(size));
+                for (int i = 0; i < size; i++) {
+                    StandardSession session =
+                        (StandardSession) currentSessions[i];
+                    currentStandardSessions[i] = session;
+                    /* SJSAS 6375689
+                    session.passivate();
+                    */
+                    oos.writeObject(session);
+                }
+            } catch (IOException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SAVING_PERSISTED_SESSION_IO_EXCEPTION),
+                        e);
+                log.log(Level.SEVERE, msg, e);
+                if (oos != null) {
+                    try {
+                        oos.close();
+                    } catch (IOException f) {
+                        // Ignore
+                    }
+                    oos = null;
+                }
+                throw e;
+            }
+        }
+
+        // Flush and close the output stream
+        try {
+            oos.flush();
+        } catch (IOException e) {
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException f) {
+                    // Ignore
+                }
+                oos = null;
+            }
+            throw e;
+        } finally {
+            try {
+                if (oos != null) {
+                    oos.close();
+                }
+            } catch (IOException f) {
+                // ignore
+            }
+        }
+
+        if (doExpire) {
+            // Expire all the sessions we just wrote
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "Expiring " + currentStandardSessions.length + " persisted sessions");
+            for (StandardSession session : currentStandardSessions) {
+                try {
+                    session.expire(false);
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Check if the directory for this full qualified file
+     * exists and is valid
+     * Hercules: added method
+     */    
+    private boolean isDirectoryValidFor(String fullPathFileName) {
+        int lastSlashIdx = fullPathFileName.lastIndexOf(File.separator);
+        if(lastSlashIdx == -1) {
+            return false;
+        }
+        String result = fullPathFileName.substring(0, lastSlashIdx);
+        //System.out.println("PATH name = " + result);
+        return new File(result).isDirectory();
+    }    
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners
+     * associated with this StandardManager.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        if( ! initialized )
+            init();
+        
+        // Validate and update our current component state
+        if (started) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.MANAGER_STARTED_INFO);
+            }
+            return;
+        }
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // Force initialization of the random number generator
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Force random number initialization starting");
+        generateSessionId();
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Force random number initialization completed");
+
+        // Load unloaded sessions, if any
+        try {
+            load();
+        } catch (Throwable t) {
+            log.log(Level.SEVERE,
+                    LogFacade.LOADING_SESSIONS_EXCEPTION, t);
+        }
+
+    }
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+        stop(false);
+    }
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @param isShutdown true if this manager is being stopped as part of a
+     * domain shutdown (as opposed to an undeployment), and false otherwise
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop(boolean isShutdown) throws LifecycleException {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Stopping");
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.MANAGER_NOT_STARTED_INFO));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Write out sessions
+        try {
+            unload(false, isShutdown);
+        } catch (IOException e) {
+            log.log(Level.SEVERE, LogFacade.LOADING_SESSIONS_EXCEPTION, e);
+        }
+
+        // Expire all active sessions and notify their listeners
+        Session sessions[] = findSessions();
+        if (sessions != null) {
+            for (Session session : sessions) {
+                if (!session.isValid()) {
+                    continue;
+                }
+                try {
+                    session.expire();
+                } catch (Throwable t) {
+                    // Ignore
+                } finally {
+                    // Measure against memory leaking if references to the session
+                    // object are kept in a shared field somewhere
+                    session.recycle();
+                }
+            }
+        }
+
+        // Require a new random number generator if we are restarted
+        resetRandom();
+
+        if( initialized ) {
+            destroy();
+        }
+    }
+
+
+    // ----------------------------------------- PropertyChangeListener Methods
+
+
+    /**
+     * Process property change events from our associated Context.
+     *
+     * @param event The property change event that has occurred
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        // Validate the source of this event
+        if (!(event.getSource() instanceof Context))
+            return;
+
+        // Process a relevant property change
+        if ("sessionTimeout".equals(event.getPropertyName())) {
+            try {
+                setMaxInactiveIntervalSeconds
+                    ((Integer) event.getNewValue() *60 );
+            } catch (NumberFormatException e) {
+                log.log(Level.SEVERE, LogFacade.INVALID_SESSION_TIMEOUT_SETTING_EXCEPTION,
+                        event.getNewValue().toString());
+            }
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Return a File object representing the pathname to our
+     * persistence file, if any.
+     */
+    private File file() {
+
+        // START SJSAS 6359401
+        if (absPathName != null) {
+            return new File(absPathName);
+        }
+        // END SJSAS 6359401
+
+        if ((pathname == null) || (pathname.length() == 0))
+            return (null);
+        File file = new File(pathname);
+        if (!file.isAbsolute()) {
+            if (container instanceof Context) {
+                ServletContext servletContext =
+                    ((Context) container).getServletContext();
+                File tempdir = (File)
+                    servletContext.getAttribute(ServletContext.TEMPDIR);
+                if (tempdir != null)
+                    file = new File(tempdir, pathname);
+            }
+        }
+
+        // START SJSAS 6359401
+        if (file != null) {
+            absPathName = file.getAbsolutePath();
+        }
+        // END SJSAS 6359401
+
+        return (file);
+
+    }
+
+
+    /**
+     * Invalidate all sessions that have expired.
+     */
+    public void processExpires() {
+
+        long timeNow = System.currentTimeMillis();
+
+        Session[] sessions = findSessions();
+        if (sessions != null) {
+            for (Session session : sessions) {
+                StandardSession sess = (StandardSession) session;
+                if (sess.lockBackground()) {
+                    try {
+                        sess.isValid();
+                    } finally {
+                        sess.unlockBackground();
+                    }
+                }
+            }
+        }
+
+        long timeEnd = System.currentTimeMillis();
+        processingTime += ( timeEnd - timeNow );
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardSession.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardSession.java
new file mode 100644
index 0000000..a4c39d7
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardSession.java
@@ -0,0 +1,2377 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+import com.sun.enterprise.spi.io.BaseIndirectlySerializable;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.*;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.security.SecurityUtil;
+import org.apache.catalina.util.Enumerator;
+import org.apache.tomcat.util.security.PrivilegedSetTccl;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.*;
+import java.io.*;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+//end HERCULES:add
+
+
+
+/**
+ * Standard implementation of the <b>Session</b> interface.  This object is
+ * serializable, so that it can be stored in persistent storage or transferred
+ * to a different JVM for distributable session support.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>:  An instance of this class represents both the
+ * internal (Session) and application level (HttpSession) view of the session.
+ * However, because the class itself is not declared public, Java logic outside
+ * of the <code>org.apache.catalina.session</code> package cannot cast an
+ * HttpSession view of this instance back to a Session view.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>:  If you add fields to this class, you must
+ * make sure that you carry them over in the read/writeObject methods so
+ * that this class is properly serialized.
+ *
+ * @author Craig R. McClanahan
+ * @author Sean Legassick
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @version $Revision: 1.33 $ $Date: 2007/03/12 21:41:52 $
+ */
+
+public class StandardSession
+    implements HttpSession, Session, Serializable {
+
+    private static final Logger log = LogFacade.getLogger();
+
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Construct a new Session associated with the specified Manager.
+     *
+     * @param manager The manager with which this Session is associated
+     */
+    public StandardSession(Manager manager) {
+
+        super();
+        setManager(manager);
+        if (manager instanceof ManagerBase) {
+            this.debug = ((ManagerBase) manager).getDebug();
+        }
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * Type array.
+     */
+    protected static final String EMPTY_ARRAY[] = new String[0];
+
+    /**
+     * The dummy attribute value serialized when a NotSerializableException is
+     * encountered in <code>writeObject()</code>.
+     */
+    protected static final String NOT_SERIALIZED =
+        "___NOT_SERIALIZABLE_EXCEPTION___";
+
+    //HERCULES:add
+    /**
+     * The string used in the name for setAttribute and removeAttribute
+     * to signify on-demand sync
+     */    
+    protected static final String SYNC_STRING = "com.sun.sync";
+    //end HERCULES:add
+
+    /**
+     * The method signature for the <code>fireContainerEvent</code> method.
+     */
+    static final Class<?> containerEventTypes[] =
+        { String.class, Object.class };
+
+    /**
+     * Descriptive information describing this Session implementation.
+     */
+    protected static final String info = "StandardSession/1.0";
+
+    /**
+     * Set of attribute names which are not allowed to be persisted.
+     */
+    private static final String[] excludedAttributes = {
+        Globals.SUBJECT_ATTR
+    };
+
+    /**
+     * The HTTP session context associated with this session.
+     */
+    protected static volatile HttpSessionContext sessionContext = null;
+
+    /**
+     * Used for serialized format versioning.
+     * 1 = first version where this is being tracked.
+     *
+     * NOTE: You must increment this version whenever any changes are made
+     * to the serialized representation of this class between releases
+     */
+    private static final Short SERIALIZED_FORM_VERSION = Short.valueOf("1");
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The session identifier of the parent SipApplicationSession, if any
+     */
+    private String sipAppSessionId = null;
+
+    /**
+     * The BEKEY of this session, or <tt>null</tt>.
+     *
+     * <p>The BEKEY is used by the Converged Loadbalancer (CLB) in DCR mode
+     * for loadbalancing purposes, and supplied to the web container in the
+     * form of a request header.
+     *
+     * <p>See https://sailfin.dev.java.net/issues/show_bug.cgi?id=1647
+     * for additional details
+     */
+    private String beKey;
+
+    /**
+     * The collection of user data attributes associated with this Session.
+     */
+    protected Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();    
+
+    /**
+     * The authentication type used to authenticate our cached Principal,
+     * if any.  NOTE:  This value is not included in the serialized
+     * version of this object.
+     */
+    protected transient String authType = null;
+
+    /**
+     * The <code>java.lang.Method</code> for the
+     * <code>fireContainerEvent()</code> method of the
+     * <code>org.apache.catalina.core.StandardContext</code> method,
+     * if our Context implementation is of this class.  This value is
+     * computed dynamically the first time it is needed, or after
+     * a session reload (since it is declared transient).
+     */
+    protected transient Method containerEventMethod = null;
+
+    /**
+     * The time this session was created, in milliseconds since midnight,
+     * January 1, 1970 GMT.
+     */
+    protected long creationTime = 0L;
+
+    /**
+     * The debugging detail level for this component.  NOTE:  This value
+     * is not included in the serialized version of this object.
+     */
+    protected transient int debug = 0;
+
+    /**
+     * We are currently processing a session expiration, so bypass
+     * certain IllegalStateException tests.  NOTE:  This value is not
+     * included in the serialized version of this object.
+     */
+    protected transient boolean expiring = false;
+
+    /**
+     * The facade associated with this session.  NOTE:  This value is not
+     * included in the serialized version of this object.
+     */
+    protected transient StandardSessionFacade facade = null;
+
+    /**
+     * The session identifier of this Session.
+     */
+    protected String id = null;
+
+    /**
+     * The last accessed time for this Session.
+     */
+    protected long lastAccessedTime = creationTime;
+
+    /**
+     * The session event listeners for this Session.
+     */
+    protected transient ArrayList<SessionListener> listeners =
+        new ArrayList<SessionListener>();
+
+    /**
+     * The Manager with which this Session is associated.
+     */
+    protected transient Manager manager = null;
+
+    /**
+     * The context with which this Session is associated.
+     */
+    protected transient StandardContext context = null;
+
+    /**
+     * The maximum time interval, in seconds, between client requests before
+     * the servlet container may invalidate this session.  A negative time
+     * indicates that the session should never time out.
+     */
+    protected int maxInactiveInterval = -1;
+
+    /**
+     * Flag indicating whether this session is new or not.
+     */
+    protected boolean isNew = false;
+
+    /**
+     * Flag indicating whether this session is valid or not.
+     */
+    protected boolean isValid = false;
+
+    /**
+     * Internal notes associated with this session by Catalina components
+     * and event listeners.  <b>IMPLEMENTATION NOTE:</b> This object is
+     * <em>not</em> saved and restored across session serializations!
+     */
+    protected transient Map<String, Object> notes = new Hashtable<String, Object>();
+
+    /**
+     * The authenticated Principal associated with this session, if any.
+     // START SJSWS 6371339
+     // * <b>IMPLEMENTATION NOTE:</b>  This object is <i>not</i> saved and
+     // * restored across session serializations!
+     // END SJSWS 6371339
+     */
+    protected transient Principal principal = null;
+
+    /**
+     * The current accessed time for this session.
+     */
+    protected long thisAccessedTime = creationTime;
+
+    /**
+     * The session version, incremented and used by in-memory-replicating
+     * session managers
+     */
+    protected AtomicLong version = new AtomicLong(-1);
+
+    /**
+     * single sign on id. It is null if there is no SSO.
+     */
+    protected String ssoId = null;
+
+    /**
+     * single sign on version.
+     */
+    protected volatile long ssoVersion = 0L;
+
+
+    // ----------------------------------------------------- Session Properties
+
+
+    /**
+     * Return the authentication type used to authenticate our cached
+     * Principal, if any.
+     */
+    public String getAuthType() {
+
+        return (this.authType);
+
+    }
+
+
+    /**
+     * Set the authentication type used to authenticate our cached
+     * Principal, if any.
+     *
+     * @param authType The new cached authentication type
+     */
+    public void setAuthType(String authType) {
+
+        this.authType = authType;
+    }
+
+
+    /**
+     * Set the creation time for this session.  This method is called by the
+     * Manager when an existing Session instance is reused.
+     *
+     * @param time The new creation time
+     */
+    public void setCreationTime(long time) {
+
+        this.creationTime = time;
+        this.lastAccessedTime = time;
+        this.thisAccessedTime = time;
+
+    }
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getId() {
+
+        return getIdInternal();
+
+    }
+
+
+    /**
+     * Return the session identifier for this session.
+     */
+    public String getIdInternal() {
+
+        return (this.id);
+
+    }
+
+
+    /**
+     * Set the session identifier for this session.
+     *
+     * @param id The new session identifier
+     */
+    public void setId(String id) {
+
+        if ((this.id != null) && (manager != null))
+            manager.remove(this);
+
+        String oldId = this.id;
+        this.id = id;
+
+        if (manager != null)
+            manager.add(this);
+
+        
+        // Notify interested session event listeners
+        if (oldId == null) { // the session is just created
+            tellNew();
+        } else { // change session id
+            HttpSessionEvent event = null;
+            List<EventListener> listeners = context.getApplicationEventListeners();
+            if (listeners.isEmpty()) {
+                return;
+            }
+            Iterator<EventListener> iter = listeners.iterator();
+            while (iter.hasNext()) {
+                EventListener eventListener = iter.next();
+                if (!(eventListener instanceof HttpSessionIdListener)) {
+                    continue;
+                }
+                HttpSessionIdListener listener = (HttpSessionIdListener)eventListener;
+                try {
+                    fireContainerEvent(context,
+                                       "beforeSessionIdChanged",
+                                       listener);
+                    if (event == null) {
+                        event = new HttpSessionEvent(getSession());
+                    }
+                    listener.sessionIdChanged(event, oldId);
+                    fireContainerEvent(context,
+                                       "afterSessionIdChanged",
+                                       listener);
+                } catch (Throwable t) {
+                    try {
+                        fireContainerEvent(context,
+                                           "afterSessionIdChanged",
+                                           listener);
+                    } catch (Exception e) {
+                        // Ignore
+                    }
+                    log(rb.getString(LogFacade.SESSION_ID_CHANGE_EVENT_LISTENER_EXCEPTION), t);
+                }
+            }
+        }
+    }
+
+
+/**
+     * Sets the BEKEY for this session
+     *
+     * <p>The BEKEY is used by the Converged Loadbalancer (CLB) in DCR mode
+     * for loadbalancing purposes, and supplied to the web container in the
+     * form of a request header.
+     *
+     * @param beKey the BEKEY for this session, or <tt>null</tt> if not
+     * present
+     */
+    public void setBeKey(String beKey) {
+        this.beKey = beKey;
+    }
+
+
+    /**
+     * Gets the BEKEY of this session
+     *
+     * @return the BEKEY of this session, or <tt>null</tt> if not present
+     */
+    public String getBeKey() {
+        return beKey;
+    }
+
+
+    /**
+     * Sets the id of the SipApplicationSession that is the parent of this
+     * StandardSession.
+     *
+     * @param id SipApplicationSession id
+     */
+    public void setSipApplicationSessionId(String id) {
+        sipAppSessionId = id;
+    }
+
+
+    /**
+     * Gets the id of the SipApplicationSession that is the parent of this
+     * StandardSession.
+     *
+     * @return The SipApplicationSession id, or null if this
+     * StandardSession does not have any SipApplicationSession parent
+     */
+    public String getSipApplicationSessionId() {
+        return sipAppSessionId;
+    }
+
+
+    /**
+     * Inform the listeners about the new session.
+     *
+     */
+    public void tellNew() {
+
+        // Notify interested session event listeners
+        fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
+
+        HttpSessionEvent event = new HttpSessionEvent(getSession());
+
+        // Notify interested application event listeners
+        for (HttpSessionListener listener : context.getSessionListeners()) {
+            try {
+                fireContainerEvent(context, "beforeSessionCreated",
+                                   listener);
+                listener.sessionCreated(event);
+                fireContainerEvent(context, "afterSessionCreated",
+                                   listener);
+            } catch (Throwable t) {
+                try {
+                    fireContainerEvent(context, "afterSessionCreated",
+                                       listener);
+                } catch (Exception e) {
+                    // Ignore
+                }
+                log(rb.getString(LogFacade.SESSION_EVENT_LISTENER_EXCEPTION), t);
+            }
+        }
+    }
+
+
+    /**
+     * Return descriptive information about this Session implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    /**
+     * Return the last time the client sent a request associated with this
+     * session, as the number of milliseconds since midnight, January 1, 1970
+     * GMT.  Actions that your application takes, such as getting or setting
+     * a value associated with the session, do not affect the access time.
+     */
+    public long getLastAccessedTime() {
+        if ( !isValid() ) {
+            throw new IllegalStateException
+                ("getLastAccessedTime: " + rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+        }
+        return (this.lastAccessedTime);
+
+    }
+    
+
+    // START SJSAS 6470831
+    /**
+     * Same as getLastAccessedTime(), except that there is no call to 
+     * isValid(), which may expire the session and cause any subsequent
+     * session access to throw an IllegalStateException.
+     */
+    public long getLastAccessedTimeInternal() {
+        return this.lastAccessedTime;
+    }
+    // END SJSAS 6470831
+
+    
+    /**
+     * Set the last time the client sent a request associated with this
+     * session, as the number of milliseconds since midnight, January 1, 1970
+     * GMT.  Actions that your application takes, such as getting or setting
+     * a value associated with the session, do not affect the access time.
+     * HERCULES: added method
+     */	
+    public void setLastAccessedTime(long lastAcessedTime) {
+        this.lastAccessedTime = lastAcessedTime;
+    }    
+
+
+    /**
+     * Return the Manager within which this Session is valid.
+     */
+    public Manager getManager() {
+
+        return (this.manager);
+
+    }
+
+
+    /**
+     * Set the Manager within which this Session is valid.
+     *
+     * @param manager The new Manager
+     */
+    public void setManager(Manager manager) {
+        this.manager = manager;
+        context = (StandardContext) manager.getContainer();
+    }
+
+
+    /**
+     * Return the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session.  A negative
+     * time indicates that the session should never time out.
+     */
+    public int getMaxInactiveInterval() {
+
+        return (this.maxInactiveInterval);
+
+    }
+
+
+    /**
+     * Set the maximum time interval, in seconds, between client requests
+     * before the servlet container will invalidate the session.  A negative
+     * time indicates that the session should never time out.
+     *
+     * @param interval The new maximum interval
+     */
+    public void setMaxInactiveInterval(int interval) {
+
+        this.maxInactiveInterval = interval;
+        if (isValid && interval == 0) {
+            expire();
+        }
+
+    }
+
+
+    /**
+     * Set the <code>isNew</code> flag for this session.
+     *
+     * @param isNew The new value for the <code>isNew</code> flag
+     */
+    public void setNew(boolean isNew) {
+
+        this.isNew = isNew;
+
+    }
+
+
+    /**
+     * Return the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.  If there
+     * is no current associated Principal, return <code>null</code>.
+     */
+    public Principal getPrincipal() {
+
+        return (this.principal);
+
+    }
+
+
+    /**
+     * Set the authenticated Principal that is associated with this Session.
+     * This provides an <code>Authenticator</code> with a means to cache a
+     * previously authenticated Principal, and avoid potentially expensive
+     * <code>Realm.authenticate()</code> calls on every request.
+     *
+     * @param principal The new Principal, or <code>null</code> if none
+     */
+    public void setPrincipal(Principal principal) {
+
+        this.principal = principal;
+    }
+
+
+    /**
+     * Return the <code>HttpSession</code> for which this object
+     * is the facade.
+     */
+    public HttpSession getSession() {
+
+        if (facade == null){
+            if (SecurityUtil.isPackageProtectionEnabled()){
+                final StandardSession fsession = this;
+                facade = AccessController.doPrivileged(
+                        new PrivilegedAction<StandardSessionFacade>(){
+                    public StandardSessionFacade run(){
+                        return new StandardSessionFacade(fsession);
+                    }
+                });
+            } else {
+                facade = new StandardSessionFacade(this);
+            }
+        }
+        return (facade);
+
+    }
+
+
+    /**
+     * Return the <code>isValid</code> flag for this session.
+     */
+    public boolean isValid() {
+
+        if (this.expiring){
+            return true;
+        }
+
+        if (!this.isValid ) {
+            return false;
+        }
+
+        if (isForegroundLocked()) {
+            return true;
+        }
+
+        /* SJSAS 6329289
+        if (maxInactiveInterval >= 0) { 
+            long timeNow = System.currentTimeMillis();
+            int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);
+            if (timeIdle >= maxInactiveInterval) {
+                expire(true);
+            }
+        }
+        */
+        // START SJSAS 6329289
+        if (hasExpired()) {
+            expire(true);
+        }
+        // END SJSAS 6329289
+
+        return (this.isValid);
+    }
+
+    // START CR 6363689
+    public boolean getIsValid() {
+        return this.isValid; 
+    }    
+    // END CR 6363689    
+
+    /**
+     * Set the <code>isValid</code> flag for this session.
+     *
+     * @param isValid The new value for the <code>isValid</code> flag
+     */
+    public void setValid(boolean isValid) {
+
+        this.isValid = isValid;
+        //SJSAS 6406580 START
+        if (!isValid && (getManager() instanceof PersistentManagerBase)) {
+            ((PersistentManagerBase) getManager()).addToInvalidatedSessions(this.id);            
+        }
+        //SJSAS 6406580 END        
+    }
+
+
+    // ------------------------------------------------- Session Public Methods
+
+
+    /**
+     * Update the accessed time information for this session.  This method
+     * should be called by the context when a request comes in for a particular
+     * session, even if the application does not reference it.
+     */
+    public void access() {
+        this.lastAccessedTime = this.thisAccessedTime;
+        this.thisAccessedTime = System.currentTimeMillis();
+
+        evaluateIfValid();
+    }
+
+
+    /**
+     * End the access.
+     */
+    public void endAccess() {
+        isNew = false;
+    }
+
+
+    /**
+     * Add a session event listener to this component.
+     */
+    public void addSessionListener(SessionListener listener) {
+
+        synchronized (listeners) {
+            listeners.add(listener);
+        }
+
+    }
+
+
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     */
+    public void expire() {
+
+        expire(true);
+
+    }
+    
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     *
+     * @param notify Should we notify listeners about the demise of
+     *  this session?
+     */
+    public void expire(boolean notify) {
+        expire(notify, true);
+    }
+    
+    /**
+     * Perform the internal processing required to invalidate this session,
+     * without triggering an exception if the session has already expired.
+     *
+     * @param notify Should we notify listeners about the demise of
+     *  this session?
+     * @param persistentRemove should we call store to remove the session
+     *  if available
+     */
+    public void expire(boolean notify, boolean persistentRemove) {
+
+        // Mark this session as "being expired" if needed
+        if (expiring)
+            return;
+
+        synchronized (this) {
+
+            if (manager == null)
+                return;
+
+            expiring = true;
+        
+            // Notify interested application event listeners
+            // FIXME - Assumes we call listeners in reverse order
+
+            // The call to expire() may not have been triggered by the webapp.
+            // Make sure the webapp's class loader is set when calling the
+            // listeners
+            ClassLoader oldTccl = null;
+            if (context.getLoader() != null &&
+                    context.getLoader().getClassLoader() != null) {
+                oldTccl = Thread.currentThread().getContextClassLoader();
+                if (Globals.IS_SECURITY_ENABLED) {
+                    PrivilegedAction<Void> pa = new PrivilegedSetTccl(
+                            context.getLoader().getClassLoader());
+                    AccessController.doPrivileged(pa);
+                } else {
+                    Thread.currentThread().setContextClassLoader(
+                            context.getLoader().getClassLoader());
+                }
+            }
+            try {
+                List<HttpSessionListener> listeners = context.getSessionListeners();
+                if (notify && !listeners.isEmpty()) {
+                    HttpSessionEvent event = new HttpSessionEvent(getSession());
+                    int len = listeners.size();
+                    for (int i = 0; i < len; i++) {
+                        // Invoke in reverse order of declaration
+                        HttpSessionListener listener = listeners.get((len - 1) - i);
+                        try {
+                            fireContainerEvent(context,
+                                               "beforeSessionDestroyed",
+                                               listener);
+                            listener.sessionDestroyed(event);
+                            fireContainerEvent(context,
+                                               "afterSessionDestroyed",
+                                               listener);
+                        } catch (Throwable t) {
+                            try {
+                                fireContainerEvent(context,
+                                                   "afterSessionDestroyed",
+                                                   listener);
+                            } catch (Exception e) {
+                                // Ignore
+                            }
+                            // FIXME - should we do anything besides log these?
+                            log(rb.getString(LogFacade.SESSION_EVENT_LISTENER_EXCEPTION), t);
+                        }
+                    }
+                }
+            } finally {
+                if (oldTccl != null) {
+                    if (Globals.IS_SECURITY_ENABLED) {
+                        PrivilegedAction<Void> pa =
+                            new PrivilegedSetTccl(oldTccl);
+                        AccessController.doPrivileged(pa);
+                    } else {
+                        Thread.currentThread().setContextClassLoader(oldTccl);
+                    }
+                }
+            }
+
+            setValid(false);
+
+            /*
+             * Compute how long this session has been alive, and update
+             * session manager's related properties accordingly
+             */
+            long timeNow = System.currentTimeMillis();
+            int timeAlive = (int) ((timeNow - creationTime)/1000);
+            synchronized (manager) {
+                if (timeAlive > manager.getSessionMaxAliveTimeSeconds()) {
+                    manager.setSessionMaxAliveTimeSeconds(timeAlive);
+                }
+                int numExpired = manager.getExpiredSessions();
+                numExpired++;
+                manager.setExpiredSessions(numExpired);
+                int average = manager.getSessionAverageAliveTimeSeconds();
+                average = ((average * (numExpired-1)) + timeAlive)/numExpired;
+                manager.setSessionAverageAliveTimeSeconds(average);
+            }
+            
+            // Remove this session from our manager's active sessions
+            if(persistentRemove) {
+                manager.remove(this);
+            } else {
+                if(manager instanceof PersistentManagerBase) {
+                    ((PersistentManagerBase)manager).remove(this, false);
+                }
+            }            
+
+            /*
+             * Mark session as expired *before* removing its attributes, so
+             * that its HttpSessionBindingListener objects will get an
+             * IllegalStateException when accessing the session attributes
+             * from within their valueUnbound() method
+             */ 
+            expiring = false;
+
+            // Unbind any objects associated with this session
+            String keys[] = keys();
+            for (int i = 0; i < keys.length; i++)
+                removeAttribute(keys[i], notify, false);
+
+            // Notify interested session event listeners
+            if (notify) {
+                context.sessionExpiredEvent(this);
+                fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
+            }
+
+        }
+
+    }    
+    
+    /**
+     * Perform the internal processing required to passivate
+     * this session.
+     */
+    public void passivate() {
+
+        context.sessionPassivatedStartEvent(this);
+
+        try {
+            // Notify ActivationListeners
+            HttpSessionEvent event = null;
+            String keys[] = keys();
+            for (int i = 0; i < keys.length; i++) {
+                Object attribute = getAttributeInternal(keys[i]);
+                if (attribute instanceof HttpSessionActivationListener) {
+                    if (event == null)
+                        event = new HttpSessionEvent(getSession());
+                    // FIXME: Should we catch throwables?
+                    ((HttpSessionActivationListener)attribute).sessionWillPassivate(event);
+                }
+            }
+        } finally {
+            context.sessionPassivatedEndEvent(this);
+        }
+    }
+
+
+    /**
+     * Perform internal processing required to activate this
+     * session.
+     */
+    public void activate() {
+
+        context.sessionActivatedStartEvent(this);
+
+        try {
+            // Notify ActivationListeners
+            HttpSessionEvent event = null;
+            String keys[] = keys();
+            for (int i = 0; i < keys.length; i++) {
+                Object attribute = getAttributeInternal(keys[i]);
+                if (attribute instanceof HttpSessionActivationListener) {
+                    if (event == null)
+                        event = new HttpSessionEvent(getSession());
+                    // FIXME: Should we catch throwables?
+                    ((HttpSessionActivationListener)attribute).sessionDidActivate(event);
+                }
+            }
+        } finally {
+            context.sessionActivatedEndEvent(this);
+        }
+    }
+
+
+    /**
+     * Return the object bound with the specified name to the internal notes
+     * for this session, 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 session.
+     */
+    public Iterator<String> getNoteNames() {
+        return (notes.keySet().iterator());
+    }
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle() {
+
+        // Reset the instance variables associated with this Session
+        attributes.clear();
+        setAuthType(null);
+        creationTime = 0L;
+        expiring = false;
+        id = null;
+        lastAccessedTime = 0L;
+        maxInactiveInterval = -1;
+        notes.clear();
+        setPrincipal(null);
+        isNew = false;
+        isValid = false;
+
+        listeners.clear();
+
+        manager = null;
+
+    }
+
+
+    /**
+     * Remove any object bound to the specified name in the internal notes
+     * for this session.
+     *
+     * @param name Name of the note to be removed
+     */
+    public void removeNote(String name) {
+        notes.remove(name);
+    }
+
+
+    /**
+     * Remove a session event listener from this component.
+     */
+    public void removeSessionListener(SessionListener listener) {
+
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+
+    }
+
+
+    /**
+     * Bind an object to a specified name in the internal notes associated
+     * with this session, 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);
+    }
+
+
+    // START SJSAS 6329289
+    /**
+     * Checks whether this Session has expired.
+     *
+     * @return true if this Session has expired, false otherwise
+     */
+    public boolean hasExpired() {
+
+        if (maxInactiveInterval >= 0
+                && (System.currentTimeMillis() - thisAccessedTime >=
+                    maxInactiveInterval * 1000L)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+    // END SJSAS 6329289
+
+
+    /** 
+     * Increments the version number
+     */
+    public long incrementVersion() {
+        return version.incrementAndGet();
+    } 
+
+    
+    /** 
+     * Gets the version number
+     */    
+    public long getVersion() {
+        return version.get();
+    }
+    
+
+    /** 
+     * Sets the version number
+     */    
+    public void setVersion(long value) {
+        version.set(value);
+    }    
+
+
+    /**
+     * Return the single sign on id.
+     * It is null if there is no SSO.
+     */
+    public String getSsoId() {
+        return ssoId;
+    }
+
+
+    /**
+     * Set the single sign on id.
+     */
+    public void setSsoId(String ssoId) {
+        this.ssoId = ssoId;
+    }
+
+
+    /**
+     * Return the single sign on version.
+     */
+    public long getSsoVersion() {
+        return ssoVersion;
+    }
+
+
+    /**
+     * Set the single sign on version.
+     */
+    public void setSsoVersion(long value) {
+        ssoVersion = value;
+    }
+
+
+    /**
+     * Return a string representation of this object.
+     */
+    public String toString() {
+
+        // STARTS S1AS
+        /*
+        StringBuilder sb = new StringBuilder();
+        sb.append("StandardSession[");
+        sb.append(id);
+        sb.append("]");
+        return (sb.toString());
+        */
+        // END S1AS
+        // START S1AS
+        StringBuilder sb = null;
+
+        if(!this.isValid) {
+            sb = new StringBuilder();
+        } else {
+            sb = new StringBuilder(1000);
+        }
+
+        sb.append("StandardSession[");
+        sb.append(id);
+        sb.append("]");
+        
+        if (this.isValid) {
+            Enumeration<String> attrNamesEnum = getAttributeNames();
+            while(attrNamesEnum.hasMoreElements()) {
+                String nextAttrName = attrNamesEnum.nextElement();
+                Object nextAttrValue = getAttribute(nextAttrName);
+                sb.append("\n");
+                sb.append("attrName = " + nextAttrName);
+                sb.append(" : attrValue = " + nextAttrValue);
+            }
+        }
+
+        return sb.toString();
+        // END S1AS
+    }
+
+
+    // ------------------------------------------------ Session Package Methods
+
+
+    /**
+     * Creates a StandardSession instance from the given ObjectInputStream,
+     * and returns it.
+     *
+     * If ObjectInputStream does not contain a serialized StandardSession
+     * (or one of its subclasses), this method will create an empty session
+     * and populate it with the serialized data (this is for backwards
+     * compatibility).
+     *
+     * @param ois The ObjectInputStream from which to read the serialized
+     *        session data
+     * @param manager The session manager from which to create an empty
+     *        session if needed
+     *
+     * @return The restored session
+     *
+     * @exception ClassNotFoundException If the class for an object being
+     *            restored cannot be found.
+     * @exception IOException if I/O errors occur
+     */
+    static StandardSession deserialize(ObjectInputStream ois,
+                                       Manager manager)
+            throws ClassNotFoundException, IOException {
+
+        StandardSession result = null;
+
+        Object obj = ois.readObject();
+        if (obj instanceof StandardSession) {
+            // New format following standard serialization
+            result = (StandardSession) obj;
+        } else {
+            // Old format, obj is an instance of Long and contains the
+            // session's creation time
+            result = (StandardSession) manager.createEmptySession();
+            result.setCreationTime(((Long) obj).longValue());
+            result.readRemainingObject(ois);
+        }
+
+        return result;
+    }
+
+
+    // ------------------------------------------------- HttpSession Properties
+
+
+    /**
+     * Return the time when this session was created, in milliseconds since
+     * midnight, January 1, 1970 GMT.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public long getCreationTime() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                ("getCreationTime: " + rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+
+        return (this.creationTime);
+
+    }
+
+
+    /**
+     * Return the ServletContext to which this session belongs.
+     */
+    public ServletContext getServletContext() {
+
+        if (manager == null)
+            return (null);
+        if (context == null)
+            return (null);
+        else
+            return (context.getServletContext());
+
+    }
+
+
+    /**
+     * Return the session context with which this session is associated.
+     *
+     * @deprecated As of Version 2.1, this method is deprecated and has no
+     *  replacement.  It will be removed in a future version of the
+     *  Java Servlet API.
+     */
+    public HttpSessionContext getSessionContext() {
+        if (sessionContext == null)
+            sessionContext = new StandardSessionContext();
+        return (sessionContext);
+
+    }
+
+
+    // ----------------------------------------------HttpSession Public Methods
+
+
+    /**
+     * Return the object bound with the specified name in this session, or
+     * <code>null</code> if no object is bound with that name.
+     *
+     * @param name Name of the attribute to be returned
+     *                                                                                   * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public Object getAttribute(String name) {
+
+        if (!isValid())
+            throw new IllegalStateException
+                ("getAttribute: " + rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+
+        if (name == null) return null;
+
+        return (attributes.get(name));
+    }
+
+    @Override
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * Return an <code>Enumeration</code> of <code>String</code> objects
+     * containing the names of the objects bound to this session.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public Enumeration<String> getAttributeNames() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                ("getAttributeNames: " + rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+
+
+        return (new Enumerator<String>(attributes.keySet(), true));
+
+    }
+
+
+    /**
+     * Return the object bound with the specified name in this session, or
+     * <code>null</code> if no object is bound with that name.
+     *
+     * @param name Name of the value to be returned
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     *
+     * @deprecated As of Version 2.2, this method is replaced by
+     *  <code>getAttribute()</code>
+     */
+    public Object getValue(String name) {
+
+        return (getAttribute(name));
+
+    }
+
+
+    /**
+     * Return the set of names of objects bound to this session.  If there
+     * are no such objects, a zero-length array is returned.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     *
+     * @deprecated As of Version 2.2, this method is replaced by
+     *  <code>getAttributeNames()</code>
+     */
+    public String[] getValueNames() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                ("getValueNames: " + rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+
+        return (keys());
+
+    }
+    
+    
+// ------------------------session locking --HERCULES:add-------------------
+    
+    /**
+     * get this session locked for foreground
+     * if the session is found to be presently background
+     * locked; retry logic in a time-decay polling loop
+     * waits for background lock to clear
+     * after 6 attempts (12.6 seconds) it unlocks the
+     * session and acquires the foreground lock
+     */         
+    protected boolean getSessionLockForForeground() {
+        boolean result = false;
+        StandardSession sess = (StandardSession) this;       
+        //now lock the session
+        //System.out.println("IN LOCK_SESSION_FOR_FOREGROUND: sess =" + sess);        
+        long pollTime = 200L;
+        int tryNumber = 0;
+        int numTries = 7;
+        boolean keepTrying = true;
+        boolean lockResult = false;
+        //System.out.println("locking session: sess =" + sess);
+        //try to lock up to numTries (i.e. 7) times
+        //poll and wait starting with 200 ms
+        while(keepTrying) {
+            lockResult = sess.lockForeground();
+            if(lockResult) {
+                keepTrying = false;
+                result = true;
+                break;
+            }
+            tryNumber++;
+            if(tryNumber < (numTries - 1) ) {
+                pollTime = pollTime * 2L;
+            } else {
+                //unlock the background so we can take over
+                //FIXME: need to log warning for this situation
+                sess.unlockBackground();
+            }              
+        }
+        //System.out.println("finished locking session: sess =" + sess);
+        //System.out.println("LOCK = " + sess.getSessionLock());
+        return result;
+    } 
+     
+    /**
+     * return whether this session is currently foreground locked
+     */    
+    public boolean isForegroundLocked() {
+        //in this case we are not using locks
+        //so just return false
+        if(_sessionLock == null)
+            return false;        
+        synchronized(sessionLockMonitor) {
+            return _sessionLock.isForegroundLocked();
+        } 
+    }    
+    
+    /**
+     * lock the session for foreground
+     * returns true if successful; false if unsuccessful
+     */       
+    public boolean lockBackground() {
+        //in this case we are not using locks
+        //so just return true
+        if(_sessionLock == null)
+            return true;
+        synchronized(sessionLockMonitor) {
+            return _sessionLock.lockBackground();
+        }
+    }
+    
+    /**
+     * lock the session for background
+     * returns true if successful; false if unsuccessful
+     */     
+    public boolean lockForeground() {
+        //in this case we are not using locks
+        //so just return true
+        if(_sessionLock == null)
+            return true;
+        synchronized(sessionLockMonitor) {
+            return _sessionLock.lockForeground();
+        }
+    }
+    
+    /**
+     * unlock the session completely
+     * irregardless of whether it was foreground or background locked
+     */     
+    public void unlockForegroundCompletely() {
+        //in this case we are not using locks
+        //so just return true
+        if(_sessionLock == null)
+            return;
+        synchronized(sessionLockMonitor) {
+            _sessionLock.unlockForegroundCompletely();
+        }
+    }
+    
+    /**
+     * unlock the session from foreground
+     */      
+    public void unlockForeground() {
+        //in this case we are not using locks
+        //so just return true
+        if(_sessionLock == null)
+            return;
+        synchronized(sessionLockMonitor) {
+            _sessionLock.unlockForeground();
+        }
+    } 
+    
+    /**
+     * unlock the session from background
+     */     
+    public void unlockBackground() {
+        //in this case we are not using locks
+        //so just return true
+        if(_sessionLock == null)
+            return;
+        synchronized(sessionLockMonitor) {
+            _sessionLock.unlockBackground();
+        }
+    }    
+
+    /**
+     * return the Session lock
+     */     
+    public SessionLock getSessionLock() {
+        return _sessionLock;
+    }    
+    
+    /**
+     * set the Session lock
+     * @param sessionLock
+     */     
+    public void setSessionLock(SessionLock sessionLock) {
+        _sessionLock = sessionLock;
+    }
+    
+    /**
+     * @return true if this session has been locked by any
+     * out-of-band (i.e., non-http) request, false otherwise
+     */      
+    public boolean hasNonHttpLockOccurred() {
+        //in this case we are not using locks
+        //so just return false
+        if(_sessionLock == null)
+            return false;
+        synchronized(sessionLockMonitor) {
+            return _sessionLock.hasNonHttpLockOccurred();
+        }
+    }
+
+    protected transient SessionLock _sessionLock = new SessionLock();
+
+    protected final Object sessionLockMonitor = new Object();
+
+// ------------------------end session locking ---HERCULES:add--------        
+    
+
+
+    /**
+     * Invalidates this session and unbinds any objects bound to it.
+     *
+     * @exception IllegalStateException if this method is called on
+     *  an invalidated session
+     * HERCULES:modified method
+     */
+    public void invalidate() {
+
+        if (!isValid)
+            throw new IllegalStateException
+                ("invalidate: " + rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+        //make sure foreground locked first
+        if(!this.isForegroundLocked()) {
+            this.getSessionLockForForeground();
+        }
+        // Cause this session to expire
+        try {
+            expire();
+        } finally {
+            this.unlockForeground();
+        }
+
+    } 
+
+
+    /**
+     * Return <code>true</code> if the client does not yet know about the
+     * session, or if the client chooses not to join the session.  For
+     * example, if the server used only cookie-based sessions, and the client
+     * has disabled the use of cookies, then a session would be new on each
+     * request.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public boolean isNew() {
+
+        if (!isValid())
+            throw new IllegalStateException
+                ("isNew: " + rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+
+        return (this.isNew);
+
+    }
+
+
+    /**
+     * Bind an object to this session, using the specified name.  If an object
+     * of the same name is already bound to this session, the object is
+     * replaced.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueBound()</code> on the object.
+     *
+     * @param name Name to which the object is bound, cannot be null
+     * @param value Object to be bound, cannot be null
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     *
+     * @deprecated As of Version 2.2, this method is replaced by
+     *  <code>setAttribute()</code>
+     */
+    public void putValue(String name, Object value) {
+
+        setAttribute(name, value);
+
+    }
+
+
+    /**
+     * Remove the object bound with the specified name from this session.  If
+     * the session does not have an object bound with this name, this method
+     * does nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     *
+     * @param name Name of the object to remove from this session.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public void removeAttribute(String name) {
+
+        removeAttribute(name, true, true);
+
+    }
+
+
+    /**
+     * Remove the object bound with the specified name from this session.  If
+     * the session does not have an object bound with this name, this method
+     * does nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     *
+     * @param name Name of the object to remove from this session.
+     * @param notify Should we notify interested listeners that this
+     *  attribute is being removed?
+     * @param checkValid Indicates whether IllegalStateException must be
+     * thrown if session has already been invalidated 
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public void removeAttribute(String name, boolean notify, 
+                                boolean checkValid) {
+
+        if (name == null) return;
+
+        // Validate our current state
+        if (!isValid() && checkValid)
+            throw new IllegalStateException
+                ("removeAttribute: " + rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+
+        // Remove this attribute from our collection
+        Object value = attributes.remove(name);
+
+        // Do we need to do valueUnbound() and attributeRemoved() notification?
+        if (!notify || (value == null)) {
+            return;
+        }
+
+        // Call the valueUnbound() method if necessary
+        HttpSessionBindingEvent event = null;
+        if (value instanceof HttpSessionBindingListener) {
+            event = new HttpSessionBindingEvent(getSession(), name, value);
+            try {
+                context.fireContainerEvent(
+                    ContainerEvent.BEFORE_SESSION_VALUE_UNBOUND, null);
+                ((HttpSessionBindingListener) value).valueUnbound(event);
+                context.fireContainerEvent(
+                    ContainerEvent.AFTER_SESSION_VALUE_UNBOUND, null);
+            } catch (Throwable t) {
+                // Log exception
+                context.fireContainerEvent(
+                    ContainerEvent.AFTER_SESSION_VALUE_UNBOUND, null);
+            }
+        }
+        
+        // Notify special event listeners on removeAttribute
+        //HERCULES:add
+        // fire container event        
+        context.fireContainerEvent("sessionRemoveAttributeCalled", event);
+        // fire sync container event if name equals SYNC_STRING
+        if (SYNC_STRING.equals(name)) {
+            context.fireContainerEvent("sessionSync",  (new HttpSessionBindingEvent(getSession(), name)));
+        }         
+        //END HERCULES:add         
+
+        // Notify interested application event listeners
+        List<EventListener> listeners = context.getApplicationEventListeners();
+        if (listeners.isEmpty()) {
+            return;
+        }
+        Iterator<EventListener> iter = listeners.iterator();
+        while (iter.hasNext()) {
+            EventListener eventListener = iter.next();
+            if (!(eventListener instanceof HttpSessionAttributeListener)) {
+                continue;
+            }
+            HttpSessionAttributeListener listener =
+                (HttpSessionAttributeListener) eventListener;
+            try {
+                fireContainerEvent(context,
+                                   "beforeSessionAttributeRemoved",
+                                   listener);
+                if (event == null) {
+                    event = new HttpSessionBindingEvent(getSession(), name, value);
+                }
+                listener.attributeRemoved(event);
+                fireContainerEvent(context,
+                                   "afterSessionAttributeRemoved",
+                                   listener);
+            } catch (Throwable t) {
+                try {
+                    fireContainerEvent(context,
+                                       "afterSessionAttributeRemoved",
+                                       listener);
+                } catch (Exception e) {
+                    // Ignore
+                }
+                log(rb.getString(LogFacade.SESSION_ATTRIBUTE_EVENT_LISTENER_EXCEPTION), t);
+            }
+        }
+
+    }
+
+
+    /**
+     * Remove the object bound with the specified name from this session.  If
+     * the session does not have an object bound with this name, this method
+     * does nothing.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueUnbound()</code> on the object.
+     *
+     * @param name Name of the object to remove from this session.
+     *
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     *
+     * @deprecated As of Version 2.2, this method is replaced by
+     *  <code>removeAttribute()</code>
+     */
+    public void removeValue(String name) {
+
+        removeAttribute(name);
+
+    }
+
+
+    /**
+     * Bind an object to this session, using the specified name.  If an object
+     * of the same name is already bound to this session, the object is
+     * replaced.
+     * <p>
+     * After this method executes, and if the object implements
+     * <code>HttpSessionBindingListener</code>, the container calls
+     * <code>valueBound()</code> on the object.
+     *
+     * @param name Name to which the object is bound, cannot be null
+     * @param value Object to be bound, cannot be null
+     *
+     * @exception IllegalArgumentException if an attempt is made to add a
+     *  non-serializable object in an environment marked distributable.
+     * @exception IllegalStateException if this method is called on an
+     *  invalidated session
+     */
+    public void setAttribute(String name, Object value) {
+
+        // Name cannot be null
+        if (name == null)
+            throw new IllegalArgumentException
+                (rb.getString(LogFacade.NAME_PARAMETER_CANNOT_BE_NULL_EXCEPTION));
+
+        // Null value is the same as removeAttribute()
+        if (value == null) {
+            removeAttribute(name);
+            return;
+        }
+
+        // Validate our current state
+        if (!isValid()) {
+            throw new IllegalStateException
+                ("setAttribute: "+ rb.getString(LogFacade.SESSION_INVALIDATED_EXCEPTION));
+        }
+
+        if (manager != null) {
+            manager.checkSessionAttribute(name, value);
+        }
+
+        // Construct an event with the new value
+        HttpSessionBindingEvent event = null;
+
+        // Call the valueBound() method if necessary
+        if (value instanceof HttpSessionBindingListener) {
+            event = new HttpSessionBindingEvent(getSession(), name, value);
+            try {
+                ((HttpSessionBindingListener) value).valueBound(event);
+            } catch (Throwable t){
+                log(rb.getString(LogFacade.SESSION_BINDING_EVENT_LISTENER_EXCEPTION), t);
+            }
+        }
+
+        // Replace or add this attribute
+        Object unbound = attributes.put(name, value);
+
+        // Call the valueUnbound() method if necessary
+        if ((unbound != null) &&
+            (unbound instanceof HttpSessionBindingListener)) {
+            try {
+                context.fireContainerEvent(
+                    ContainerEvent.BEFORE_SESSION_VALUE_UNBOUND, null);
+                ((HttpSessionBindingListener) unbound).valueUnbound
+                    (new HttpSessionBindingEvent(getSession(), name));
+                context.fireContainerEvent(
+                    ContainerEvent.AFTER_SESSION_VALUE_UNBOUND, null);
+            } catch (Throwable t) {
+                context.fireContainerEvent(
+                    ContainerEvent.AFTER_SESSION_VALUE_UNBOUND, null);
+                log(rb.getString(LogFacade.SESSION_BINDING_EVENT_LISTENER_EXCEPTION), t);
+            }
+        }
+        
+        //HERCULES:add
+        // fire sync container event if name equals SYNC_STRING
+        if (SYNC_STRING.equals(name)) {
+            context.fireContainerEvent("sessionSync",
+                new HttpSessionBindingEvent(getSession(), name));
+        }
+        //end HERCULES:add
+
+        // Notify interested application event listeners
+        List<EventListener> listeners = context.getApplicationEventListeners();
+        if (listeners.isEmpty()) {
+            return;
+        }
+        Iterator<EventListener> iter = listeners.iterator();
+        while (iter.hasNext()) {
+            EventListener eventListener = iter.next();
+            if (!(eventListener instanceof HttpSessionAttributeListener)) {
+                continue;
+            }
+            HttpSessionAttributeListener listener =
+                (HttpSessionAttributeListener) eventListener;
+            try {
+                if (unbound != null) {
+                    fireContainerEvent(context,
+                                       "beforeSessionAttributeReplaced",
+                                       listener);
+                    if (event == null) {
+                        event = new HttpSessionBindingEvent
+                            (getSession(), name, unbound);
+                    }
+                    listener.attributeReplaced(event);
+                    fireContainerEvent(context,
+                                       "afterSessionAttributeReplaced",
+                                       listener);
+                } else {
+                    fireContainerEvent(context,
+                                       "beforeSessionAttributeAdded",
+                                       listener);
+                    if (event == null) {
+                        event = new HttpSessionBindingEvent(
+                                        getSession(), name, value);
+                    }
+                    listener.attributeAdded(event);
+                    fireContainerEvent(context,
+                                       "afterSessionAttributeAdded",
+                                       listener);
+                }
+            } catch (Throwable t) {
+                try {
+                    if (unbound != null) {
+                        fireContainerEvent(context,
+                                           "afterSessionAttributeReplaced",
+                                           listener);
+                    } else {
+                        fireContainerEvent(context,
+                                           "afterSessionAttributeAdded",
+                                           listener);
+                    }
+                } catch (Exception e) {
+                    // Ignore
+                }
+                log(rb.getString(LogFacade.SESSION_ATTRIBUTE_EVENT_LISTENER_EXCEPTION), t);
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------ HttpSession Protected Methods
+
+
+    /**
+     * Read a serialized version of this session object from the specified
+     * object input stream.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  The reference to the owning Manager
+     * is not restored by this method, and must be set explicitly.
+     *
+     * @param stream The input stream to read from
+     *
+     * @exception ClassNotFoundException if an unknown class is specified
+     * @exception IOException if an input/output error occurs
+     */
+    private void readObject(ObjectInputStream stream)
+        throws ClassNotFoundException, IOException {
+
+        if (listeners == null) {
+            listeners = new ArrayList<SessionListener>();
+        }
+        if (notes == null) {
+            notes = new Hashtable<String, Object>();
+        }
+
+        // Deserialize the scalar instance variables (except Manager)
+        authType = null;        // Transient only
+
+        /*
+         * The stream starts with a Long, which indicates the session's
+         * creation time. This Long may optionally be preceded by a Short,
+         * which indicates the session's serializedFormVersion.
+         */
+        Object obj = stream.readObject();
+        short readSerializedFormVersion = 0;
+        if (obj instanceof Short) {
+            readSerializedFormVersion = ((Short) obj).shortValue();
+            creationTime = ((Long) stream.readObject()).longValue();
+        } else {
+           creationTime = ((Long) obj).longValue();
+        }
+
+        readRemainingObject(stream);
+
+        /*
+         * Any additional fields that are to be included in the serialized
+         * representation of this class MUST be written to the end of the
+         * stream (in writeObject), and must be read back in HERE, i.e.,
+         * AFTER readRemainingObject (which is shared by the code that reads
+         * in sessions that were serialized using an earlier, proprietary
+         * format) has returned.
+         */
+        sipAppSessionId = (String) stream.readObject();
+
+        switch (readSerializedFormVersion) {
+        case 0:
+            // Do nothing
+            break;
+        case 1:
+            beKey = (String) stream.readObject();
+            break;
+        default:
+            throw new IOException("Unable to deserialize into "
+                    + getClass().getName()
+                    + " due to unknown serializedFormVersion of "
+                    + readSerializedFormVersion);
+        }
+    }
+
+
+    /**
+     * Reads the serialized session data from the given ObjectInputStream,
+     * with the assumption that the session's creation time, which appears
+     * first in the serialized data, has already been consumed.
+     *
+     * @param stream The ObjectInputStream from which to read the serialized
+     *        session data
+     *
+     * @exception ClassNotFoundException If the class for an object being
+     *            restored cannot be found.
+     * @exception IOException if I/O errors occur
+     */
+    private void readRemainingObject(ObjectInputStream stream)
+            throws ClassNotFoundException, IOException {
+
+        version = new AtomicLong();
+
+        lastAccessedTime = ((Long) stream.readObject()).longValue();
+        maxInactiveInterval = ((Integer) stream.readObject()).intValue();
+        isNew = ((Boolean) stream.readObject()).booleanValue();
+        isValid = ((Boolean) stream.readObject()).booleanValue();
+        thisAccessedTime = ((Long) stream.readObject()).longValue();
+        /* SJSWS 6371339
+        principal = null;        // Transient only
+        //        setId((String) stream.readObject());
+        id = (String) stream.readObject();
+        */
+        // START SJSWS 6371339
+        // Read the next object, if it is of type Principal, then
+        // store it in the principal variable
+        Object obj = stream.readObject();
+        if (obj instanceof Principal) {
+            principal = (Principal)obj;
+            id = (String) stream.readObject();
+        }
+        else {
+            principal = null;
+            id = (String) obj;
+        }
+        // END SJSWS 6371339
+        if (debug >= 2)
+            log("readObject() loading session " + id);
+
+        // START PWC 6444754
+        obj = stream.readObject();
+        int n = 0;
+        if (obj instanceof String) {
+            authType = (String) obj;
+            n = ((Integer) stream.readObject()).intValue();
+        } else {
+            n = ((Integer) obj).intValue();
+        }
+        // END PWC 6444754
+
+        // Deserialize the attribute count and attribute values
+        if (attributes == null)
+            attributes = new ConcurrentHashMap<String, Object>();
+        /* PWC 6444754
+        int n = ((Integer) stream.readObject()).intValue();
+        */
+        boolean isValidSave = isValid;
+        isValid = true;
+        for (int i = 0; i < n; i++) {
+            String name = (String) stream.readObject();
+            Object value = stream.readObject();
+            if ((value instanceof String) && (value.equals(NOT_SERIALIZED)))
+                continue;
+            if (debug >= 2)
+                log("  loading attribute '" + name +
+                    "' with value '" + value + "'");
+            attributes.put(name, value);
+        }
+        isValid = isValidSave;
+    }
+
+
+    /**
+     * Write a serialized version of this session object to the specified
+     * object output stream.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  The owning Manager will not be stored
+     * in the serialized representation of this Session.  After calling
+     * <code>readObject()</code>, you must set the associated Manager
+     * explicitly.
+     * <p>
+     * <b>IMPLEMENTATION NOTE</b>:  Any attribute that is not Serializable
+     * will be unbound from the session, with appropriate actions if it
+     * implements HttpSessionBindingListener.  If you do not want any such
+     * attributes, be sure the <code>distributable</code> property of the
+     * associated Manager is set to <code>true</code>.
+     *
+     * @param stream The output stream to write to
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        stream.writeObject(SERIALIZED_FORM_VERSION);
+        stream.writeObject(Long.valueOf(creationTime));
+        stream.writeObject(Long.valueOf(lastAccessedTime));
+        stream.writeObject(Integer.valueOf(maxInactiveInterval));
+        stream.writeObject(Boolean.valueOf(isNew));
+        stream.writeObject(Boolean.valueOf(isValid));
+        stream.writeObject(Long.valueOf(thisAccessedTime));
+        // START SJSWS 6371339
+        // If the principal is serializable, write it out
+        // START PWC 6444754
+        boolean serialPrincipal = false;
+        // END PWC 6444754
+        if (principal instanceof java.io.Serializable) {
+            // START PWC 6444754
+            serialPrincipal = true;
+            // END PWC 6444754
+            stream.writeObject(principal);
+        }
+        // END SJSWS 6371339
+        stream.writeObject(id);
+        if (debug >= 2)
+            log("writeObject() storing session " + id);
+
+        // START PWC 6444754
+        if (serialPrincipal && authType != null) {
+            stream.writeObject(authType);
+        }
+        // END PWC 6444754
+
+        // Accumulate the names of serializable and non-serializable attributes
+        String keys[] = keys();
+        ArrayList<String> saveNames = new ArrayList<String>();
+        ArrayList<Object> saveValues = new ArrayList<Object>();
+        for (int i = 0; i < keys.length; i++) {
+            Object value = attributes.get(keys[i]);
+            if (value == null) {
+                continue;            
+
+            //HERCULES:mod
+            /* original PE code next 4 lines
+            else if (value instanceof Serializable) {
+                saveNames.add(keys[i]);
+                saveValues.add(value);
+            }
+             */ 
+            //original Hercules code was next line
+            //else if (value instanceof Serializable || value instanceof javax.ejb.EJBLocalObject || value instanceof javax.naming.Context || value instanceof javax.ejb.EJBLocalHome ) { //Bug 4853798
+            //FIXME: IndirectlySerializable includes more than 3 classes in Hercules code
+            //need to explore implications of this
+
+            } else if (isSerializable(value)) {    
+                saveNames.add(keys[i]);
+                saveValues.add(value);
+            //end HERCULES:mod             
+            } else {
+                removeAttribute(keys[i], true, true);
+            }
+        }
+
+        // Serialize the attribute count and the Serializable attributes
+        int n = saveNames.size();
+        stream.writeObject(Integer.valueOf(n));
+        for (int i = 0; i < n; i++) {
+            stream.writeObject(saveNames.get(i));
+            //HERCULES:mod
+            /* orignal PE code            
+            try {
+                stream.writeObject(saveValues.get(i));
+                if (debug >= 2)
+                    log("  storing attribute '" + saveNames.get(i) +
+                        "' with value '" + saveValues.get(i) + "'");
+            } catch (NotSerializableException e) {
+                log(sm.getString("standardSession.notSerializable",
+                                 saveNames.get(i), id), e);
+                //standardSession.notSerializable=PWC2785: Cannot serialize session attribute {0} for session {1}
+                stream.writeObject(NOT_SERIALIZED);
+                if (debug >= 2)
+                    log("  storing attribute '" + saveNames.get(i) +
+                        "' with value NOT_SERIALIZED");
+            }
+             *end original PE code
+             */ 
+            
+            //following is replacement code from Hercules
+            try {
+                stream.writeObject(saveValues.get(i));
+                if (debug >= 2)
+                    log("  storing attribute '" + saveNames.get(i) +
+                        "' with value '" + saveValues.get(i) + "'");
+            } catch (NotSerializableException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_SERIALIZE_SESSION_EXCEPTION),
+                                                  new Object[] {saveNames.get(i), id});
+                log(msg, e);
+                stream.writeObject(NOT_SERIALIZED);
+                if (debug >= 2)
+                    log("  storing attribute '" + saveNames.get(i) +
+                        "' with value NOT_SERIALIZED");
+            } catch (IOException ioe) {
+		if ( ioe.getCause() instanceof NotSerializableException ) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_SERIALIZE_SESSION_EXCEPTION),
+                                                  new Object[] {saveNames.get(i), id});
+                	log(msg, ioe);
+                	stream.writeObject(NOT_SERIALIZED);
+                	if (debug >= 2)
+                    		log("  storing attribute '" + saveNames.get(i) +
+                        	"' with value NOT_SERIALIZED");
+		} else 
+			throw ioe;
+	    }
+            //end HERCULES:mod
+        }
+
+        stream.writeObject(sipAppSessionId);
+        stream.writeObject(beKey);
+
+    }
+
+
+
+    /**
+     * Exclude attribute that cannot be serialized.
+     * @param name the attribute's name
+     */
+    protected boolean exclude(String name){
+
+        for (int i = 0; i < excludedAttributes.length; i++) {
+            if (name.equalsIgnoreCase(excludedAttributes[i]))
+                return true;
+        }
+
+        return false;
+    }
+
+
+    protected void evaluateIfValid() {
+        /*
+	 * If this session has expired or is in the process of expiring or
+	 * will never expire, return
+	 */
+        if (!this.isValid || expiring || maxInactiveInterval < 0)
+            return;
+
+        isValid();
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Fire container events if the Context implementation is the
+     * <code>org.apache.catalina.core.StandardContext</code>.
+     *
+     * @param context Context for which to fire events
+     * @param type Event type
+     * @param data Event data
+     *
+     * @exception Exception occurred during event firing
+     */
+    protected void fireContainerEvent(Context context, String type, Object data)
+            throws Exception {
+
+        if (!(context instanceof StandardContext)) {
+            return; // Container events are not supported
+        }
+        // NOTE:  Race condition is harmless, so do not synchronize
+        if (containerEventMethod == null) {
+            containerEventMethod =
+                context.getClass().getMethod("fireContainerEvent",
+                                             containerEventTypes);
+        }
+        Object containerEventParams[] = new Object[2];
+        containerEventParams[0] = type;
+        containerEventParams[1] = data;
+        containerEventMethod.invoke(context, containerEventParams);
+
+    }
+                                      
+
+
+    /**
+     * Notify all session event listeners that a particular event has
+     * occurred for this Session.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param data Event data
+     */
+    public void fireSessionEvent(String type, Object data) {
+
+        // Fire monitoring probe
+        if (Session.SESSION_CREATED_EVENT.equals(type)) {
+            context.sessionCreatedEvent(this);
+        }  else {
+            context.sessionDestroyedEvent(this);
+        }
+
+        if (listeners.size() < 1)
+            return;
+        SessionEvent event = new SessionEvent(this, type, data);
+        SessionListener list[] = new SessionListener[0];
+        synchronized (listeners) {
+            list = listeners.toArray(list);
+        }
+
+        for (int i = 0; i < list.length; i++){
+            (list[i]).sessionEvent(event);
+        }
+
+    }
+
+
+    /**
+     * Return the names of all currently defined session attributes
+     * as an array of Strings.  If there are no defined attributes, a
+     * zero-length array is returned.
+     */
+    protected String[] keys() {
+        if (attributes.size() > 0) {
+            // take a snapshot of attributes.keySet()
+            List<String> list = new ArrayList<String>();
+            for (String key : attributes.keySet()) {
+                list.add(key);
+            }
+            return list.toArray(new String[list.size()]);
+        } else {
+            return EMPTY_ARRAY;
+        }
+    }
+
+
+    /**
+     * Return the value of an attribute without a check for validity.
+     */
+    protected Object getAttributeInternal(String name) {
+        return (attributes.get(name));
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Manager (if any).
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+        message = neutralizeForLog(message);
+        if ((manager != null) && (manager instanceof ManagerBase)) {
+            ((ManagerBase) manager).log(message);
+        } else {
+            log.log(Level.INFO, "StandardSession: " + message);
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Manager (if any).
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    protected void log(String message, Throwable t) {
+        message = neutralizeForLog(message);
+        if ((manager != null) && (manager instanceof ManagerBase)) {
+            ((ManagerBase) manager).log(message, t);
+        } else {
+            log.log(Level.WARNING, "StandardSession: " + message, t);
+        }
+    }
+
+
+    /**
+     * Returns true if the given value may be serialized, false otherwise.
+     *
+     * A given value is considered serializable if it is an instance of
+     * java.io.Serializable or
+     * com.sun.enterprise.spi.io.BaseIndirectlySerializable, or if special
+     * serialization logic for it exists. For example, in the case of
+     * GlassFish, instances of javax.naming.Context are replaced with
+     * corresponding instances of SerializableJNDIContext during serialization
+     * (this is done by the specialized object outputstream returned by
+     * the JavaEEObjectStreamFactory mechanism).
+     * 
+     * @return true if the given value may be serialized, false otherwise
+     */
+    static boolean isSerializable(Object value) {
+        if ((value instanceof Serializable)
+                || (value instanceof BaseIndirectlySerializable)
+                || (value instanceof javax.naming.Context)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+}
+
+
+// ------------------------------------------------------------ Protected Class
+
+
+/**
+ * This class is a dummy implementation of the <code>HttpSessionContext</code>
+ * interface, to conform to the requirement that such an object be returned
+ * when <code>HttpSession.getSessionContext()</code> is called.
+ *
+ * @author Craig R. McClanahan
+ *
+ * @deprecated As of Java Servlet API 2.1 with no replacement.  The
+ *  interface will be removed in a future version of this API.
+ */
+
+final class StandardSessionContext implements HttpSessionContext {
+
+
+    private HashMap<?, String> dummy = new HashMap<String, String>();
+
+    /**
+     * Return the session identifiers of all sessions defined
+     * within this context.
+     *
+     * @deprecated As of Java Servlet API 2.1 with no replacement.
+     *  This method must return an empty <code>Enumeration</code>
+     *  and will be removed in a future version of the API.
+     */
+    public Enumeration<String> getIds() {
+        return (new Enumerator<String>(dummy));
+    }
+
+
+    /**
+     * Return the <code>HttpSession</code> associated with the
+     * specified session identifier.
+     *
+     * @param id Session identifier for which to look up a session
+     *
+     * @deprecated As of Java Servlet API 2.1 with no replacement.
+     *  This method must return null and will be removed in a
+     *  future version of the API.
+     */
+    public HttpSession getSession(String id) {
+        return (null);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardSessionFacade.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardSessionFacade.java
new file mode 100644
index 0000000..52c210f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/StandardSessionFacade.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+import java.util.Enumeration;
+
+
+/**
+ * Facade for the StandardSession object.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:00 $
+ */
+
+public class StandardSessionFacade
+    implements HttpSession {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new session facade.
+     */
+    public StandardSessionFacade(StandardSession session) {
+        super();
+        this.session = session;
+    }
+
+
+    /**
+     * Construct a new session facade.
+     */
+    public StandardSessionFacade(HttpSession session) {
+        super();
+        this.session = session;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped session object.
+     */
+    private HttpSession session = null;
+
+
+    // ---------------------------------------------------- HttpSession Methods
+
+
+    public long getCreationTime() {
+        return session.getCreationTime();
+    }
+
+
+    public String getId() {
+        return session.getId();
+    }
+
+
+    public long getLastAccessedTime() {
+        return session.getLastAccessedTime();
+    }
+
+
+    public ServletContext getServletContext() {
+        // FIXME : Facade this object ?
+        return session.getServletContext();
+    }
+
+
+    public void setMaxInactiveInterval(int interval) {
+        session.setMaxInactiveInterval(interval);
+    }
+
+
+    public int getMaxInactiveInterval() {
+        return session.getMaxInactiveInterval();
+    }
+
+
+    /**
+     * @deprecated
+     */
+    public HttpSessionContext getSessionContext() {
+        return session.getSessionContext();
+    }
+
+
+    public Object getAttribute(String name) {
+        return session.getAttribute(name);
+    }
+
+
+    /**
+     * @deprecated
+     */
+    public Object getValue(String name) {
+        return session.getAttribute(name);
+    }
+
+
+    /**
+     * @deprecated
+     */
+    public Enumeration<String> getAttributeNames() {
+        return session.getAttributeNames();
+    }
+
+
+    public String[] getValueNames() {
+        return session.getValueNames();
+    }
+
+
+    public void setAttribute(String name, Object value) {
+        session.setAttribute(name, value);
+    }
+
+
+    /**
+     * @deprecated
+     */
+    public void putValue(String name, Object value) {
+        session.setAttribute(name, value);
+    }
+
+
+    public void removeAttribute(String name) {
+        session.removeAttribute(name);
+    }
+
+
+    /**
+     * @deprecated
+     */
+    public void removeValue(String name) {
+        session.removeAttribute(name);
+    }
+
+
+    public void invalidate() {
+        session.invalidate();
+    }
+
+
+    public boolean isNew() {
+        return session.isNew();
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/StoreBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/session/StoreBase.java
new file mode 100644
index 0000000..a840d27
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/StoreBase.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.session;
+
+import org.apache.catalina.*;
+import org.apache.catalina.util.LifecycleSupport;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+//HERCULES:end added
+
+/**
+ * Abstract implementation of the Store interface to
+ * support most of the functionality required by a Store.
+ *
+ * @author Bip Thelin
+ * @version $Revision: 1.8 $, $Date: 2007/05/05 05:32:19 $
+ */
+
+public abstract class StoreBase
+    implements Lifecycle, Store {
+
+    private static final java.util.logging.Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    /* SJSAS
+    protected static String info = "StoreBase/1.0";
+    */
+    // START SJSAS
+    // If this variable were static, it would need to be protected from 
+    // manipulation by malicious code by making it final. However, this
+    // variable must not be final, because it is assigned a different value 
+    // in some of the EE subclasses. Therefore, turning it into an instance
+    // variable.
+    protected String info = "StoreBase/1.0";
+    // END SJSAS
+
+    /**
+     * Name to register for this Store, used for logging.
+     */
+    /* SJSAS 
+    protected static String storeName = "StoreBase";
+    */
+    // START SJSAS
+    // If this variable were static, it would need to be protected from 
+    // manipulation by malicious code by making it final. However, this
+    // variable must not be final, because it is assigned a different value 
+    // in some of the EE subclasses. Therefore, turning it into an instance
+    // variable.
+    protected String storeName = "StoreBase";
+    // END SJSAS
+
+    /**
+     * The debugging detail level for this component.
+     */
+    protected int debug = 0;
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+    /**
+     * The property change support for this component.
+     */
+    protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+    /**
+     * The Manager with which this JDBCStore is associated.
+     */
+    protected Manager manager;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the info for this Store.
+     */
+    public String getInfo() {
+        return(info);
+    }
+
+
+    /**
+     * Return the name for this Store, used for logging.
+     */
+    public String getStoreName() {
+        return(storeName);
+    }
+
+
+    /**
+     * Set the debugging detail level for this Store.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Return the debugging detail level for this Store.
+     */
+    public int getDebug() {
+        return(this.debug);
+    }
+
+
+    /**
+     * Set the Manager with which this Store is associated.
+     *
+     * @param manager The newly associated Manager
+     */
+    public void setManager(Manager manager) {
+        Manager oldManager = this.manager;
+        this.manager = manager;
+        support.firePropertyChange("manager", oldManager, this.manager);
+    }
+
+    /**
+     * Return the Manager with which the Store is associated.
+     */
+    public Manager getManager() {
+        return(this.manager);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this Store.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+    /**
+     * Add a property change listener to this component.
+     *
+     * @param listener a value of type 'PropertyChangeListener'
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        support.addPropertyChangeListener(listener);
+    }
+
+    /**
+     * Remove a property change listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        support.removePropertyChangeListener(listener);
+    }
+        
+    /**
+    * Serialize a session into an output stream.  
+    *
+    * @param sess
+    *   The session to be serialized
+    *
+    * @param oos
+    *   The output stream the session should be written to
+    * Hercules: added method
+    */
+    public void writeSession(Session sess, ObjectOutputStream oos)
+        throws IOException {
+      if ( sess == null )  {
+        return;
+      }
+      
+      oos.writeObject(sess);
+    } 
+    
+    /**
+    * Create a session object from an input stream.
+    *
+    * @param manager
+    *   The manager that will own this session
+    *
+    * @param ois
+    *   The input stream containing the serialized session
+    *
+    * @return 
+    *   The resulting session object
+    * Hercules: added method
+    */
+    public Session readSession(Manager manager, ObjectInputStream ois)
+            throws ClassNotFoundException, IOException {
+
+        StandardSession sess = StandardSession.deserialize(ois, manager);
+        sess.setManager(manager);
+      
+        return sess;
+    }
+    
+    /**
+    * public wrapper for processExpires()
+    * don't want to make processExpires() public
+    * called from manager background thread
+    *
+    * Hercules: added method
+    */    
+    public void doProcessExpires() {
+        this.processExpires();
+    }
+    
+    /**
+    * no-op method - sub classes will
+    * implement to remove a session from the store
+    * cache
+    * Hercules: added method
+    */    
+    public void removeFromStoreCache(String id) {
+        //do nothing
+    }     
+    
+
+    // ----------------------------------------------------- Protected Methods
+
+    /**
+     * Called by our background reaper thread to check if Sessions
+     * saved in our store are subject of being expired. If so expire
+     * the Session and remove it from the Store.
+     */
+    public void processExpires() {
+        long timeNow = System.currentTimeMillis(); 
+        String[] keys = null;
+
+        if(!started) {
+            return;
+        }
+
+        try {
+            keys = keys();
+        } catch (IOException e) {
+            log("Error during processExpires", e);
+            return;
+        }
+
+        for (int i = 0; i < keys.length; i++) {
+            try {
+                StandardSession session = (StandardSession) load(keys[i]);
+                if (session == null) {
+                    continue;
+                }
+                int timeIdle = (int) ((timeNow - session.thisAccessedTime) / 1000L);
+                if (timeIdle < session.getMaxInactiveInterval()) {
+                    continue;
+                } 
+                if ( ( (PersistentManagerBase) manager).isLoaded( keys[i] )) {
+                    // recycle old backup session
+                    session.recycle();
+                } else {
+                    // expire swapped out session
+                    session.expire();
+                }
+                remove(keys[i]);
+            } catch (IOException e) {
+                log("Error during processExpires", e);
+            } catch (ClassNotFoundException e) {
+                log("Error during processExpires", e);
+            }
+        }
+    }
+
+    /**
+     * 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 = null;
+        String containerName = null;
+        Container container = manager.getContainer();
+        if (container != null) {
+            logger = container.getLogger();
+            containerName = container.getName();
+        }
+        if (logger != null) {
+            logger.log(neutralizeForLog(getStoreName()+"[" + containerName + "]: " +
+                       message));
+        } else {
+            log.log(Level.FINE, neutralizeForLog(getStoreName() + "[" + containerName + "]: " + message));
+        }
+    }
+
+    /**
+     * Logs the given message to the Logger associated with the Container
+     * (if any) of this StoreBase.
+     *
+     * @param message the message
+     * @param t the Throwable
+     */    
+    private void log(String message, Throwable t) {
+        Logger logger = null;
+        String containerName = null;
+        Container container = manager.getContainer();
+        if (container != null) {
+            logger = container.getLogger();
+            containerName = container.getName();
+        }
+        if (logger != null) {
+            logger.log(neutralizeForLog(getStoreName()+"[" + containerName + "]: " +
+                message), t, Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, neutralizeForLog(getStoreName()+"[" + containerName + "]: " + message), t);
+        }
+    }
+
+    /**
+     * Load and return the Session associated with the specified session
+     * identifier from this Store, without removing it.  If there is no
+     * such stored Session, return <code>null</code>.
+     *
+     * @param id Session identifier of the session to load
+     * @param version The requested session version
+     *
+     * @exception ClassNotFoundException if a deserialization error occurs
+     * @exception IOException if an input/output error occurs
+     */
+    public Session load(String id, String version)
+            throws ClassNotFoundException, IOException {
+        return load(id);
+    }    
+
+    // --------------------------------------------------------- Thread Methods
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.STORE_BASE_STARTED_EXCEPTION));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.STORE_BASE_NOT_STARTED_EXCEPTION));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/session/package.html b/appserver/web/web-core/src/main/java/org/apache/catalina/session/package.html
new file mode 100644
index 0000000..97c03ad
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/session/package.html
@@ -0,0 +1,71 @@
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<body>
+
+<p>This package contains the standard <code>Manager</code> and
+<code>Session</code> implementations that represent the collection of
+active sessions and the individual sessions themselves, respectively,
+that are associated with a <code>Context</code>.  Additional implementations
+of the <code>Manager</code> interface can be based upon the supplied
+convenience base class (<code>ManagerBase</code>), if desired.  Different
+implementations of <code>Session</code> are possible, but a need for
+functionality beyond what is provided by the standard implementation
+(<code>StandardSession</code>) is not expected.</p>
+
+<p>The convenience <code>ManagerBase</code> base class is configured by
+setting the following properties:</p>
+<ul>
+<li><b>algorithm</b> - Message digest algorithm to be used when
+    generating session identifiers.  This must be the name of an
+    algorithm supported by the <code>java.security.MessageDigest</code>
+    class on your platform.  [DEFAULT_ALGORITHM]</li>
+<li><b>debug</b> - Debugging detail level for this component. [0]</li>
+<li><b>distributable</b> - Has the web application we are associated with
+    been marked as "distributable"?  If it has, attempts to add or replace
+    a session attribute object that does not implement the
+    <code>java.io.Serializable</code> interface will be rejected.
+    [false]</li>
+<li><b>entropy</b> - A string initialization parameter that is used to
+    increase the entropy of the seeding of the random number generator
+    used in creation of session identifiers.  [NONE]</li>
+<li><b>maxInactiveInterval</b> - The default maximum inactive interval,
+    in minutes, for sessions created by this Manager.  The standard
+    implementation automatically updates this value based on the configuration
+    settings in the web application deployment descriptor.  [60]</li>
+<li><b>randomClass</b> - The Java class name of the random number generator
+    to be used when creating session identifiers for this Manager.
+    [java.security.SecureRandom]</li>
+</ul>
+
+<p>The standard implementation of the <code>Manager</code> interface
+(<code>StandardManager</code>) supports the following additional configuration
+properties:</p>
+<ul>
+<li><b>checkInterval</b> - The interval, in seconds, between checks for
+    sessions that have expired and should be invalidated.  [60]</li>
+<li><b>maxActiveSessions</b> - The maximum number of active sessions that
+    will be allowed, or -1 for no limit.  [-1]</li>
+<li><b>pathname</b> - Pathname to the file that is used to store session
+    data persistently across container restarts.  If this pathname is relative,
+    it is resolved against the temporary working directory provided by our
+    associated Context, if any.  ["sessions.ser"]</li>
+</ul>
+
+</body>
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java
new file mode 100644
index 0000000..8835ca3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import java.io.ByteArrayOutputStream;
+
+
+/**
+ * Class that extends ServletOuputStream, used as a wrapper from within
+ * <code>SsiInclude</code>
+ *
+ * @author Bip Thelin
+ * @version $Revision: 1.3 $, $Date: 2007/02/13 19:16:20 $
+ * @see ServletOutputStream and ByteArrayOutputStream
+ */
+public class ByteArrayServletOutputStream extends ServletOutputStream {
+    /**
+     * Our buffer to hold the stream.
+     */
+    protected ByteArrayOutputStream buf = null;
+
+    /**
+     * Construct a new ServletOutputStream.
+     */
+    public ByteArrayServletOutputStream() {
+        buf = new ByteArrayOutputStream();
+    }
+
+
+    /**
+     * @return the byte array.
+     */
+    public byte[] toByteArray() {
+        return buf.toByteArray();
+    }
+
+
+    /**
+     * Write to our buffer.
+     *
+     * @param b The parameter to write
+     */
+    @Override
+    public void write(int b) {
+        buf.write(b);
+    }
+
+    @Override
+    public boolean isReady() {
+        return true;
+    }
+
+    @Override
+    public void setWriteListener(WriteListener writeListener) {
+        throw new IllegalStateException();
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ExpressionParseTree.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ExpressionParseTree.java
new file mode 100644
index 0000000..2d149a5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ExpressionParseTree.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import java.text.ParseException;
+import java.util.LinkedList;
+import java.util.List;
+/**
+ * Represents a parsed expression.
+ * 
+ * @version $Revision: 1.2 $
+ * @author Paul Speed
+ */
+public class ExpressionParseTree {
+    /**
+     * Contains the current set of completed nodes. This is a workspace for the
+     * parser.
+     */
+    private LinkedList<Node> nodeStack = new LinkedList<Node>();
+    /**
+     * Contains operator nodes that don't yet have values. This is a workspace
+     * for the parser.
+     */
+    private LinkedList<OppNode> oppStack = new LinkedList<OppNode>();
+    /**
+     * The root node after the expression has been parsed.
+     */
+    private Node root;
+    /**
+     * The SSIMediator to use when evaluating the expressions.
+     */
+    private SSIMediator ssiMediator;
+
+
+    /**
+     * Creates a new parse tree for the specified expression.
+     */
+    public ExpressionParseTree(String expr, SSIMediator ssiMediator)
+            throws ParseException {
+        this.ssiMediator = ssiMediator;
+        parseExpression(expr);
+    }
+
+
+    /**
+     * Evaluates the tree and returns true or false. The specified SSIMediator
+     * is used to resolve variable references.
+     */
+    public boolean evaluateTree() {
+        return root.evaluate();
+    }
+
+
+    /**
+     * Pushes a new operator onto the opp stack, resolving existing opps as
+     * needed.
+     */
+    private void pushOpp(OppNode node) {
+        // If node is null then it's just a group marker
+        if (node == null) {
+            oppStack.add(0, null);
+            return;
+        }
+        while (true) {
+            if (oppStack.size() == 0) break;
+            OppNode top = oppStack.get(0);
+            // If the top is a spacer then don't pop
+            // anything
+            if (top == null) break;
+            // If the top node has a lower precedence then
+            // let it stay
+            if (top.getPrecedence() < node.getPrecedence()) break;
+            // Remove the top node
+            oppStack.remove(0);
+            // Let it fill its branches
+            top.popValues(nodeStack);
+            // Stick it on the resolved node stack
+            nodeStack.add(0, top);
+        }
+        // Add the new node to the opp stack
+        oppStack.add(0, node);
+    }
+
+
+    /**
+     * Resolves all pending opp nodes on the stack until the next group marker
+     * is reached.
+     */
+    private void resolveGroup() {
+        OppNode top = null;
+        while ((top = oppStack.remove(0)) != null) {
+            // Let it fill its branches
+            top.popValues(nodeStack);
+            // Stick it on the resolved node stack
+            nodeStack.add(0, top);
+        }
+    }
+
+
+    /**
+     * Parses the specified expression into a tree of parse nodes.
+     */
+    private void parseExpression(String expr) throws ParseException {
+        StringNode currStringNode = null;
+        // We cheat a little and start an artificial
+        // group right away. It makes finishing easier.
+        pushOpp(null);
+        ExpressionTokenizer et = new ExpressionTokenizer(expr);
+        while (et.hasMoreTokens()) {
+            int token = et.nextToken();
+            if (token != ExpressionTokenizer.TOKEN_STRING)
+                currStringNode = null;
+            switch (token) {
+                case ExpressionTokenizer.TOKEN_STRING :
+                    if (currStringNode == null) {
+                        currStringNode = new StringNode(et.getTokenValue());
+                        nodeStack.add(0, currStringNode);
+                    } else {
+                        // Add to the existing
+                        currStringNode.value.append(" ");
+                        currStringNode.value.append(et.getTokenValue());
+                    }
+                    break;
+                case ExpressionTokenizer.TOKEN_AND :
+                    pushOpp(new AndNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_OR :
+                    pushOpp(new OrNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_NOT :
+                    pushOpp(new NotNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_EQ :
+                    pushOpp(new EqualNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_NOT_EQ :
+                    pushOpp(new NotNode());
+                    // Sneak the regular node in. The NOT will
+                    // be resolved when the next opp comes along.
+                    oppStack.add(0, new EqualNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_RBRACE :
+                    // Closeout the current group
+                    resolveGroup();
+                    break;
+                case ExpressionTokenizer.TOKEN_LBRACE :
+                    // Push a group marker
+                    pushOpp(null);
+                    break;
+                case ExpressionTokenizer.TOKEN_GE :
+                    pushOpp(new NotNode());
+                    // Similar strategy to NOT_EQ above, except this
+                    // is NOT less than
+                    oppStack.add(0, new LessThanNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_LE :
+                    pushOpp(new NotNode());
+                    // Similar strategy to NOT_EQ above, except this
+                    // is NOT greater than
+                    oppStack.add(0, new GreaterThanNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_GT :
+                    pushOpp(new GreaterThanNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_LT :
+                    pushOpp(new LessThanNode());
+                    break;
+                case ExpressionTokenizer.TOKEN_END :
+                    break;
+            }
+        }
+        // Finish off the rest of the opps
+        resolveGroup();
+        if (nodeStack.size() == 0) {
+            throw new ParseException("No nodes created.", et.getIndex());
+        }
+        if (nodeStack.size() > 1) {
+            throw new ParseException("Extra nodes created.", et.getIndex());
+        }
+        if (oppStack.size() != 0) {
+            throw new ParseException("Unused opp nodes exist.", et.getIndex());
+        }
+        root = nodeStack.get(0);
+    }
+
+    /**
+     * A node in the expression parse tree.
+     */
+    private abstract class Node {
+        /**
+         * Return true if the node evaluates to true.
+         */
+        public abstract boolean evaluate();
+    }
+    /**
+     * A node the represents a String value
+     */
+    private class StringNode extends Node {
+        StringBuilder value;
+        String resolved = null;
+
+
+        public StringNode(String value) {
+            this.value = new StringBuilder(value);
+        }
+
+
+        /**
+         * Resolves any variable references and returns the value string.
+         */
+        public String getValue() {
+            if (resolved == null)
+                resolved = ssiMediator.substituteVariables(value.toString());
+            return resolved;
+        }
+
+
+        /**
+         * Returns true if the string is not empty.
+         */
+        public boolean evaluate() {
+            return !(getValue().length() == 0);
+        }
+
+
+        public String toString() {
+            return value.toString();
+        }
+    }
+
+    private static final int PRECEDENCE_NOT = 5;
+    private static final int PRECEDENCE_COMPARE = 4;
+    private static final int PRECEDENCE_LOGICAL = 1;
+
+    /**
+     * A node implementation that represents an operation.
+     */
+    private abstract class OppNode extends Node {
+        /**
+         * The left branch.
+         */
+        Node left;
+        /**
+         * The right branch.
+         */
+        Node right;
+
+
+        /**
+         * Returns a preference level suitable for comparison to other OppNode
+         * preference levels.
+         */
+        public abstract int getPrecedence();
+
+
+        /**
+         * Lets the node pop its own branch nodes off the front of the
+         * specified list. The default pulls two.
+         */
+        public void popValues(List<Node> values) {
+            right = values.remove(0);
+            left = values.remove(0);
+        }
+    }
+    private final class NotNode extends OppNode {
+        public boolean evaluate() {
+            return !left.evaluate();
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_NOT;
+        }
+
+
+        /**
+         * Overridden to pop only one value.
+         */
+        public void popValues(List<Node> values) {
+            left = values.remove(0);
+        }
+
+
+        public String toString() {
+            return left + " NOT";
+        }
+    }
+    private final class AndNode extends OppNode {
+        public boolean evaluate() {
+            if (!left.evaluate()) // Short circuit
+                return false;
+            return right.evaluate();
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_LOGICAL;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " AND";
+        }
+    }
+    private final class OrNode extends OppNode {
+        public boolean evaluate() {
+            if (left.evaluate()) // Short circuit
+                return true;
+            return right.evaluate();
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_LOGICAL;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " OR";
+        }
+    }
+    private abstract class CompareNode extends OppNode {
+        protected int compareBranches() {
+            String val1 = ((StringNode)left).getValue();
+            String val2 = ((StringNode)right).getValue();
+            return val1.compareTo(val2);
+        }
+    }
+    private final class EqualNode extends CompareNode {
+        public boolean evaluate() {
+            return (compareBranches() == 0);
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_COMPARE;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " EQ";
+        }
+    }
+    private final class GreaterThanNode extends CompareNode {
+        public boolean evaluate() {
+            return (compareBranches() > 0);
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_COMPARE;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " GT";
+        }
+    }
+    private final class LessThanNode extends CompareNode {
+        public boolean evaluate() {
+            return (compareBranches() < 0);
+        }
+
+
+        public int getPrecedence() {
+            return PRECEDENCE_COMPARE;
+        }
+
+
+        public String toString() {
+            return left + " " + right + " LT";
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ExpressionTokenizer.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ExpressionTokenizer.java
new file mode 100644
index 0000000..5a399a4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ExpressionTokenizer.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+/**
+ * Parses an expression string to return the individual tokens. This is
+ * patterned similar to the StreamTokenizer in the JDK but customized for SSI
+ * conditional expression parsing.
+ * 
+ * @version $Revision: 1.2 $
+ * @author Paul Speed
+ */
+public class ExpressionTokenizer {
+    public static final int TOKEN_STRING = 0;
+    public static final int TOKEN_AND = 1;
+    public static final int TOKEN_OR = 2;
+    public static final int TOKEN_NOT = 3;
+    public static final int TOKEN_EQ = 4;
+    public static final int TOKEN_NOT_EQ = 5;
+    public static final int TOKEN_RBRACE = 6;
+    public static final int TOKEN_LBRACE = 7;
+    public static final int TOKEN_GE = 8;
+    public static final int TOKEN_LE = 9;
+    public static final int TOKEN_GT = 10;
+    public static final int TOKEN_LT = 11;
+    public static final int TOKEN_END = 12;
+    private char[] expr;
+    private String tokenVal = null;
+    private int index;
+    private int length;
+
+
+    /**
+     * Creates a new parser for the specified expression.
+     */
+    public ExpressionTokenizer(String expr) {
+        this.expr = expr.trim().toCharArray();
+        this.length = this.expr.length;
+    }
+
+
+    /**
+     * Returns true if there are more tokens.
+     */
+    public boolean hasMoreTokens() {
+        return index < length;
+    }
+
+
+    /**
+     * Returns the current index for error reporting purposes.
+     */
+    public int getIndex() {
+        return index;
+    }
+
+
+    protected boolean isMetaChar(char c) {
+        return Character.isWhitespace(c) || c == '(' || c == ')' || c == '!'
+                || c == '<' || c == '>' || c == '|' || c == '&' || c == '=';
+    }
+
+
+    /**
+     * Returns the next token type and initializes any state variables
+     * accordingly.
+     */
+    public int nextToken() {
+        // Skip any leading white space
+        while (index < length && Character.isWhitespace(expr[index]))
+            index++;
+        // Clear the current token val
+        tokenVal = null;
+        if (index == length) return TOKEN_END; // End of string
+        int start = index;
+        char currentChar = expr[index];
+        char nextChar = (char)0;
+        index++;
+        if (index < length) nextChar = expr[index];
+        // Check for a known token start
+        switch (currentChar) {
+            case '(' :
+                return TOKEN_LBRACE;
+            case ')' :
+                return TOKEN_RBRACE;
+            case '=' :
+                return TOKEN_EQ;
+            case '!' :
+                if (nextChar == '=') {
+                    index++;
+                    return TOKEN_NOT_EQ;
+                } else {
+                    return TOKEN_NOT;
+                }
+            case '|' :
+                if (nextChar == '|') {
+                    index++;
+                    return TOKEN_OR;
+                }
+                break;
+            case '&' :
+                if (nextChar == '&') {
+                    index++;
+                    return TOKEN_AND;
+                }
+                break;
+            case '>' :
+                if (nextChar == '=') {
+                    index++;
+                    return TOKEN_GE; // Greater than or equal
+                } else {
+                    return TOKEN_GT; // Greater than
+                }
+            case '<' :
+                if (nextChar == '=') {
+                    index++;
+                    return TOKEN_LE; // Less than or equal
+                } else {
+                    return TOKEN_LT; // Less than
+                }
+            default :
+                // Otherwise it's a string
+                break;
+        }
+        int end = index;
+        // If it's a quoted string then end is the next unescaped quote
+        if (currentChar == '"' || currentChar == '\'') {
+            char endChar = currentChar;
+            boolean escaped = false;
+            start++;
+            for (; index < length; index++) {
+                if (expr[index] == '\\' && !escaped) {
+                    escaped = true;
+                    continue;
+                }
+                if (expr[index] == endChar && !escaped) break;
+                escaped = false;
+            }
+            end = index;
+            index++; // Skip the end quote
+        } else {
+            // End is the next whitespace character
+            for (; index < length; index++) {
+                if (isMetaChar(expr[index])) break;
+            }
+            end = index;
+        }
+        // Extract the string from the array
+        this.tokenVal = new String(expr, start, end - start);
+        return TOKEN_STRING;
+    }
+
+
+    /**
+     * Returns the String value of the token if it was type TOKEN_STRING.
+     * Otherwise null is returned.
+     */
+    public String getTokenValue() {
+        return tokenVal;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java
new file mode 100644
index 0000000..30d216f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * A HttpServletResponseWrapper, used from
+ * <code>SSIServletExternalResolver</code>
+ * 
+ * @author Bip Thelin
+ * @author David Becker
+ * @version $Revision: 1.3 $, $Date: 2007/02/13 19:16:20 $
+ */
+public class ResponseIncludeWrapper extends HttpServletResponseWrapper {
+    /**
+     * The names of some headers we want to capture.
+     */
+    private static final String CONTENT_TYPE = "content-type";
+    private static final String LAST_MODIFIED = "last-modified";
+    private static final String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";
+
+    private static final ThreadLocal<SimpleDateFormat> RFC1123_FORMAT =
+        new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                // rfc 1123 pattern format
+                SimpleDateFormat f = new SimpleDateFormat(RFC1123_PATTERN, Locale.US);
+                f.setTimeZone(TimeZone.getTimeZone("GMT"));
+                return f;
+            }
+        };
+
+    private SimpleDateFormat rfc1123Format;
+    protected long lastModified = -1;
+    private String contentType = null;
+
+    /**
+     * Our ServletOutputStream
+     */
+    protected ServletOutputStream captureServletOutputStream;
+    protected ServletOutputStream servletOutputStream;
+    protected PrintWriter printWriter;
+    
+    private ServletContext context;
+    private HttpServletRequest request;
+
+
+    /**
+     * Initialize our wrapper with the current HttpServletResponse and
+     * ServletOutputStream.
+     * 
+     * @param context The servlet context
+     * @param request The HttpServletResponse to use
+     * @param response The response to use
+     * @param captureServletOutputStream The ServletOutputStream to use
+     */
+    public ResponseIncludeWrapper(ServletContext context, 
+    		HttpServletRequest request, HttpServletResponse response,
+           ServletOutputStream captureServletOutputStream) {
+        super(response);
+        this.context = context;
+        this.request = request;
+        this.captureServletOutputStream = captureServletOutputStream;
+        this.rfc1123Format = RFC1123_FORMAT.get();
+    }
+
+
+    /**
+     * Flush the servletOutputStream or printWriter ( only one will be non-null )
+     * This must be called after a requestDispatcher.include, since we can't
+     * assume that the included servlet flushed its stream.
+     */
+    public void flushOutputStreamOrWriter() throws IOException {
+        if (servletOutputStream != null) {
+            servletOutputStream.flush();
+        }
+        if (printWriter != null) {
+            printWriter.flush();
+        }
+    }
+
+
+    /**
+     * Return a printwriter, throws and exception if a OutputStream already
+     * been returned.
+     * 
+     * @return a PrintWriter object
+     * @exception java.io.IOException
+     *                if the outputstream already been called
+     */
+    public PrintWriter getWriter() throws java.io.IOException {
+        if (servletOutputStream == null) {
+            if (printWriter == null) {
+                setCharacterEncoding(getCharacterEncoding());
+                printWriter = new PrintWriter(
+                        new OutputStreamWriter(captureServletOutputStream,
+                                               getCharacterEncoding()));
+            }
+            return printWriter;
+        }
+        throw new IllegalStateException();
+    }
+
+
+    /**
+     * Return a OutputStream, throws and exception if a printwriter already
+     * been returned.
+     * 
+     * @return a OutputStream object
+     * @exception java.io.IOException
+     *                if the printwriter already been called
+     */
+    public ServletOutputStream getOutputStream() throws java.io.IOException {
+        if (printWriter == null) {
+            if (servletOutputStream == null) {
+                servletOutputStream = captureServletOutputStream;
+            }
+            return servletOutputStream;
+        }
+        throw new IllegalStateException();
+    }
+    
+    
+    /**
+     * Returns the value of the <code>last-modified</code> header field. The
+     * result is the number of milliseconds since January 1, 1970 GMT.
+     *
+     * @return the date the resource referenced by this
+     *   <code>ResponseIncludeWrapper</code> was last modified, or -1 if not
+     *   known.                                                             
+     */
+    public long getLastModified() {                                                                                                                                                           
+        if (lastModified == -1) {
+            // javadocs say to return -1 if date not known, if you want another
+            // default, put it here
+            return -1;
+        }
+        return lastModified;
+    }
+
+    /**
+     * Sets the value of the <code>last-modified</code> header field.
+     *
+     * @param lastModified The number of milliseconds since January 1, 1970 GMT.
+     */
+    public void setLastModified(long lastModified) {
+        this.lastModified = lastModified;
+        ((HttpServletResponse) getResponse()).setDateHeader(LAST_MODIFIED,
+                lastModified);
+    }
+
+    /**
+     * Returns the value of the <code>content-type</code> header field.
+     *
+     * @return the content type of the resource referenced by this
+     *   <code>ResponseIncludeWrapper</code>, or <code>null</code> if not known.
+     */
+    public String getContentType() {
+        if (contentType == null) {
+            String url = request.getRequestURI();
+            String mime = context.getMimeType(url);
+            if (mime != null)
+            {
+                setContentType(mime);
+            }
+            else
+            {
+            	// return a safe value
+               setContentType("application/x-octet-stream");
+            }
+        }
+        return contentType;
+    }
+    
+    /**
+     * Sets the value of the <code>content-type</code> header field.
+     *
+     * @param mime a mime type
+     */
+    public void setContentType(String mime) {
+        contentType = mime;
+        if (contentType != null) {
+            getResponse().setContentType(contentType);
+        }
+    }
+
+
+    public void addDateHeader(String name, long value) {
+        super.addDateHeader(name, value);
+        String lname = name.toLowerCase(Locale.ENGLISH);
+        if (lname.equals(LAST_MODIFIED)) {
+            lastModified = value;
+        }
+    }
+
+    public void addHeader(String name, String value) {
+        super.addHeader(name, value);
+        String lname = name.toLowerCase(Locale.ENGLISH);
+        if (lname.equals(LAST_MODIFIED)) {
+            try {
+                lastModified = rfc1123Format.parse(value).getTime();
+            } catch (Throwable ignore) { }
+        } else if (lname.equals(CONTENT_TYPE)) {
+            contentType = value;
+        }
+    }
+
+    public void setDateHeader(String name, long value) {
+        super.setDateHeader(name, value);
+        String lname = name.toLowerCase(Locale.ENGLISH);
+        if (lname.equals(LAST_MODIFIED)) {
+            lastModified = value;
+        }
+    }
+
+    public void setHeader(String name, String value) {
+        super.setHeader(name, value);
+        String lname = name.toLowerCase(Locale.ENGLISH);
+        if (lname.equals(LAST_MODIFIED)) {
+            try {
+                lastModified = rfc1123Format.parse(value).getTime();
+            } catch (Throwable ignore) { }
+        }
+        else if (lname.equals(CONTENT_TYPE))
+        {
+            contentType = value;
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSICommand.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSICommand.java
new file mode 100644
index 0000000..479a324
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSICommand.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import java.io.PrintWriter;
+/**
+ * The interface that all SSI commands ( SSIEcho, SSIInclude, ...) must
+ * implement.
+ * 
+ * @author Bip Thelin
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:19 $
+ */
+public interface SSICommand {
+    /**
+     * Write the output of the command to the writer.
+     * 
+     * @param ssiMediator
+     *            the ssi mediator
+     * @param commandName
+     *            the name of the actual command ( ie. echo )
+     * @param paramNames
+     *            The parameter names
+     * @param paramValues
+     *            The parameter values
+     * @param writer
+     *            the writer to output to
+     * @return the most current modified date resulting from any SSI commands
+     * @throws SSIStopProcessingException
+     *             if SSI processing should be aborted
+     */
+	public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer)
+            throws SSIStopProcessingException;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConditional.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConditional.java
new file mode 100644
index 0000000..ed44e10
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConditional.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import java.io.PrintWriter;
+import java.text.ParseException;
+/**
+ * SSI command that handles all conditional directives.
+ * 
+ * @version $Revision: 1.2 $
+ * @author Paul Speed
+ * @author David Becker
+ */
+public class SSIConditional implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer)
+            throws SSIStopProcessingException {
+    	// Assume anything using conditionals was modified by it
+    	long lastModified = System.currentTimeMillis();
+        // Retrieve the current state information
+        SSIConditionalState state = ssiMediator.getConditionalState();
+        if ("if".equalsIgnoreCase(commandName)) {
+            // Do nothing if we are nested in a false branch
+            // except count it
+            if (state.processConditionalCommandsOnly) {
+                state.nestingCount++;
+                return lastModified;
+            }
+            state.nestingCount = 0;
+            // Evaluate the expression
+            if (evaluateArguments(paramNames, paramValues, ssiMediator)) {
+                // No more branches can be taken for this if block
+                state.branchTaken = true;
+            } else {
+                // Do not process this branch
+                state.processConditionalCommandsOnly = true;
+                state.branchTaken = false;
+            }
+        } else if ("elif".equalsIgnoreCase(commandName)) {
+            // No need to even execute if we are nested in
+            // a false branch
+            if (state.nestingCount > 0) return lastModified;
+            // If a branch was already taken in this if block
+            // then disable output and return
+            if (state.branchTaken) {
+                state.processConditionalCommandsOnly = true;
+                return lastModified;
+            }
+            // Evaluate the expression
+            if (evaluateArguments(paramNames, paramValues, ssiMediator)) {
+                // Turn back on output and mark the branch
+                state.processConditionalCommandsOnly = false;
+                state.branchTaken = true;
+            } else {
+                // Do not process this branch
+                state.processConditionalCommandsOnly = true;
+                state.branchTaken = false;
+            }
+        } else if ("else".equalsIgnoreCase(commandName)) {
+            // No need to even execute if we are nested in
+            // a false branch
+            if (state.nestingCount > 0) return lastModified;
+            // If we've already taken another branch then
+            // disable output otherwise enable it.
+            state.processConditionalCommandsOnly = state.branchTaken;
+            // And in any case, it's safe to say a branch
+            // has been taken.
+            state.branchTaken = true;
+        } else if ("endif".equalsIgnoreCase(commandName)) {
+            // If we are nested inside a false branch then pop out
+            // one level on the nesting count
+            if (state.nestingCount > 0) {
+                state.nestingCount--;
+                return lastModified;
+            }
+            // Turn output back on
+            state.processConditionalCommandsOnly = false;
+            // Reset the branch status for any outer if blocks,
+            // since clearly we took a branch to have gotten here
+            // in the first place.
+            state.branchTaken = true;
+        } else {
+            throw new SSIStopProcessingException();
+            //throw new SsiCommandException( "Not a conditional command:" +
+            // cmdName );
+        }
+        return lastModified;
+    }
+
+
+    /**
+     * Retrieves the expression from the specified arguments and peforms the
+     * necessary evaluation steps.
+     */
+    private boolean evaluateArguments(String[] names, String[] values,
+            SSIMediator ssiMediator) throws SSIStopProcessingException {
+        String expr = getExpression(names, values);
+        if (expr == null) {
+            throw new SSIStopProcessingException();
+            //throw new SsiCommandException( "No expression specified." );
+        }
+        try {
+            ExpressionParseTree tree = new ExpressionParseTree(expr,
+                    ssiMediator);
+            return tree.evaluateTree();
+        } catch (ParseException e) {
+            //throw new SsiCommandException( "Error parsing expression." );
+            throw new SSIStopProcessingException();
+        }
+    }
+
+
+    /**
+     * Returns the "expr" if the arg name is appropriate, otherwise returns
+     * null.
+     */
+    private String getExpression(String[] paramNames, String[] paramValues) {
+        if ("expr".equalsIgnoreCase(paramNames[0])) return paramValues[0];
+        return null;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConditionalState.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConditionalState.java
new file mode 100644
index 0000000..2667537
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConditionalState.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+/**
+ * This class is used by SSIMediator and SSIConditional to keep track of state
+ * information necessary to process the nested conditional commands ( if, elif,
+ * else, endif ).
+ * 
+ * @version $Revision: 1.2 $
+ * @author Dan Sandberg
+ * @author Paul Speed
+ */
+class SSIConditionalState {
+    /**
+     * Set to true if the current conditional has already been completed, i.e.:
+     * a branch was taken.
+     */
+    boolean branchTaken = false;
+    /**
+     * Counts the number of nested false branches.
+     */
+    int nestingCount = 0;
+    /**
+     * Set to true if only conditional commands ( if, elif, else, endif )
+     * should be processed.
+     */
+    boolean processConditionalCommandsOnly = false;
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConfig.java
new file mode 100644
index 0000000..1ff4bd9
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIConfig.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.PrintWriter;
+/**
+ * Implements the Server-side #exec command
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:19 $
+ */
+public final class SSIConfig implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            String substitutedValue = ssiMediator
+                    .substituteVariables(paramValue);
+            if (paramName.equalsIgnoreCase("errmsg")) {
+                ssiMediator.setConfigErrMsg(substitutedValue);
+            } else if (paramName.equalsIgnoreCase("sizefmt")) {
+                ssiMediator.setConfigSizeFmt(substitutedValue);
+            } else if (paramName.equalsIgnoreCase("timefmt")) {
+                ssiMediator.setConfigTimeFmt(substitutedValue);
+            } else {
+                ssiMediator.log("#config--Invalid attribute: " + paramName);
+                //We need to fetch this value each time, since it may change
+                // during the
+                // loop
+                String configErrMsg = ssiMediator.getConfigErrMsg();
+                writer.write(HtmlEntityEncoder.encodeXSS(configErrMsg));
+            }
+        }
+        // Setting config options doesn't really change the page
+        return 0;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIEcho.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIEcho.java
new file mode 100644
index 0000000..70ecda5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIEcho.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.PrintWriter;
+/**
+ * Return the result associated with the supplied Server Variable.
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:19 $
+ */
+public class SSIEcho implements SSICommand {
+    protected final static String DEFAULT_ENCODING = "entity";
+    protected final static String MISSING_VARIABLE_VALUE = "(none)";
+
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        String encoding = DEFAULT_ENCODING;
+        String originalValue = null;
+        String errorMessage = null; // delay the call of HtmlEntityEncoder.encode
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            if (paramName.equalsIgnoreCase("var")) {
+                originalValue = paramValue;
+            } else if (paramName.equalsIgnoreCase("encoding")) {
+                if (isValidEncoding(paramValue)) {
+                    encoding = paramValue;
+                } else {
+                    ssiMediator.log("#echo--Invalid encoding: " + paramValue);
+                    if (errorMessage == null) {
+                        errorMessage = getEncodedConfigErrorMessage(ssiMediator);
+                    }
+                    writer.write(errorMessage);
+                }
+            } else {
+                ssiMediator.log("#echo--Invalid attribute: " + paramName);
+                if (errorMessage == null) {
+                    errorMessage = getEncodedConfigErrorMessage(ssiMediator);
+                }
+                writer.write(errorMessage);
+            }
+        }
+        String variableValue = ssiMediator.getVariableValue(
+                originalValue, encoding);
+        if (variableValue == null) {
+            variableValue = MISSING_VARIABLE_VALUE;
+        }
+        writer.write(variableValue);
+        return System.currentTimeMillis();
+    }
+
+
+    protected boolean isValidEncoding(String encoding) {
+        return encoding.equalsIgnoreCase("url")
+                || encoding.equalsIgnoreCase("entity")
+                || encoding.equalsIgnoreCase("none");
+    }
+
+    private String getEncodedConfigErrorMessage(SSIMediator ssiMediator) {
+        String errorMessage = ssiMediator.getConfigErrMsg();
+        return HtmlEntityEncoder.encodeXSS(errorMessage);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIExec.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIExec.java
new file mode 100644
index 0000000..a7fe5f9
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIExec.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.apache.catalina.util.IOTools;
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+/**
+ * Implements the Server-side #exec command
+ * 
+ * @author Bip Thelin
+ * @author Amy Roh
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:19 $
+ */
+public class SSIExec implements SSICommand {
+    protected SSIInclude ssiInclude;
+    protected final static int BUFFER_SIZE = 1024;
+
+    public SSIExec() {
+        this.ssiInclude = new SSIInclude();
+    }
+
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
+        String configErrMsg = ssiMediator.getConfigErrMsg();
+        String paramName = paramNames[0];
+        String paramValue = paramValues[0];
+        String substitutedValue = ssiMediator.substituteVariables(paramValue);
+        if (paramName.equalsIgnoreCase("cgi")) {
+            lastModified = ssiInclude.process(ssiMediator, "include",
+                    			new String[]{"virtual"}, new String[]{substitutedValue},
+								writer);
+        } else if (paramName.equalsIgnoreCase("cmd")) {
+            boolean foundProgram = false;
+            try {
+                Runtime rt = Runtime.getRuntime();
+                Process proc = rt.exec(substitutedValue);
+                foundProgram = true;
+                BufferedReader stdOutReader = new BufferedReader(
+                        new InputStreamReader(proc.getInputStream()));
+                BufferedReader stdErrReader = new BufferedReader(
+                        new InputStreamReader(proc.getErrorStream()));
+                char[] buf = new char[BUFFER_SIZE];
+                IOTools.flow(stdErrReader, writer, buf);
+                IOTools.flow(stdOutReader, writer, buf);
+                proc.waitFor();
+                lastModified = System.currentTimeMillis();                
+            } catch (InterruptedException e) {
+                ssiMediator.log("Couldn't exec file: " + substitutedValue, e);
+                writer.write(HtmlEntityEncoder.encodeXSS(configErrMsg));
+            } catch (IOException e) {
+                //if (!foundProgram) {
+                    //apache doesn't output an error message if it can't find
+                    // a program
+                //}
+                ssiMediator.log("Couldn't exec file: " + substitutedValue, e);
+            }
+        }
+        return lastModified;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIExternalResolver.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIExternalResolver.java
new file mode 100644
index 0000000..b1b6b71
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIExternalResolver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+/**
+ * Interface used by SSIMediator to talk to the 'outside world' ( usually a
+ * servlet )
+ * 
+ * @author Dan Sandberg
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:19 $
+ */
+public interface SSIExternalResolver {
+    /**
+     * Adds any external variables to the variableNames collection.
+     * 
+     * @param variableNames
+     *            the collection to add to
+     */
+    public void addVariableNames(Collection<String> variableNames);
+
+
+    public String getVariableValue(String name);
+
+
+    /**
+     * Set the named variable to the specified value. If value is null, then
+     * the variable will be removed ( ie. a call to getVariableValue will
+     * return null )
+     * 
+     * @param name
+     *            of the variable
+     * @param value
+     *            of the variable
+     */
+    public void setVariableValue(String name, String value);
+
+
+    /**
+     * Returns the current date. This is useful for putting the SSI stuff in a
+     * regression test. Since you can make the current date a constant, it
+     * makes testing easier since the output won't change.
+     * 
+     * @return the data
+     */
+    public Date getCurrentDate();
+
+
+    public long getFileSize(String path, boolean virtual) throws IOException;
+
+
+    public long getFileLastModified(String path, boolean virtual)
+            throws IOException;
+
+
+    public String getFileText(String path, boolean virtual) throws IOException;
+
+
+    public void log(String message, Throwable throwable);
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFilter.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFilter.java
new file mode 100644
index 0000000..6ce7238
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFilter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.apache.catalina.Globals;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+/**
+ * Filter to process SSI requests within a webpage. Mapped to a content types
+ * from within web.xml.
+ * 
+ * @author David Becker
+ * @version $Revision: 1.1 $, $Date: 2007/02/13 19:16:21 $
+ * @see org.apache.catalina.ssi.SSIServlet
+ */
+public class SSIFilter implements Filter {
+	protected FilterConfig config = null;
+    /** Debug level for this servlet. */
+    protected int debug = 0;
+    /** Expiration time in seconds for the doc. */
+    protected Long expires = null;
+    /** virtual path can be webapp-relative */
+    protected boolean isVirtualWebappRelative = false;
+    /** regex pattern to match when evaluating content types */
+	protected Pattern contentTypeRegEx = null;
+	/** default pattern for ssi filter content type matching */
+	protected Pattern shtmlRegEx =
+        Pattern.compile("text/x-server-parsed-html(;.*)?");
+
+
+    //----------------- Public methods.
+    /**
+     * Initialize this servlet.
+     * 
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void init(FilterConfig config) throws ServletException {
+    	this.config = config;
+    	
+        if (config.getInitParameter("debug") != null) {
+            debug = Integer.parseInt(config.getInitParameter("debug"));
+        }
+
+        if (config.getInitParameter("contentType") != null) {
+            contentTypeRegEx = Pattern.compile(config.getInitParameter("contentType"));
+        } else {
+            contentTypeRegEx = shtmlRegEx;
+        }
+
+        isVirtualWebappRelative = 
+            Boolean.parseBoolean(config.getInitParameter("isVirtualWebappRelative"));
+
+        if (config.getInitParameter("expires") != null)
+            expires = Long.valueOf(config.getInitParameter("expires"));
+
+        if (debug > 0)
+            config.getServletContext().log(
+                    "SSIFilter.init() SSI invoker started with 'debug'=" + debug);
+    }
+
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain chain) throws IOException, ServletException {
+        // cast once
+        HttpServletRequest req = (HttpServletRequest)request;
+        HttpServletResponse res = (HttpServletResponse)response;
+        
+        // indicate that we're in SSI processing
+        req.setAttribute(Globals.SSI_FLAG_ATTR, "true");           
+
+        // setup to capture output
+        ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream();
+        ResponseIncludeWrapper responseIncludeWrapper =
+            new ResponseIncludeWrapper(config.getServletContext(),req, res, basos);
+
+        // process remainder of filter chain
+        chain.doFilter(req, responseIncludeWrapper);
+
+        // we can't assume the chain flushed its output
+        responseIncludeWrapper.flushOutputStreamOrWriter();
+        byte[] bytes = basos.toByteArray();
+
+        // get content type
+        String contentType = responseIncludeWrapper.getContentType();
+
+        // is this an allowed type for SSI processing?
+        if (contentTypeRegEx.matcher(contentType).matches()) {
+            String encoding = res.getCharacterEncoding();
+
+            // set up SSI processing 
+            SSIExternalResolver ssiExternalResolver =
+                new SSIServletExternalResolver(config.getServletContext(), req,
+                        res, isVirtualWebappRelative, debug, encoding);
+            SSIProcessor ssiProcessor = new SSIProcessor(ssiExternalResolver,
+                    debug);
+            
+            // prepare readers/writers
+            Reader reader =
+                new InputStreamReader(new ByteArrayInputStream(bytes), encoding);
+            ByteArrayOutputStream ssiout = new ByteArrayOutputStream();
+            PrintWriter writer =
+                new PrintWriter(new OutputStreamWriter(ssiout, encoding));
+            
+            // do SSI processing  
+            long lastModified = ssiProcessor.process(reader,
+                    responseIncludeWrapper.getLastModified(), writer);
+            
+            // set output bytes
+            writer.flush();
+            bytes = ssiout.toByteArray();
+            
+            // override headers
+            if (expires != null) {
+                res.setDateHeader("expires", (new java.util.Date()).getTime()
+                        + expires.longValue() * 1000);
+            }
+            if (lastModified > 0) {
+                res.setDateHeader("last-modified", lastModified);
+            }
+            res.setContentLength(bytes.length);
+            
+            Matcher shtmlMatcher =
+                shtmlRegEx.matcher(responseIncludeWrapper.getContentType());
+            if (shtmlMatcher.matches()) {
+            	// Convert shtml mime type to ordinary html mime type but preserve
+                // encoding, if any.
+            	String enc = shtmlMatcher.group(1);
+            	res.setContentType("text/html" + ((enc != null) ? enc : ""));
+            }
+        }
+
+        // write output
+        OutputStream out = null;
+        try {
+            out = res.getOutputStream();
+        } catch (IllegalStateException e) {
+            // Ignore, will try to use a writer
+        }
+        if (out == null) {
+            res.getWriter().write(new String(bytes, Charset.defaultCharset()));
+        } else {
+            out.write(bytes);
+        }
+    }
+
+    public void destroy() {
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFlastmod.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFlastmod.java
new file mode 100644
index 0000000..521ea7c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFlastmod.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.apache.catalina.util.Strftime;
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.Locale;
+/**
+ * Implements the Server-side #flastmod command
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.5 $, $Date: 2007/05/05 05:32:20 $
+ */
+public final class SSIFlastmod implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+    	long lastModified = 0;
+        String configErrMsg = null;
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            String substitutedValue = ssiMediator
+                    .substituteVariables(paramValue);
+            try {
+                if (paramName.equalsIgnoreCase("file")
+                        || paramName.equalsIgnoreCase("virtual")) {
+                    boolean virtual = paramName.equalsIgnoreCase("virtual");
+                    lastModified = ssiMediator.getFileLastModified(
+                            substitutedValue, virtual);
+                    Date date = new Date(lastModified);
+                    String configTimeFmt = ssiMediator.getConfigTimeFmt();
+                    writer.write(formatDate(date, configTimeFmt));
+                } else {
+                    ssiMediator.log("#flastmod--Invalid attribute: "
+                            + paramName);
+                    if (configErrMsg == null) {
+                        configErrMsg = getEncodedConfigErrorMessage(ssiMediator);
+                    }
+                    writer.write(configErrMsg);
+                }
+            } catch (IOException e) {
+                ssiMediator.log(
+                        "#flastmod--Couldn't get last modified for file: "
+                                + substitutedValue, e);
+                if (configErrMsg == null) {
+                    configErrMsg = getEncodedConfigErrorMessage(ssiMediator);
+                }
+                writer.write(configErrMsg);
+            }
+        }
+        return lastModified;
+    }
+
+
+    protected String formatDate(Date date, String configTimeFmt) {
+        Strftime strftime = new Strftime(configTimeFmt, Locale.US);
+        return strftime.format(date);
+    }
+
+    private String getEncodedConfigErrorMessage(SSIMediator ssiMediator) {
+        String configErrMsg = ssiMediator.getConfigErrMsg();
+        return HtmlEntityEncoder.encodeXSS(configErrMsg);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFsize.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFsize.java
new file mode 100644
index 0000000..c965208
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIFsize.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.DecimalFormat;
+/**
+ * Implements the Server-side #fsize command
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:20 $
+ */
+public final class SSIFsize implements SSICommand {
+    private final static int ONE_KILOBYTE = 1024;
+    private final static int ONE_MEGABYTE = 1024 * 1024;
+
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
+        String configErrMsg = null;
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            String substitutedValue = ssiMediator
+                    .substituteVariables(paramValue);
+            try {
+                if (paramName.equalsIgnoreCase("file")
+                        || paramName.equalsIgnoreCase("virtual")) {
+                    boolean virtual = paramName.equalsIgnoreCase("virtual");
+                    lastModified = ssiMediator.getFileLastModified(
+                            substitutedValue, virtual);
+                    long size = ssiMediator.getFileSize(substitutedValue,
+                            virtual);
+                    String configSizeFmt = ssiMediator.getConfigSizeFmt();
+                    writer.write(formatSize(size, configSizeFmt));
+                } else {
+                    ssiMediator.log("#fsize--Invalid attribute: " + paramName);
+                    if (configErrMsg == null) {
+                        configErrMsg = getEncodedConfigErrorMessage(ssiMediator);
+                    }
+                    writer.write(configErrMsg);
+                }
+            } catch (IOException e) {
+                ssiMediator.log("#fsize--Couldn't get size for file: "
+                        + substitutedValue, e);
+                if (configErrMsg == null) {
+                    configErrMsg = getEncodedConfigErrorMessage(ssiMediator);
+                }
+                writer.write(configErrMsg);
+            }
+        }
+        return lastModified;
+    }
+
+
+    public String repeat(char aChar, int numChars) {
+        if (numChars < 0) {
+            throw new IllegalArgumentException("Num chars can't be negative");
+        }
+        StringBuilder buf = new StringBuilder();
+        for (int i = 0; i < numChars; i++) {
+            buf.append(aChar);
+        }
+        return buf.toString();
+    }
+
+
+    public String padLeft(String str, int maxChars) {
+        String result = str;
+        int charsToAdd = maxChars - str.length();
+        if (charsToAdd > 0) {
+            result = repeat(' ', charsToAdd) + str;
+        }
+        return result;
+    }
+
+
+    //We try to mimic Apache here, as we do everywhere
+    //All the 'magic' numbers are from the util_script.c Apache source file.
+    protected String formatSize(long size, String format) {
+        String retString = "";
+        if (format.equalsIgnoreCase("bytes")) {
+            DecimalFormat decimalFormat = new DecimalFormat("#,##0");
+            retString = decimalFormat.format(size);
+        } else {
+            if (size < 0) {
+                retString = "-1k";
+            } else if (size == 0) {
+                retString = "0k";
+            } else if (size < ONE_KILOBYTE) {
+                retString = "1k";
+            } else if (size < ONE_MEGABYTE) {
+                retString = Long.toString((size + 512) / ONE_KILOBYTE);
+                retString += "k";
+            } else if (size < 99 * ONE_MEGABYTE) {
+                DecimalFormat decimalFormat = new DecimalFormat("0.0M");
+                retString = decimalFormat.format(size / (double)ONE_MEGABYTE);
+            } else {
+                retString = Long.toString((size + (529 * ONE_KILOBYTE))
+                        / ONE_MEGABYTE);
+                retString += "M";
+            }
+            retString = padLeft(retString, 5);
+        }
+        return retString;
+    }
+
+    private String getEncodedConfigErrorMessage(SSIMediator ssiMediator) {
+        String errorMessage = ssiMediator.getConfigErrMsg();
+        return HtmlEntityEncoder.encodeXSS(errorMessage);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIInclude.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIInclude.java
new file mode 100644
index 0000000..05b65fa
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIInclude.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+/**
+ * Implements the Server-side #include command
+ * 
+ * @author Bip Thelin
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:20 $
+ */
+public final class SSIInclude implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
+        String configErrMsg = null;
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            String substitutedValue = ssiMediator
+                    .substituteVariables(paramValue);
+            try {
+                if (paramName.equalsIgnoreCase("file")
+                        || paramName.equalsIgnoreCase("virtual")) {
+                    boolean virtual = paramName.equalsIgnoreCase("virtual");
+                    lastModified = ssiMediator.getFileLastModified(
+                    		 substitutedValue, virtual);
+                    String text = ssiMediator.getFileText(substitutedValue,
+                            virtual);
+                    writer.write(text);
+                } else {
+                    ssiMediator.log("#include--Invalid attribute: "
+                            + paramName);
+                    if (configErrMsg == null) {
+                        configErrMsg = getEncodedConfigErrorMessage(ssiMediator);
+                    }
+                    writer.write(configErrMsg);
+                }
+            } catch (IOException e) {
+                ssiMediator.log("#include--Couldn't include file: "
+                        + substitutedValue, e);
+                if (configErrMsg == null) {
+                    configErrMsg = getEncodedConfigErrorMessage(ssiMediator);
+                }
+                writer.write(configErrMsg);
+            }
+        }
+        return lastModified;
+    }
+
+    private String getEncodedConfigErrorMessage(SSIMediator ssiMediator) {
+        String errorMessage = ssiMediator.getConfigErrMsg();
+        return HtmlEntityEncoder.encodeXSS(errorMessage);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIMediator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIMediator.java
new file mode 100644
index 0000000..237332b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIMediator.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.apache.catalina.util.Strftime;
+import org.apache.catalina.util.URLEncoder;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TimeZone;
+import org.glassfish.grizzly.http.util.HttpUtils;
+
+/**
+ * Allows the different SSICommand implementations to share data/talk to each
+ * other
+ * 
+ * @author Bip Thelin
+ * @author Amy Roh
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.5 $, $Date: 2007/05/05 05:32:20 $
+ */
+public class SSIMediator {
+    protected final static String DEFAULT_CONFIG_ERR_MSG = "[an error occurred while processing this directive]";
+    protected final static String DEFAULT_CONFIG_TIME_FMT = "%A, %d-%b-%Y %T %Z";
+    protected final static String DEFAULT_CONFIG_SIZE_FMT = "abbrev";
+    protected final static URLEncoder urlEncoder;
+    protected String configErrMsg = DEFAULT_CONFIG_ERR_MSG;
+    protected String configTimeFmt = DEFAULT_CONFIG_TIME_FMT;
+    protected String configSizeFmt = DEFAULT_CONFIG_SIZE_FMT;
+    protected String className = getClass().getName();
+    protected SSIExternalResolver ssiExternalResolver;
+    protected long lastModifiedDate;
+    protected Strftime strftime;
+    protected SSIConditionalState conditionalState = new SSIConditionalState();
+    static {
+        //We try to encode only the same characters that apache does
+        urlEncoder = new URLEncoder();
+        urlEncoder.addSafeCharacter(',');
+        urlEncoder.addSafeCharacter(':');
+        urlEncoder.addSafeCharacter('-');
+        urlEncoder.addSafeCharacter('_');
+        urlEncoder.addSafeCharacter('.');
+        urlEncoder.addSafeCharacter('*');
+        urlEncoder.addSafeCharacter('/');
+        urlEncoder.addSafeCharacter('!');
+        urlEncoder.addSafeCharacter('~');
+        urlEncoder.addSafeCharacter('\'');
+        urlEncoder.addSafeCharacter('(');
+        urlEncoder.addSafeCharacter(')');
+    }
+
+
+    public SSIMediator(SSIExternalResolver ssiExternalResolver,
+            long lastModifiedDate, int debug) {
+        this.ssiExternalResolver = ssiExternalResolver;
+        this.lastModifiedDate = lastModifiedDate;
+        setConfigTimeFmt(DEFAULT_CONFIG_TIME_FMT, true);
+    }
+
+
+    public void setConfigErrMsg(String configErrMsg) {
+        this.configErrMsg = configErrMsg;
+    }
+
+
+    public void setConfigTimeFmt(String configTimeFmt) {
+        setConfigTimeFmt(configTimeFmt, false);
+    }
+
+
+    public void setConfigTimeFmt(String configTimeFmt, boolean fromConstructor) {
+        this.configTimeFmt = configTimeFmt;
+        //What's the story here with Locale.US?? Why??
+        this.strftime = new Strftime(configTimeFmt, Locale.US);
+        //Variables like DATE_LOCAL, DATE_GMT, and LAST_MODIFIED need to be
+        // updated when
+        //the timefmt changes. This is what Apache SSI does.
+        setDateVariables(fromConstructor);
+    }
+
+
+    public void setConfigSizeFmt(String configSizeFmt) {
+        this.configSizeFmt = configSizeFmt;
+    }
+
+
+    public String getConfigErrMsg() {
+        return configErrMsg;
+    }
+
+
+    public String getConfigTimeFmt() {
+        return configTimeFmt;
+    }
+
+
+    public String getConfigSizeFmt() {
+        return configSizeFmt;
+    }
+
+
+    public SSIConditionalState getConditionalState() {
+        return conditionalState;
+    }
+
+
+    public Collection<String> getVariableNames() {
+        Set<String> variableNames = new HashSet<String>();
+        //These built-in variables are supplied by the mediator ( if not
+        // over-written by
+        // the user ) and always exist
+        variableNames.add("DATE_GMT");
+        variableNames.add("DATE_LOCAL");
+        variableNames.add("LAST_MODIFIED");
+        ssiExternalResolver.addVariableNames(variableNames);
+        //Remove any variables that are reserved by this class
+        Iterator<String> iter = variableNames.iterator();
+        while (iter.hasNext()) {
+            String name = iter.next();
+            if (isNameReserved(name)) {
+                iter.remove();
+            }
+        }
+        return variableNames;
+    }
+
+
+    public long getFileSize(String path, boolean virtual) throws IOException {
+        return ssiExternalResolver.getFileSize(path, virtual);
+    }
+
+
+    public long getFileLastModified(String path, boolean virtual)
+            throws IOException {
+        return ssiExternalResolver.getFileLastModified(path, virtual);
+    }
+
+
+    public String getFileText(String path, boolean virtual) throws IOException {
+        return ssiExternalResolver.getFileText(path, virtual);
+    }
+
+
+    protected boolean isNameReserved(String name) {
+        return name.startsWith(className + ".");
+    }
+
+
+    public String getVariableValue(String variableName) {
+        return getVariableValue(variableName, "none");
+    }
+
+
+    public void setVariableValue(String variableName, String variableValue) {
+        if (!isNameReserved(variableName)) {
+            ssiExternalResolver.setVariableValue(variableName, variableValue);
+        }
+    }
+
+
+    public String getVariableValue(String variableName, String encoding) {
+        String lowerCaseVariableName = variableName.toLowerCase(Locale.ENGLISH);
+        String variableValue = null;
+        if (!isNameReserved(lowerCaseVariableName)) {
+            //Try getting it externally first, if it fails, try getting the
+            // 'built-in'
+            // value
+            variableValue = ssiExternalResolver.getVariableValue(variableName);
+            if (variableValue == null) {
+                variableName = variableName.toUpperCase(Locale.ENGLISH);
+                variableValue = ssiExternalResolver
+                        .getVariableValue(className + "." + variableName);
+            }
+            if (variableValue != null) {
+                variableValue = encode(variableValue, encoding);
+            }
+        }
+        return variableValue;
+    }
+
+
+    /**
+     * Applies variable substitution to the specified String and returns the
+     * new resolved string.
+     */
+    public String substituteVariables(String val) {
+        // If it has no references or HTML entities then no work
+        // need to be done
+        if (val.indexOf('$') < 0 && val.indexOf('&') < 0) return val;
+
+        // HTML decoding
+        val = val.replace("&lt;", "<");
+        val = val.replace("&gt;", ">");
+        val = val.replace("&quot;", "\"");
+        val = val.replace("&amp;", "&");
+
+        StringBuilder sb = new StringBuilder(val);
+        int charStart = sb.indexOf("&#");
+        while (charStart > -1) {
+            int charEnd = sb.indexOf(";", charStart);
+            if (charEnd > -1) {
+                char c = (char) Integer.parseInt(
+                        sb.substring(charStart + 2, charEnd));
+                sb.delete(charStart, charEnd + 1);
+                sb.insert(charStart, c);
+                charStart = sb.indexOf("&#");
+            } else {
+                break;
+            }
+        }
+
+        for (int i = 0; i < sb.length();) {
+            // Find the next $
+            for (; i < sb.length(); i++) {
+                if (sb.charAt(i) == '$') {
+                    i++;
+                    break;
+                }
+            }
+            if (i == sb.length()) break;
+            // Check to see if the $ is escaped
+            if (i > 1 && sb.charAt(i - 2) == '\\') {
+                sb.deleteCharAt(i - 2);
+                i--;
+                continue;
+            }
+            int nameStart = i;
+            int start = i - 1;
+            int end = -1;
+            int nameEnd = -1;
+            char endChar = ' ';
+            // Check for {} wrapped var
+            if (sb.charAt(i) == '{') {
+                nameStart++;
+                endChar = '}';
+            }
+            // Find the end of the var reference
+            for (; i < sb.length(); i++) {
+                if (sb.charAt(i) == endChar) break;
+            }
+            end = i;
+            nameEnd = end;
+            if (endChar == '}') end++;
+            // We should now have enough to extract the var name
+            String varName = sb.substring(nameStart, nameEnd);
+            String value = getVariableValue(varName);
+            if (value == null) value = "";
+            // Replace the var name with its value
+            sb.replace(start, end, value);
+            // Start searching for the next $ after the value
+            // that was just substituted.
+            i = start + value.length();
+        }
+        return sb.toString();
+    }
+
+
+    protected String formatDate(Date date, TimeZone timeZone) {
+        String retVal;
+        if (timeZone != null) {
+            //we temporarily change strftime. Since SSIMediator is inherently
+            // single-threaded, this
+            //isn't a problem
+            TimeZone oldTimeZone = strftime.getTimeZone();
+            strftime.setTimeZone(timeZone);
+            retVal = strftime.format(date);
+            strftime.setTimeZone(oldTimeZone);
+        } else {
+            retVal = strftime.format(date);
+        }
+        return retVal;
+    }
+
+
+    protected String encode(String value, String encoding) {
+        String retVal = null;
+        if (encoding.equalsIgnoreCase("url")) {
+            retVal = urlEncoder.encode(value);
+        } else if (encoding.equalsIgnoreCase("none")) {
+            retVal = value;
+        } else if (encoding.equalsIgnoreCase("entity")) {
+            //Not sure how this is really different than none
+            retVal = HttpUtils.filter(value);
+        } else {
+            //This shouldn't be possible
+            throw new IllegalArgumentException("Unknown encoding: " + encoding);
+        }
+        return retVal;
+    }
+
+
+    public void log(String message) {
+        ssiExternalResolver.log(message, null);
+    }
+
+
+    public void log(String message, Throwable throwable) {
+        ssiExternalResolver.log(message, throwable);
+    }
+
+
+    protected void setDateVariables(boolean fromConstructor) {
+        boolean alreadySet = ssiExternalResolver.getVariableValue(className
+                + ".alreadyset") != null;
+        //skip this if we are being called from the constructor, and this has
+        // already
+        // been set
+        if (!(fromConstructor && alreadySet)) {
+            ssiExternalResolver.setVariableValue(className + ".alreadyset",
+                    "true");
+            Date date = new Date();
+            TimeZone timeZone = TimeZone.getTimeZone("GMT");
+            String retVal = formatDate(date, timeZone);
+            //If we are setting on of the date variables, we want to remove
+            // them from the
+            // user
+            //defined list of variables, because this is what Apache does
+            setVariableValue("DATE_GMT", null);
+            ssiExternalResolver.setVariableValue(className + ".DATE_GMT",
+                    retVal);
+            retVal = formatDate(date, null);
+            setVariableValue("DATE_LOCAL", null);
+            ssiExternalResolver.setVariableValue(className + ".DATE_LOCAL",
+                    retVal);
+            retVal = formatDate(new Date(lastModifiedDate), null);
+            setVariableValue("LAST_MODIFIED", null);
+            ssiExternalResolver.setVariableValue(className + ".LAST_MODIFIED",
+                    retVal);
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIPrintenv.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIPrintenv.java
new file mode 100644
index 0000000..57d4828
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIPrintenv.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Iterator;
+/**
+ * Implements the Server-side #printenv command
+ * 
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.3 $, $Date: 2007/02/13 19:16:21 $
+ */
+public class SSIPrintenv implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer) {
+    	long lastModified = 0;
+        //any arguments should produce an error
+        if (paramNames.length > 0) {
+            String errorMessage = ssiMediator.getConfigErrMsg();
+            writer.write(HtmlEntityEncoder.encodeXSS(errorMessage));
+        } else {
+            Collection<String> variableNames = ssiMediator.getVariableNames();
+            Iterator<String> iter = variableNames.iterator();
+            while (iter.hasNext()) {
+                String variableName = iter.next();
+                String variableValue = ssiMediator
+                        .getVariableValue(variableName);
+                //This shouldn't happen, since all the variable names must
+                // have values
+                if (variableValue == null) {
+                    variableValue = "(none)";
+                }
+                writer.write(variableName);
+                writer.write('=');
+                writer.write(variableValue);
+                writer.write('\n');
+                lastModified = System.currentTimeMillis();
+            }
+        }
+        return lastModified;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIProcessor.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIProcessor.java
new file mode 100644
index 0000000..f08187e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIProcessor.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.apache.catalina.util.IOTools;
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.StringTokenizer;
+/**
+ * The entry point to SSI processing. This class does the actual parsing,
+ * delegating to the SSIMediator, SSICommand, and SSIExternalResolver as
+ * necessary[
+ * 
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:20 $
+ */
+public class SSIProcessor {
+    /** The start pattern */
+    protected final static String COMMAND_START = "<!--#";
+    /** The end pattern */
+    protected final static String COMMAND_END = "-->";
+    protected final static int BUFFER_SIZE = 4096;
+    protected SSIExternalResolver ssiExternalResolver;
+    protected HashMap<String, SSICommand> commands =
+        new HashMap<String, SSICommand>();
+    protected int debug;
+
+
+    public SSIProcessor(SSIExternalResolver ssiExternalResolver, int debug) {
+        this.ssiExternalResolver = ssiExternalResolver;
+        this.debug = debug;
+        addBuiltinCommands();
+    }
+
+
+    protected void addBuiltinCommands() {
+        addCommand("config", new SSIConfig());
+        addCommand("echo", new SSIEcho());
+        addCommand("exec", new SSIExec());
+        addCommand("include", new SSIInclude());
+        addCommand("flastmod", new SSIFlastmod());
+        addCommand("fsize", new SSIFsize());
+        addCommand("printenv", new SSIPrintenv());
+        addCommand("set", new SSISet());
+        SSIConditional ssiConditional = new SSIConditional();
+        addCommand("if", ssiConditional);
+        addCommand("elif", ssiConditional);
+        addCommand("endif", ssiConditional);
+        addCommand("else", ssiConditional);
+    }
+
+
+    public void addCommand(String name, SSICommand command) {
+        commands.put(name, command);
+    }
+
+
+    /**
+     * Process a file with server-side commands, reading from reader and
+     * writing the processed version to writer. NOTE: We really should be doing
+     * this in a streaming way rather than converting it to an array first.
+     * 
+     * @param reader
+     *            the reader to read the file containing SSIs from
+     * @param writer
+     *            the writer to write the file with the SSIs processed.
+     * @return the most current modified date resulting from any SSI commands
+     * @throws IOException
+     *             when things go horribly awry. Should be unlikely since the
+     *             SSICommand usually catches 'normal' IOExceptions.
+     */
+    public long process(Reader reader, long lastModifiedDate,
+            PrintWriter writer) throws IOException {
+        SSIMediator ssiMediator = new SSIMediator(ssiExternalResolver,
+                lastModifiedDate, debug);
+        StringWriter stringWriter = new StringWriter();
+        IOTools.flow(reader, stringWriter);
+        String fileContents = stringWriter.toString();
+        int index = 0;
+        boolean inside = false;
+        StringBuilder command = new StringBuilder();
+        try {
+            while (index < fileContents.length()) {
+                char c = fileContents.charAt(index);
+                if (!inside) {
+                    if (c == COMMAND_START.charAt(0)
+                            && charCmp(fileContents, index, COMMAND_START)) {
+                        inside = true;
+                        index += COMMAND_START.length();
+                        command.setLength(0); //clear the command string
+                    } else {
+                        if (!ssiMediator.getConditionalState().processConditionalCommandsOnly) {
+                            writer.write(c);
+                        }
+                        index++;
+                    }
+                } else {
+                    if (c == COMMAND_END.charAt(0)
+                            && charCmp(fileContents, index, COMMAND_END)) {
+                        inside = false;
+                        index += COMMAND_END.length();
+                        String strCmd = parseCmd(command);
+                        if (debug > 0) {
+                            ssiExternalResolver.log(
+                                    "SSIProcessor.process -- processing command: "
+                                            + strCmd, null);
+                        }
+                        String[] paramNames = parseParamNames(command, strCmd
+                                .length());
+                        String[] paramValues = parseParamValues(command,
+                                strCmd.length(), paramNames.length);
+                        //We need to fetch this value each time, since it may
+                        // change
+                        // during the loop
+                        String configErrMsg = ssiMediator.getConfigErrMsg();
+                        SSICommand ssiCommand =
+                                commands.get(strCmd.toLowerCase(Locale.ENGLISH));
+                        String errorMessage = null;
+                        if (ssiCommand == null) {
+                            errorMessage = "Unknown command: " + strCmd;
+                        } else if (paramValues == null) {
+                            errorMessage = "Error parsing directive parameters.";
+                        } else if (paramNames.length != paramValues.length) {
+                            errorMessage = "Parameter names count does not match parameter values count on command: "
+                                    + strCmd;
+                        } else {
+                            // don't process the command if we are processing
+                            // conditional
+                            // commands only and the
+                            // command is not conditional
+                            if (!ssiMediator.getConditionalState().processConditionalCommandsOnly
+                                    || ssiCommand instanceof SSIConditional) {
+                                long lmd = ssiCommand.process(ssiMediator, strCmd,
+                                               paramNames, paramValues, writer);
+                                if (lmd > lastModifiedDate) {
+                                    lastModifiedDate = lmd;
+                                }                                    
+                            }
+                        }
+                        if (errorMessage != null) {
+                            ssiExternalResolver.log(errorMessage, null);
+                            writer.write(HtmlEntityEncoder.encodeXSS(configErrMsg));
+                        }
+                    } else {
+                        command.append(c);
+                        index++;
+                    }
+                }
+            }
+        } catch (SSIStopProcessingException e) {
+            //If we are here, then we have already stopped processing, so all
+            // is good
+        }
+        return lastModifiedDate;
+    }
+
+
+    /**
+     * Parse a StringBuilder and take out the param type token. Called from
+     * <code>requestHandler</code>
+     * 
+     * @param cmd
+     *            a value of type 'StringBuilder'
+     * @return a value of type 'String[]'
+     */
+    protected String[] parseParamNames(StringBuilder cmd, int start) {
+        int bIdx = start;
+        int i = 0;
+        int quotes = 0;
+        boolean inside = false;
+        StringBuilder retBuf = new StringBuilder();
+        while (bIdx < cmd.length()) {
+            if (!inside) {
+                while (bIdx < cmd.length() && isSpace(cmd.charAt(bIdx)))
+                    bIdx++;
+                if (bIdx >= cmd.length()) break;
+                inside = !inside;
+            } else {
+                while (bIdx < cmd.length() && cmd.charAt(bIdx) != '=') {
+                    retBuf.append(cmd.charAt(bIdx));
+                    bIdx++;
+                }
+                retBuf.append('=');
+                inside = !inside;
+                quotes = 0;
+                boolean escaped = false;
+                for (; bIdx < cmd.length() && quotes != 2; bIdx++) {
+                    char c = cmd.charAt(bIdx);
+                    // Need to skip escaped characters
+                    if (c == '\\' && !escaped) {
+                        escaped = true;
+                        continue;
+                    }
+                    if (c == '"' && !escaped) quotes++;
+                    escaped = false;
+                }
+            }
+        }
+        StringTokenizer str = new StringTokenizer(retBuf.toString(), "=");
+        String[] retString = new String[str.countTokens()];
+        while (str.hasMoreTokens()) {
+            retString[i++] = str.nextToken().trim();
+        }
+        return retString;
+    }
+
+
+    /**
+     * Parse a StringBuilder and take out the param token. Called from
+     * <code>requestHandler</code>
+     * 
+     * @param cmd
+     *            a value of type 'StringBuilder'
+     * @return a value of type 'String[]'
+     */
+    protected String[] parseParamValues(StringBuilder cmd, int start, int count) {
+        int valIndex = 0;
+        boolean inside = false;
+        String[] vals = new String[count];
+        StringBuilder sb = new StringBuilder();
+        char endQuote = 0;
+        for (int bIdx = start; bIdx < cmd.length(); bIdx++) {
+            if (!inside) {
+                while (bIdx < cmd.length() && !isQuote(cmd.charAt(bIdx)))
+                    bIdx++;
+                if (bIdx >= cmd.length()) break;
+                inside = !inside;
+                endQuote = cmd.charAt(bIdx);
+            } else {
+                boolean escaped = false;
+                for (; bIdx < cmd.length(); bIdx++) {
+                    char c = cmd.charAt(bIdx);
+                    // Check for escapes
+                    if (c == '\\' && !escaped) {
+                        escaped = true;
+                        continue;
+                    }
+                    // If we reach the other " then stop
+                    if (c == endQuote && !escaped) break;
+                    // Since parsing of attributes and var
+                    // substitution is done in separate places,
+                    // we need to leave escape in the string
+                    if (c == '$' && escaped) sb.append('\\');
+                    escaped = false;
+                    sb.append(c);
+                }
+                // If we hit the end without seeing a quote
+                // the signal an error
+                if (bIdx == cmd.length()) return null;
+                vals[valIndex++] = sb.toString();
+                sb.delete(0, sb.length()); // clear the buffer
+                inside = !inside;
+            }
+        }
+        return vals;
+    }
+
+
+    /**
+     * Parse a StringBuilder and take out the command token. Called from
+     * <code>requestHandler</code>
+     * 
+     * @param cmd
+     *            a value of type 'StringBuilder'
+     * @return a value of type 'String', or null if there is none
+     */
+    private String parseCmd(StringBuilder cmd) {
+        int firstLetter = -1;
+        int lastLetter = -1;
+        for (int i = 0; i < cmd.length(); i++) {
+            char c = cmd.charAt(i);
+            if (Character.isLetter(c)) {
+                if (firstLetter == -1) {
+                    firstLetter = i;
+                }
+                lastLetter = i;
+            } else if (isSpace(c)) {
+                if (lastLetter > -1) {
+                    break;
+                }
+            } else {
+                break;
+            }
+        }
+        String command = null;
+        if (firstLetter != -1) {
+            command = cmd.substring(firstLetter, lastLetter + 1);
+        }
+        return command;
+    }
+
+
+    protected boolean charCmp(String buf, int index, String command) {
+        return buf.regionMatches(index, command, 0, command.length());
+    }
+
+
+    protected boolean isSpace(char c) {
+        return c == ' ' || c == '\n' || c == '\t' || c == '\r';
+    }
+    
+    protected boolean isQuote(char c) {
+        return c == '\'' || c == '\"' || c == '`';
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServlet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServlet.java
new file mode 100644
index 0000000..79417c0
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServlet.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.apache.catalina.Globals;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Locale;
+/**
+ * Servlet to process SSI requests within a webpage. Mapped to a path from
+ * within web.xml.
+ * 
+ * @author Bip Thelin
+ * @author Amy Roh
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:20 $
+ */
+public class SSIServlet extends HttpServlet {
+    /** Debug level for this servlet. */
+    protected int debug = 0;
+    /** Should the output be buffered. */
+    protected boolean buffered = false;
+    /** Expiration time in seconds for the doc. */
+    protected Long expires = null;
+    /** virtual path can be webapp-relative */
+    protected boolean isVirtualWebappRelative = false;
+    /** Input encoding. If not specified, uses platform default */
+    protected String inputEncoding = null;
+    /** Output encoding. If not specified, uses platform default */
+    protected String outputEncoding = "UTF-8";
+
+
+    //----------------- Public methods.
+    /**
+     * Initialize this servlet.
+     * 
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void init() throws ServletException {
+        
+        if (getServletConfig().getInitParameter("debug") != null)
+            debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));
+        
+        isVirtualWebappRelative = 
+            Boolean.parseBoolean(getServletConfig().getInitParameter("isVirtualWebappRelative"));
+        
+        if (getServletConfig().getInitParameter("expires") != null)
+            expires = Long.valueOf(getServletConfig().getInitParameter("expires"));
+        
+        buffered = Boolean.parseBoolean(getServletConfig().getInitParameter("buffered"));
+        
+        inputEncoding = getServletConfig().getInitParameter("inputEncoding");
+        
+        if (getServletConfig().getInitParameter("outputEncoding") != null)
+            outputEncoding = getServletConfig().getInitParameter("outputEncoding");
+        
+        if (debug > 0)
+            log("SSIServlet.init() SSI invoker started with 'debug'=" + debug);
+
+    }
+
+
+    /**
+     * Process and forward the GET request to our <code>requestHandler()</code>*
+     * 
+     * @param req
+     *            a value of type 'HttpServletRequest'
+     * @param res
+     *            a value of type 'HttpServletResponse'
+     * @exception IOException
+     *                if an error occurs
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void doGet(HttpServletRequest req, HttpServletResponse res)
+            throws IOException, ServletException {
+        if (debug > 0) log("SSIServlet.doGet()");
+        requestHandler(req, res);
+    }
+
+
+    /**
+     * Process and forward the POST request to our
+     * <code>requestHandler()</code>.
+     * 
+     * @param req
+     *            a value of type 'HttpServletRequest'
+     * @param res
+     *            a value of type 'HttpServletResponse'
+     * @exception IOException
+     *                if an error occurs
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void doPost(HttpServletRequest req, HttpServletResponse res)
+            throws IOException, ServletException {
+        if (debug > 0) log("SSIServlet.doPost()");
+        requestHandler(req, res);
+    }
+
+
+    /**
+     * Process our request and locate right SSI command.
+     * 
+     * @param req
+     *            a value of type 'HttpServletRequest'
+     * @param res
+     *            a value of type 'HttpServletResponse'
+     */
+    protected void requestHandler(HttpServletRequest req,
+            HttpServletResponse res) throws IOException, ServletException {
+        ServletContext servletContext = getServletContext();
+        String path = SSIServletRequestUtil.getRelativePath(req);
+        if (debug > 0)
+            log("SSIServlet.requestHandler()\n" + "Serving "
+                    + (buffered?"buffered ":"unbuffered ") + "resource '"
+                    + neutralizeForLog(path) + "'");
+        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
+        // (the "toUpperCase()" avoids problems on Windows systems)
+        if (path == null || path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF")
+                || path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")) {
+            res.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+            log("Can't serve file: " + neutralizeForLog(path));
+            return;
+        }
+        URL resource = servletContext.getResource(path);
+        if (resource == null) {
+            res.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+            log("Can't find file: " + neutralizeForLog(path));
+            return;
+        }
+        String resourceMimeType = servletContext.getMimeType(path);
+        if (resourceMimeType == null) {
+            resourceMimeType = "text/html";
+        }
+        res.setContentType(resourceMimeType + ";charset=" + outputEncoding);
+        if (expires != null) {
+            res.setDateHeader("Expires", (new java.util.Date()).getTime()
+                    + expires.longValue() * 1000);
+        }
+        req.setAttribute(Globals.SSI_FLAG_ATTR, "true");
+        processSSI(req, res, resource);
+    }
+
+
+    protected void processSSI(HttpServletRequest req, HttpServletResponse res,
+            URL resource) throws IOException {
+        SSIExternalResolver ssiExternalResolver =
+            new SSIServletExternalResolver(getServletContext(), req, res,
+                    isVirtualWebappRelative, debug, inputEncoding);
+        SSIProcessor ssiProcessor = new SSIProcessor(ssiExternalResolver,
+                debug);
+        PrintWriter printWriter = null;
+        StringWriter stringWriter = null;
+        if (buffered) {
+            stringWriter = new StringWriter();
+            printWriter = new PrintWriter(stringWriter);
+        } else {
+            printWriter = res.getWriter();
+        }
+
+        URLConnection resourceInfo = resource.openConnection();
+        try (InputStream resourceInputStream = resourceInfo.getInputStream()) {
+            String encoding = resourceInfo.getContentEncoding();
+            if (encoding == null) {
+                encoding = inputEncoding;
+            }
+            InputStreamReader isr;
+            if (encoding == null) {
+                isr = new InputStreamReader(resourceInputStream);
+            } else {
+                isr = new InputStreamReader(resourceInputStream, encoding);
+            }
+            try (BufferedReader bufferedReader = new BufferedReader(isr)) { //this should clode isr as well
+                long lastModified = ssiProcessor.process(bufferedReader,
+                  resourceInfo.getLastModified(), printWriter);
+                if (lastModified > 0) {
+                    res.setDateHeader("last-modified", lastModified);
+                }
+                if (buffered) {
+                    printWriter.flush();
+                    String text = stringWriter.toString();
+                    res.getWriter().write(text);
+                }
+            }
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServletExternalResolver.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServletExternalResolver.java
new file mode 100644
index 0000000..b9e5bda
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServletExternalResolver.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.apache.catalina.util.RequestUtil;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Locale;
+import org.glassfish.grizzly.http.util.Constants;
+import org.glassfish.grizzly.utils.Charsets;
+
+/**
+ * An implementation of SSIExternalResolver that is used with servlets.
+ * 
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:20 $
+ */
+public class SSIServletExternalResolver implements SSIExternalResolver {
+    protected final String VARIABLE_NAMES[] = {"AUTH_TYPE", "CONTENT_LENGTH",
+            "CONTENT_TYPE", "DOCUMENT_NAME", "DOCUMENT_URI",
+            "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING",
+            "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", "HTTP_HOST",
+            "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", "PATH_TRANSLATED",
+            "QUERY_STRING", "QUERY_STRING_UNESCAPED", "REMOTE_ADDR",
+            "REMOTE_HOST", "REMOTE_PORT", "REMOTE_USER", "REQUEST_METHOD",
+            "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_ADDR",
+            "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE",
+            "UNIQUE_ID"};
+    protected ServletContext context;
+    protected HttpServletRequest req;
+    protected HttpServletResponse res;
+    protected boolean isVirtualWebappRelative;
+    protected int debug;
+    protected String inputEncoding;
+
+    public SSIServletExternalResolver(ServletContext context,
+            HttpServletRequest req, HttpServletResponse res,
+            boolean isVirtualWebappRelative, int debug, String inputEncoding) {
+        this.context = context;
+        this.req = req;
+        this.res = res;
+        this.isVirtualWebappRelative = isVirtualWebappRelative;
+        this.debug = debug;
+        this.inputEncoding = inputEncoding;
+    }
+
+
+    public void log(String message, Throwable throwable) {
+        //We can't assume that Servlet.log( message, null )
+        //is the same as Servlet.log( message ), since API
+        //doesn't seem to say so.
+        if (throwable != null) {
+            context.log(message, throwable);
+        } else {
+            context.log(message);
+        }
+    }
+
+
+    public void addVariableNames(Collection<String> variableNames) {
+        for (int i = 0; i < VARIABLE_NAMES.length; i++) {
+            String variableName = VARIABLE_NAMES[i];
+            String variableValue = getVariableValue(variableName);
+            if (variableValue != null) {
+                variableNames.add(variableName);
+            }
+        }
+        Enumeration<String> e = req.getAttributeNames();
+        while (e.hasMoreElements()) {
+            String name = e.nextElement();
+            if (!isNameReserved(name)) {
+                variableNames.add(name);
+            }
+        }
+    }
+
+
+    protected Object getReqAttributeIgnoreCase(String targetName) {
+        Object object = null;
+        if (!isNameReserved(targetName)) {
+            object = req.getAttribute(targetName);
+            if (object == null) {
+                Enumeration<String> e = req.getAttributeNames();
+                while (e.hasMoreElements()) {
+                    String name = e.nextElement();
+                    if (targetName.equalsIgnoreCase(name)
+                            && !isNameReserved(name)) {
+                        object = req.getAttribute(name);
+                        if (object != null) {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return object;
+    }
+
+
+    protected boolean isNameReserved(String name) {
+        return name.startsWith("java.") || name.startsWith("javax.")
+                || name.startsWith("sun.");
+    }
+
+
+    public void setVariableValue(String name, String value) {
+        if (!isNameReserved(name)) {
+            req.setAttribute(name, value);
+        }
+    }
+
+
+    public String getVariableValue(String name) {
+        String retVal = null;
+        Object object = getReqAttributeIgnoreCase(name);
+        if (object != null) {
+            retVal = object.toString();
+        } else {
+            retVal = getCGIVariable(name);
+        }
+        return retVal;
+    }
+
+
+    protected String getCGIVariable(String name) {
+        String retVal = null;
+        String[] nameParts = name.toUpperCase(Locale.ENGLISH).split("_");
+        int requiredParts = 2;
+        if (nameParts.length == 1) {
+            if (nameParts[0].equals("PATH")) {
+                requiredParts = 1;
+                retVal = null; // Not implemented
+            }
+        }
+        else if (nameParts[0].equals("AUTH")) {
+            if (nameParts[1].equals("TYPE")) {
+                retVal = req.getAuthType();
+            }
+        } else if(nameParts[0].equals("CONTENT")) {
+            if (nameParts[1].equals("LENGTH")) {
+                int contentLength = req.getContentLength();
+                if (contentLength >= 0) {
+                    retVal = Integer.toString(contentLength);
+                }
+            } else if (nameParts[1].equals("TYPE")) {
+                retVal = req.getContentType();
+            }
+        } else if (nameParts[0].equals("DOCUMENT")) {
+            if (nameParts[1].equals("NAME")) {
+                String requestURI = req.getRequestURI();
+                retVal = requestURI.substring(requestURI.lastIndexOf('/') + 1);
+            } else if (nameParts[1].equals("URI")) {
+                retVal = req.getRequestURI();
+            }
+        } else if (name.equalsIgnoreCase("GATEWAY_INTERFACE")) {
+            retVal = "CGI/1.1";
+        } else if (nameParts[0].equals("HTTP")) {
+            if (nameParts[1].equals("ACCEPT")) {
+                String accept = null;
+                if (nameParts.length == 2) {
+                    accept = "Accept";
+                } else if (nameParts[2].equals("ENCODING")) {
+                    requiredParts = 3;
+                    accept = "Accept-Encoding";
+                } else if (nameParts[2].equals("LANGUAGE")) {
+                    requiredParts = 3;
+                    accept = "Accept-Language";
+                }
+                if (accept != null) {
+                    Enumeration<String> acceptHeaders = req.getHeaders(accept);
+                    if (acceptHeaders != null)
+                        if (acceptHeaders.hasMoreElements()) {
+                            StringBuilder rv = new StringBuilder(
+                                    acceptHeaders.nextElement());
+                            while (acceptHeaders.hasMoreElements()) {
+                                rv.append(", ");
+                                rv.append(acceptHeaders.nextElement());
+                            }
+                        retVal = rv.toString();
+                    }
+                }
+            }
+            else if (nameParts[1].equals("CONNECTION")) {
+                retVal = req.getHeader("Connection");
+            }
+            else if (nameParts[1].equals("HOST")) {
+                retVal = req.getHeader("Host");
+            }
+            else if (nameParts[1].equals("REFERER")) {
+                retVal = req.getHeader("Referer");
+            }
+            else if (nameParts[1].equals("USER"))
+                if (nameParts.length == 3)
+                    if (nameParts[2].equals("AGENT")) {
+                        requiredParts = 3;
+                        retVal = req.getHeader("User-Agent");
+                    }
+
+        } else if (nameParts[0].equals("PATH")) {
+            if (nameParts[1].equals("INFO")) {
+                retVal = req.getPathInfo();
+            } else if (nameParts[1].equals("TRANSLATED")) {
+                retVal = req.getPathTranslated();
+            }
+        } else if (nameParts[0].equals("QUERY")) {
+            if (nameParts[1].equals("STRING")) {
+                String queryString = req.getQueryString();
+                if (nameParts.length == 2) {
+                    //apache displays this as an empty string rather than (none)
+                    retVal = nullToEmptyString(queryString);
+                } else if (nameParts[2].equals("UNESCAPED")) {
+                    requiredParts = 3;
+                    if (queryString != null) {
+                        // Use default as a last resort
+                        String queryStringEncoding =
+                            org.glassfish.grizzly.http.util.Constants.DEFAULT_HTTP_CHARACTER_ENCODING;
+                
+                        /*String uriEncoding = null;
+                        boolean useBodyEncodingForURI = false;
+                
+                        // Get encoding settings from request / connector if
+                        // possible
+                        String requestEncoding = req.getCharacterEncoding();
+                        if (req instanceof CoyoteRequest) {
+                            uriEncoding =
+                                ((CoyoteRequest)req).getConnector().getURIEncoding();
+                            useBodyEncodingForURI = ((CoyoteRequest)req)
+                                    .getConnector().getUseBodyEncodingForURI();
+                        }
+                
+                        // If valid, apply settings from request / connector
+                        if (uriEncoding != null) {
+                            queryStringEncoding = uriEncoding;
+                        } else if(useBodyEncodingForURI) {
+                            if (requestEncoding != null) {
+                                queryStringEncoding = requestEncoding;
+                            }
+                        }*/
+                
+                        try {
+                            retVal = URLDecoder.decode(queryString,
+                                    queryStringEncoding);                       
+                        } catch (UnsupportedEncodingException e) {
+                            retVal = queryString;
+                        }
+                    }
+                }
+            }
+        } else if(nameParts[0].equals("REMOTE")) {
+            if (nameParts[1].equals("ADDR")) {
+                retVal = req.getRemoteAddr();
+            } else if (nameParts[1].equals("HOST")) {
+                retVal = req.getRemoteHost();
+            } else if (nameParts[1].equals("IDENT")) {
+                retVal = null; // Not implemented
+            } else if (nameParts[1].equals("PORT")) {
+                retVal = Integer.toString( req.getRemotePort());
+            } else if (nameParts[1].equals("USER")) {
+                retVal = req.getRemoteUser();
+            }
+        } else if(nameParts[0].equals("REQUEST")) {
+            if (nameParts[1].equals("METHOD")) {
+                retVal = req.getMethod();
+            }
+            else if (nameParts[1].equals("URI")) {
+                // If this is an error page, get the original URI
+                retVal = (String) req.getAttribute(
+                        "javax.servlet.forward.request_uri");
+                if (retVal == null) retVal=req.getRequestURI();
+            }
+        } else if (nameParts[0].equals("SCRIPT")) {
+            String scriptName = req.getServletPath();
+            if (nameParts[1].equals("FILENAME")) {
+                retVal = context.getRealPath(scriptName);
+            }
+            else if (nameParts[1].equals("NAME")) {
+                retVal = scriptName;
+            }
+        } else if (nameParts[0].equals("SERVER")) {
+            if (nameParts[1].equals("ADDR")) {
+                retVal = req.getLocalAddr();
+            }
+            if (nameParts[1].equals("NAME")) {
+                retVal = req.getServerName();
+            } else if (nameParts[1].equals("PORT")) {
+                retVal = Integer.toString(req.getServerPort());
+            } else if (nameParts[1].equals("PROTOCOL")) {
+                retVal = req.getProtocol();
+            } else if (nameParts[1].equals("SOFTWARE")) {
+                StringBuilder rv = new StringBuilder(context.getServerInfo());
+                rv.append(" ");
+                rv.append(System.getProperty("java.vm.name"));
+                rv.append("/");
+                rv.append(System.getProperty("java.vm.version"));
+                rv.append(" ");
+                rv.append(System.getProperty("os.name"));
+                retVal = rv.toString();
+            }
+        } else if (name.equalsIgnoreCase("UNIQUE_ID")) {
+            retVal = req.getRequestedSessionId();
+        }
+        if (requiredParts != nameParts.length) return null;
+            return retVal;
+    }
+
+    public Date getCurrentDate() {
+        return new Date();
+    }
+
+
+    protected String nullToEmptyString(String string) {
+        String retVal = string;
+        if (retVal == null) {
+            retVal = "";
+        }
+        return retVal;
+    }
+
+
+    protected String getPathWithoutFileName(String servletPath) {
+        String retVal = null;
+        int lastSlash = servletPath.lastIndexOf('/');
+        if (lastSlash >= 0) {
+            //cut off file name
+            retVal = servletPath.substring(0, lastSlash + 1);
+        }
+        return retVal;
+    }
+
+
+    // the caller of the API expect a non-null String
+    protected String getPathWithoutContext(final String contextPath,
+            final String servletPath) {
+        if (servletPath.startsWith(contextPath)) {
+            return servletPath.substring(contextPath.length());
+        }
+        return servletPath;
+    }
+
+
+    protected String getAbsolutePath(String path) throws IOException {
+        String pathWithoutContext = SSIServletRequestUtil.getRelativePath(req);
+        String prefix = getPathWithoutFileName(pathWithoutContext);
+        if (prefix == null) {
+            throw new IOException("Couldn't remove filename from path: "
+                    + pathWithoutContext);
+        }
+        String fullPath = prefix + path;
+        String retVal = RequestUtil.normalize(fullPath);
+        if (retVal == null) {
+            throw new IOException("Normalization yielded null on path: "
+                    + fullPath);
+        }
+        return retVal;
+    }
+
+
+    protected ServletContextAndPath getServletContextAndPathFromNonVirtualPath(
+            String nonVirtualPath) throws IOException {
+        if (nonVirtualPath.startsWith("/") || nonVirtualPath.startsWith("\\")) {
+            throw new IOException("A non-virtual path can't be absolute: "
+                    + nonVirtualPath);
+        }
+        if (nonVirtualPath.indexOf("../") >= 0) {
+            throw new IOException("A non-virtual path can't contain '../' : "
+                    + nonVirtualPath);
+        }
+        String path = getAbsolutePath(nonVirtualPath);
+        ServletContextAndPath csAndP = new ServletContextAndPath(
+                context, path);
+        return csAndP;
+    }
+
+
+    protected ServletContextAndPath getServletContextAndPathFromVirtualPath(
+            String virtualPath) throws IOException {
+
+        if (!virtualPath.startsWith("/") && !virtualPath.startsWith("\\")) {
+            return new ServletContextAndPath(context,
+                    getAbsolutePath(virtualPath));
+        } else {
+            String normalized = RequestUtil.normalize(virtualPath);
+            if (isVirtualWebappRelative) {
+                return new ServletContextAndPath(context, normalized);
+            } else {
+                ServletContext normContext = context.getContext(normalized);
+                if (normContext == null) {
+                    throw new IOException("Couldn't get context for path: "
+                            + normalized);
+                }
+                //If it's the root context, then there is no context element
+                // to remove,
+                // ie:
+                // '/file1.shtml' vs '/appName1/file1.shtml'
+                if (!isRootContext(normContext)) {
+                    String noContext = getPathWithoutContext(
+                            normContext.getContextPath(), normalized);
+                    return new ServletContextAndPath(normContext, noContext);
+                } else {
+                    return new ServletContextAndPath(normContext, normalized);
+                }
+            }
+        }
+    }
+
+
+    //Assumes servletContext is not-null
+    //Assumes that identity comparison will be true for the same context
+    //Assuming the above, getContext("/") will be non-null as long as the root
+    // context is
+    // accessible.
+    //If it isn't, then servletContext can't be the root context anyway, hence
+    // they will
+    // not match.
+    protected boolean isRootContext(ServletContext servletContext) {
+        return servletContext == servletContext.getContext("/");
+    }
+
+
+    protected ServletContextAndPath getServletContextAndPath(
+            String originalPath, boolean virtual) throws IOException {
+        ServletContextAndPath csAndP = null;
+        if (debug > 0) {
+            log("SSIServletExternalResolver.getServletContextAndPath( "
+                    + originalPath + ", " + virtual + ")", null);
+        }
+        if (virtual) {
+            csAndP = getServletContextAndPathFromVirtualPath(originalPath);
+        } else {
+            csAndP = getServletContextAndPathFromNonVirtualPath(originalPath);
+        }
+        return csAndP;
+    }
+
+
+    protected URLConnection getURLConnection(String originalPath,
+            boolean virtual) throws IOException {
+        ServletContextAndPath csAndP = getServletContextAndPath(originalPath,
+                virtual);
+        ServletContext context = csAndP.getServletContext();
+        String path = csAndP.getPath();
+        URL url = context.getResource(path);
+        if (url == null) {
+            throw new IOException("Context did not contain resource: " + path);
+        }
+        URLConnection urlConnection = url.openConnection();
+        return urlConnection;
+    }
+
+
+    public long getFileLastModified(String path, boolean virtual)
+            throws IOException {
+        long lastModified = 0;
+        try {
+            URLConnection urlConnection = getURLConnection(path, virtual);
+            lastModified = urlConnection.getLastModified();
+        } catch (IOException e) {
+            // Ignore this. It will always fail for non-file based includes
+        }
+        return lastModified;
+    }
+
+
+    public long getFileSize(String path, boolean virtual) throws IOException {
+        long fileSize = -1;
+        try {
+            URLConnection urlConnection = getURLConnection(path, virtual);
+            fileSize = urlConnection.getContentLength();
+        } catch (IOException e) {
+            // Ignore this. It will always fail for non-file based includes
+        }
+        return fileSize;
+    }
+
+
+    //We are making lots of unnecessary copies of the included data here. If
+    //someone ever complains that this is slow, we should connect the included
+    // stream to the print writer that SSICommand uses.
+    public String getFileText(String originalPath, boolean virtual)
+            throws IOException {
+        try {
+            ServletContextAndPath csAndP = getServletContextAndPath(
+                    originalPath, virtual);
+            ServletContext context = csAndP.getServletContext();
+            String path = csAndP.getPath();
+            RequestDispatcher rd = context.getRequestDispatcher(path);
+            if (rd == null) {
+                throw new IOException(
+                        "Couldn't get request dispatcher for path: " + path);
+            }
+            ByteArrayServletOutputStream basos =
+                new ByteArrayServletOutputStream();
+            ResponseIncludeWrapper responseIncludeWrapper =
+                new ResponseIncludeWrapper(context, req, res, basos);
+            rd.include(req, responseIncludeWrapper);
+            //We can't assume the included servlet flushed its output
+            responseIncludeWrapper.flushOutputStreamOrWriter();
+            byte[] bytes = basos.toByteArray();
+
+            //Assume platform default encoding unless otherwise specified
+            String retVal;
+            if (inputEncoding == null) {
+                retVal = new String(bytes, Charset.defaultCharset());
+            } else {
+                retVal = new String (bytes, Charsets.lookupCharset(inputEncoding));
+            }
+
+            //make an assumption that an empty response is a failure. This is
+            // a problem
+            // if a truly empty file
+            //were included, but not sure how else to tell.
+            if (retVal.equals("") && !req.getMethod().equalsIgnoreCase(
+                    "HEAD")) {
+                throw new IOException("Couldn't find file: " + path);
+            }
+            return retVal;
+        } catch (ServletException e) {
+            throw new IOException("Couldn't include file: " + originalPath
+                    + " because of ServletException: " + e.getMessage());
+        }
+    }
+
+    protected static class ServletContextAndPath {
+        protected ServletContext servletContext;
+        protected String path;
+
+
+        public ServletContextAndPath(ServletContext servletContext,
+                                     String path) {
+            this.servletContext = servletContext;
+            this.path = path;
+        }
+
+
+        public ServletContext getServletContext() {
+            return servletContext;
+        }
+
+
+        public String getPath() {
+            return path;
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServletRequestUtil.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServletRequestUtil.java
new file mode 100644
index 0000000..a4601c2
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIServletRequestUtil.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+import org.apache.catalina.util.RequestUtil;
+
+import javax.servlet.http.HttpServletRequest;
+public class SSIServletRequestUtil {
+    /**
+     * Return the relative path associated with this servlet. Taken from
+     * DefaultServlet.java. Perhaps this should be put in
+     * org.apache.catalina.util somewhere? Seems like it would be widely used.
+     * 
+     * @param request
+     *            The servlet request we are processing
+     */
+    public static String getRelativePath(HttpServletRequest request) {
+        // Are we being processed by a RequestDispatcher.include()?
+        if (request.getAttribute("javax.servlet.include.request_uri") != null) {
+            String result = (String)request
+                    .getAttribute("javax.servlet.include.path_info");
+            if (result == null)
+                result = (String)request
+                        .getAttribute("javax.servlet.include.servlet_path");
+            if ((result == null) || (result.equals(""))) result = "/";
+            return (result);
+        }
+        // No, extract the desired path directly from the request
+        String result = request.getPathInfo();
+        if (result == null) {
+            result = request.getServletPath();
+        }
+        if ((result == null) || (result.equals(""))) {
+            result = "/";
+        }
+        return RequestUtil.normalize(result);
+    }
+
+
+    /**
+     * 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 normalize should be
+     * the same as DefaultServlet.normalize, which is almost the same ( see
+     * source code below ) as RequestUtil.normalize. Do we need all this
+     * duplication?
+     * 
+     * @param path
+     *            Path to be normalized
+     * @deprecated
+     */
+    public static String normalize(String path) {
+        return RequestUtil.normalize(path);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSISet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSISet.java
new file mode 100644
index 0000000..40457d9
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSISet.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import java.io.PrintWriter;
+/**
+ * Implements the Server-side #set command
+ * 
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @author David Becker
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:20 $
+ */
+public class SSISet implements SSICommand {
+    /**
+     * @see SSICommand
+     */
+    public long process(SSIMediator ssiMediator, String commandName,
+            String[] paramNames, String[] paramValues, PrintWriter writer)
+            throws SSIStopProcessingException {
+        long lastModified = 0;
+        String variableName = null;
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            String paramValue = paramValues[i];
+            if (paramName.equalsIgnoreCase("var")) {
+                variableName = paramValue;
+            } else if (paramName.equalsIgnoreCase("value")) {
+                if (variableName != null) {
+                    String substitutedValue = ssiMediator
+                            .substituteVariables(paramValue);
+                    ssiMediator.setVariableValue(variableName,
+                            substitutedValue);
+                    lastModified = System.currentTimeMillis();
+                } else {
+                    ssiMediator.log("#set--no variable specified");
+                    String errorMessage = getEncodedConfigErrorMessage(ssiMediator);
+                    writer.write(errorMessage);
+                    throw new SSIStopProcessingException();
+                }
+            } else {
+                ssiMediator.log("#set--Invalid attribute: " + paramName);
+                String errorMessage = getEncodedConfigErrorMessage(ssiMediator);
+                writer.write(errorMessage);
+                throw new SSIStopProcessingException();
+            }
+        }
+        return lastModified;
+    }
+
+    private String getEncodedConfigErrorMessage(SSIMediator ssiMediator) {
+        String errorMessage = ssiMediator.getConfigErrMsg();
+        return HtmlEntityEncoder.encodeXSS(errorMessage);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIStopProcessingException.java b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIStopProcessingException.java
new file mode 100644
index 0000000..1dde6db
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/SSIStopProcessingException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.ssi;
+
+
+/**
+ * Exception used to tell SSIProcessor that it should stop processing SSI
+ * commands. This is used to mimic the Apache behavior in #set with invalid
+ * attributes.
+ * 
+ * @author Paul Speed
+ * @author Dan Sandberg
+ * @version $Revision: 1.4 $, $Date: 2007/05/05 05:32:20 $
+ */
+public class SSIStopProcessingException extends Exception {
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/package.html b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/package.html
new file mode 100644
index 0000000..d9fc540
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/ssi/package.html
@@ -0,0 +1,35 @@
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<body>
+<p>This package contains code that is used by the SsiInvoker.</p>
+<p>This class consists of <code>SsiMediator.java</code> which works as a
+mediator between the different SsiCommands. To add a command you have to
+implement the <code>SsiCommand</code> interface and extend the
+<code>SsiMediator</code>. Commands currently implemented are</p>
+
+<ul>
+<li><b>SsiConfig</b> - Implementation of the NCSA command Config i.e. &lt;!--#config errmsg="error?"--&gt;</li>
+<li><b>SsiEcho</b> - Implementation of the NCSA command Echo i.e. &lt;!--#echo var="SERVER_NAME"--&gt;</li>
+<li><b>SsiExec</b> - Not implemented</li>
+<li><b>SsiFlastMod</b> - Implementation of the NCSA command flastmod i.e. &lt;!--#flastmod virtual="file"--&gt;</li>
+<li><b>SsiFsize</b> - Implementation of the NCSA command fsize i.e. &lt;!--#fsize file="file"--&gt;</li>
+<li><b>SsiInclude</b> - Implementation of the NCSA command Include i.e. &lt;!--#config virtual="includefile"--&gt;</li>
+</ul>
+</body>
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Bootstrap.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Bootstrap.java
new file mode 100644
index 0000000..7b013fd
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Bootstrap.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.security.SecurityClassLoad;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Boostrap loader for Catalina.  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.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.5 $ $Date: 2006/10/03 20:19:13 $
+ */
+
+public final class Bootstrap {
+
+
+    // ------------------------------------------------------------ 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 Bootstrap daemon = null;
+     */
+
+    private static final Logger log = LogFacade.getLogger();
+
+    // ----------------------------------------------------------- Variables
+
+    /**
+     * Debugging detail level for processing the startup.
+     */
+    private int debug = 0;
+
+
+    /**
+     * Daemon reference.
+     */
+    private Object catalinaDaemon = null;
+
+
+    private ClassLoader commonLoader = null;
+    private ClassLoader catalinaLoader = null;
+    private ClassLoader sharedLoader = null;
+
+
+    // ------------------------------------------------------ Private Methods
+
+    private void initClassLoaders() {
+        try {
+            ClassLoaderFactory.setDebug(debug);
+            commonLoader = createClassLoader("common", null);
+            if( commonLoader == null ) {
+                // no config file, default to this loader 
+                // - we might be in a 'single' env.
+                commonLoader=this.getClass().getClassLoader();
+            }
+
+            catalinaLoader = createClassLoader("server", commonLoader);
+            sharedLoader = createClassLoader("shared", commonLoader);
+        } catch (Throwable t) {
+            log.log(Level.SEVERE, LogFacade.CLASS_LOADER_CREATION_EXCEPTION, t);
+            System.exit(1);
+        }
+    }
+
+
+    private ClassLoader createClassLoader(String name, ClassLoader parent)
+        throws Exception {
+
+        String value = CatalinaProperties.getProperty(name + ".loader");
+        if ((value == null) || (value.equals("")))
+            return parent;
+
+        ArrayList<File> unpackedList = new ArrayList<File>();
+        ArrayList<File> packedList = new ArrayList<File>();
+        ArrayList<URL> urlList = new ArrayList<URL>();
+
+        StringTokenizer tokenizer = new StringTokenizer(value, ",");
+        while (tokenizer.hasMoreElements()) {
+            String repository = tokenizer.nextToken();
+            // Check for a JAR URL repository
+            try {
+                urlList.add(new URL(repository));
+                continue;
+            } catch (MalformedURLException e) {
+                // Ignore
+            }
+            // 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());
+            }
+            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 = unpackedList.toArray(new File[unpackedList.size()]);
+        File[] packed = packedList.toArray(new File[packedList.size()]);
+        URL[] urls = urlList.toArray(new URL[urlList.size()]);
+
+        return ClassLoaderFactory.createClassLoader
+            (unpacked, packed, urls, parent);
+
+    }
+
+
+    /**
+     * Initialize daemon.
+     */
+    public void init()
+        throws Exception
+    {
+
+        // Set Catalina path
+        setCatalinaHome();
+        setCatalinaBase();
+
+        initClassLoaders();
+
+        Thread.currentThread().setContextClassLoader(catalinaLoader);
+
+        SecurityClassLoad.securityClassLoad(catalinaLoader);
+
+        // Load our startup class and call its process() method
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Loading startup class");
+        Class startupClass =
+            catalinaLoader.loadClass
+            ("org.apache.catalina.startup.Catalina");
+        Object startupInstance = startupClass.newInstance();
+
+        // Set the shared extensions class loader
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Setting startup class properties");
+        String methodName = "setParentClassLoader";
+        Class paramTypes[] = new Class[1];
+        paramTypes[0] = Class.forName("java.lang.ClassLoader");
+        Object paramValues[] = new Object[1];
+        paramValues[0] = sharedLoader;
+        Method method =
+            startupInstance.getClass().getMethod(methodName, paramTypes);
+        method.invoke(startupInstance, paramValues);
+
+        catalinaDaemon = startupInstance;
+
+    }
+
+
+    /**
+     * Load daemon.
+     */
+    private void load(String[] arguments)
+        throws Exception {
+
+        // Call the load() method
+        String methodName = "load";
+        Object param[];
+        Class paramTypes[];
+        if (arguments==null || arguments.length==0) {
+            paramTypes = null;
+            param = null;
+        } else {
+            paramTypes = new Class[1];
+            paramTypes[0] = arguments.getClass();
+            param = new Object[1];
+            param[0] = arguments;
+        }
+        Method method = 
+            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Calling startup class " + method);
+        method.invoke(catalinaDaemon, param);
+
+    }
+
+
+    // ----------------------------------------------------------- Main Program
+
+
+    /**
+     * Load the Catalina daemon.
+     */
+    public void init(String[] arguments)
+        throws Exception {
+
+        // Read the arguments
+        if (arguments != null) {
+            for (int i = 0; i < arguments.length; i++) {
+                if (arguments[i].equals("-debug")) {
+                    debug = 1;
+                }
+            }
+        }
+
+        init();
+        load(arguments);
+
+    }
+
+
+    /**
+     * Start the Catalina daemon.
+     */
+    public void start()
+        throws Exception {
+        if( catalinaDaemon==null ) init();
+
+        Method method = catalinaDaemon.getClass().getMethod("start",(Class[])null);
+        method.invoke(catalinaDaemon, (Object[])null);
+
+    }
+
+
+    /**
+     * Stop the Catalina Daemon.
+     */
+    public void stop()
+        throws Exception {
+
+        Method method = catalinaDaemon.getClass().getMethod("stop",(Class[]) null);
+        method.invoke(catalinaDaemon,(Object[]) null);
+
+    }
+
+
+    /**
+     * Stop the standlone server.
+     */
+    public void stopServer()
+        throws Exception {
+
+        Method method = 
+            catalinaDaemon.getClass().getMethod("stopServer",(Class[])null);
+        method.invoke(catalinaDaemon,(Object[])null);
+
+    }
+
+    /**
+     * Set flag.
+     */
+    public void setAwait(boolean await)
+        throws Exception {
+
+        Class paramTypes[] = new Class[1];
+        paramTypes[0] = Boolean.TYPE;
+        Object paramValues[] = new Object[1];
+        paramValues[0] = Boolean.valueOf(await);
+        Method method = 
+            catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
+        method.invoke(catalinaDaemon, paramValues);
+
+    }
+
+    public boolean getAwait()
+        throws Exception
+    {
+        Class paramTypes[] = new Class[0];
+        Object paramValues[] = new Object[0];
+        Method method =
+            catalinaDaemon.getClass().getMethod("getAwait", paramTypes);
+        Boolean b=(Boolean)method.invoke(catalinaDaemon, paramValues);
+        return b.booleanValue();
+    }
+
+    /**
+     * Destroy the Catalina Daemon.
+     */
+    public void destroy() {
+
+        // FIXME
+
+    }
+
+    public void setCatalinaHome(String s) {
+        System.setProperty( "catalina.home", s );
+    }
+
+    public void setCatalinaBase(String s) {
+        System.setProperty( "catalina.base", s );
+    }
+
+    /**
+     * Set the <code>catalina.base</code> System property to the current
+     * working directory if it has not been set.
+     */
+    private void setCatalinaBase() {
+
+        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.
+     */
+    private void setCatalinaHome() {
+
+        if (System.getProperty("catalina.home") != null)
+            return;
+        File bootstrapJar = 
+            new File(System.getProperty("user.dir"), "bootstrap.jar");
+        if (bootstrapJar.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 value of the catalina.home environment variable.
+     */
+    public static String getCatalinaHome() {
+        return System.getProperty("catalina.home",
+                                  System.getProperty("user.dir"));
+    }
+
+
+    /**
+     * Get the value of the catalina.base environment variable.
+     */
+    public static String getCatalinaBase() {
+        return System.getProperty("catalina.base", getCatalinaHome());
+    }
+
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Catalina.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Catalina.java
new file mode 100644
index 0000000..61e7257
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Catalina.java
@@ -0,0 +1,699 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Server;
+import org.apache.catalina.core.StandardServer;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+
+import java.io.*;
+import java.net.Socket;
+import java.text.MessageFormat;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Startup/Shutdown shell program for Catalina.  The following command line
+ * options are recognized:
+ * <ul>
+ * <li><b>-config {pathname}</b> - Set the pathname of the configuration file
+ *     to be processed.  If a relative path is specified, it will be
+ *     interpreted as relative to the directory pathname specified by the
+ *     "catalina.base" system property.   [conf/server.xml]
+ * <li><b>-help</b> - Display usage information.
+ * <li><b>-stop</b> - Stop the currently running instance of Catalina.
+ * </u>
+ *
+ * Should do the same thing as Embedded, but using a server.xml file.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.5 $ $Date: 2006/11/10 18:12:35 $
+ */
+
+public class Catalina extends Embedded {
+
+    private static final ClassLoader standardServerClassLoader =
+        StandardServer.class.getClassLoader();
+
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * Pathname to the server configuration file.
+     */
+    protected String configFile = "conf/server.xml";
+
+    // XXX Should be moved to embedded
+    /**
+     * The shared extensions class loader for this server.
+     */
+    protected ClassLoader parentClassLoader =
+        Catalina.class.getClassLoader();
+
+
+    /**
+     * The server component we are starting or stopping
+     */
+    protected Server server = null;
+
+
+    /**
+     * Are we starting a new server?
+     */
+    protected boolean starting = false;
+
+
+    /**
+     * Are we stopping an existing server?
+     */
+    protected boolean stopping = false;
+
+    /**
+     * Shutdown hook.
+     */
+    protected Thread shutdownHook = new CatalinaShutdownHook();
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    public void setConfig(String file) {
+        configFile = file;
+    }
+
+
+    public void setConfigFile(String file) {
+        configFile = file;
+    }
+
+
+    public String getConfigFile() {
+        return configFile;
+    }
+
+
+    /**
+     * Set the shared extensions class loader.
+     *
+     * @param parentClassLoader The shared extensions class loader.
+     */
+    public void setParentClassLoader(ClassLoader parentClassLoader) {
+        this.parentClassLoader = parentClassLoader;
+    }
+
+
+    /**
+     * Set the server instance we are configuring.
+     *
+     * @param server The new server
+     */
+    public void setServer(Server server) {
+        this.server = server;
+    }
+
+    // --------------------------------------------------------- Main Program
+
+    /**
+     * The application main program.
+     *
+     * @param args Command line arguments
+     */
+    public static void main(String args[]) {
+        (new Catalina()).process(args);
+    }
+
+
+    /**
+     * The instance main program.
+     *
+     * @param args Command line arguments
+     */
+    public void process(String args[]) {
+        setAwait(true);
+        setCatalinaHome();
+        setCatalinaBase();
+        try {
+            if (arguments(args)) {
+                if (starting) {
+                    load(args);
+                    start();
+                } else if (stopping) {
+                    stopServer();
+                }
+            }
+        } catch (Exception e) {
+            log.log(Level.WARNING, LogFacade.ERROR_PROCESSING_COMMAND_LINE_EXCEPTION, e);
+        }
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Process the specified command line arguments, and return
+     * <code>true</code> if we should continue processing; otherwise
+     * return <code>false</code>.
+     *
+     * @param args Command line arguments to process
+     */
+    protected boolean arguments(String args[]) {
+
+        boolean isConfig = false;
+
+        if (args.length < 1) {
+            usage();
+            return (false);
+        }
+
+        for (int i = 0; i < args.length; i++) {
+            if (isConfig) {
+                configFile = args[i];
+                isConfig = false;
+            } else if (args[i].equals("-config")) {
+                isConfig = true;
+            } else if (args[i].equals("-debug")) {
+                debug = 1;
+            } else if (args[i].equals("-nonaming")) {
+                setUseNaming( false );
+            } else if (args[i].equals("-help")) {
+                usage();
+                return (false);
+            } else if (args[i].equals("start")) {
+                starting = true;
+                stopping = false;
+            } else if (args[i].equals("stop")) {
+                starting = false;
+                stopping = true;
+            } else {
+                usage();
+                return (false);
+            }
+        }
+
+        return (true);
+
+    }
+
+
+    /**
+     * Return a File object representing our configuration file.
+     */
+    protected File configFile() {
+
+        File file = new File(configFile);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"), configFile);
+        return (file);
+
+    }
+
+
+    /**
+     * Create and configure the Digester we will be using for startup.
+     */
+    protected Digester createStartDigester() {
+        long t1=System.currentTimeMillis();
+        // Initialize the digester
+        Digester digester = new Digester();
+        if (debug>0)
+            digester.setDebug(debug);
+        digester.setValidating(false);
+        digester.setClassLoader(standardServerClassLoader);
+
+        // Configure the actions we will be using
+        digester.addObjectCreate("Server",
+                                 "org.apache.catalina.core.StandardServer",
+                                 "className");
+        digester.addSetProperties("Server");
+        digester.addSetNext("Server",
+                            "setServer",
+                            "org.apache.catalina.Server");
+
+        digester.addObjectCreate("Server/GlobalNamingResources",
+                                 "org.apache.catalina.deploy.NamingResources");
+        digester.addSetProperties("Server/GlobalNamingResources");
+        digester.addSetNext("Server/GlobalNamingResources",
+                            "setGlobalNamingResources",
+                            "org.apache.catalina.deploy.NamingResources");
+
+        digester.addObjectCreate("Server/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties("Server/Listener");
+        digester.addSetNext("Server/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addObjectCreate("Server/Service",
+                                 "org.apache.catalina.core.StandardService",
+                                 "className");
+        digester.addSetProperties("Server/Service");
+        digester.addSetNext("Server/Service",
+                            "addService",
+                            "org.apache.catalina.Service");
+
+        digester.addObjectCreate("Server/Service/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties("Server/Service/Listener");
+        digester.addSetNext("Server/Service/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addObjectCreate("Server/Service/Connector",
+                                 "org.apache.catalina.connector.CoyoteConnector",
+                                 "className");
+        digester.addRule("Server/Service/Connector", 
+                         new SetAllPropertiesRule());
+        digester.addSetNext("Server/Service/Connector",
+                            "addConnector",
+                            "org.apache.catalina.Connector");
+
+        digester.addObjectCreate("Server/Service/Connector/Factory",
+                                 "org.apache.catalina.connector.CoyoteServerSocketFactory",
+                                 "className");
+        digester.addSetProperties("Server/Service/Connector/Factory");
+        digester.addSetNext("Server/Service/Connector/Factory",
+                            "setFactory",
+                            "org.apache.catalina.net.ServerSocketFactory");
+
+        digester.addObjectCreate("Server/Service/Connector/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties("Server/Service/Connector/Listener");
+        digester.addSetNext("Server/Service/Connector/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        // Add RuleSets for nested elements
+        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
+        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
+        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
+        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));
+        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));
+        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));
+        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));
+        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
+        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
+
+        // When the 'engine' is found, set the parentClassLoader.
+        digester.addRule("Server/Service/Engine",
+                         new SetParentClassLoaderRule(digester,
+                                                      parentClassLoader));
+
+        long t2=System.currentTimeMillis();
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Digester for server.xml created " + ( t2-t1 ));
+        return (digester);
+
+    }
+
+
+    /**
+     * Create and configure the Digester we will be using for shutdown.
+     */
+    protected Digester createStopDigester() {
+
+        // Initialize the digester
+        Digester digester = new Digester();
+        if (debug>0)
+            digester.setDebug(debug);
+
+        // Configure the rules we need for shutting down
+        digester.addObjectCreate("Server",
+                                 "org.apache.catalina.core.StandardServer",
+                                 "className");
+        digester.addSetProperties("Server");
+        digester.addSetNext("Server",
+                            "setServer",
+                            "org.apache.catalina.Server");
+
+        return (digester);
+
+    }
+
+
+    public void stopServer() {
+
+        if( server == null ) {
+            // Create and execute our Digester
+            Digester digester = createStopDigester();
+            digester.setClassLoader(Thread.currentThread().getContextClassLoader());
+            File file = configFile();
+            FileInputStream fis = null;
+            try {
+                InputSource is =
+                    new InputSource("file://" + file.getAbsolutePath());
+                fis = new FileInputStream(file);
+                is.setByteStream(fis);
+                digester.push(this);
+                digester.parse(is);
+            } catch (Exception e) {
+                log.log(Level.SEVERE, LogFacade.CATALINA_STOP_EXCEPTION, e);
+                try {
+                    if (fis != null) {
+                        fis.close();
+                    }
+                } catch (IOException ioe) {}
+                System.exit(1);
+            } finally {
+                try {
+                    if (fis != null) {
+                        fis.close();
+                    }
+                } catch (IOException ioe) {}
+            }
+        }
+
+        // Stop the existing server
+        Socket socket = null;
+        OutputStream stream = null;
+        try {
+            socket = new Socket("127.0.0.1", server.getPort());
+            stream = socket.getOutputStream();
+            String shutdown = server.getShutdown();
+            for (int i = 0; i < shutdown.length(); i++)
+                stream.write(shutdown.charAt(i));
+            stream.flush();
+        } catch (IOException e) {
+            log.log(Level.SEVERE, LogFacade.CATALINA_STOP_EXCEPTION, e);
+            System.exit(1);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+            if (socket != null) {
+                try {
+                    socket.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Set the <code>catalina.base</code> System property to the current
+     * working directory if it has not been set.
+     * @deprecated Use initDirs()
+     */
+    public void setCatalinaBase() {
+        initDirs();
+    }
+
+    /**
+     * Set the <code>catalina.home</code> System property to the current
+     * working directory if it has not been set.
+     * @deprecated Use initDirs()
+     */
+    public void setCatalinaHome() {
+        initDirs();
+    }
+
+    /**
+     * Start a new server instance.
+     */
+    public void load() {
+        initDirs();
+
+        // Before digester - it may be needed
+
+        initNaming();
+
+        // Create and execute our Digester
+        Digester digester = createStartDigester();
+        long t1 = System.currentTimeMillis();
+
+        Exception ex = null;
+        InputSource inputSource = null;
+        InputStream inputStream = null;
+        File file = null;
+        try {
+            file = configFile();
+            inputStream = new FileInputStream(file);
+            inputSource = new InputSource("file://" + file.getAbsolutePath());
+        } catch (Exception e) {
+            ;
+        }
+        if (inputStream == null) {
+            try {
+                inputStream = getClass().getClassLoader()
+                    .getResourceAsStream(getConfigFile());
+                inputSource = new InputSource
+                    (getClass().getClassLoader()
+                     .getResource(getConfigFile()).toString());
+            } catch (Exception e) {
+                ;
+            }
+        }
+
+        // This should be included in catalina.jar
+        // Alternative: don't bother with xml, just create it manually.
+        if( inputStream==null ) {
+            try {
+                inputStream = getClass().getClassLoader()
+                .getResourceAsStream("server-embed.xml");
+                inputSource = new InputSource
+                (getClass().getClassLoader()
+                        .getResource("server-embed.xml").toString());
+            } catch (Exception e) {
+                ;
+            }
+        }
+
+        if (inputStream == null && file != null) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_LOAD_SERVER_XML_EXCEPTION),
+                                              file.getAbsolutePath());
+            log.log(Level.WARNING, msg);
+            return;
+        }
+
+        if (inputStream != null) {
+            try {
+                inputSource.setByteStream(inputStream);
+                digester.push(this);
+                digester.parse(inputSource);
+            } catch (Exception e) {
+                log.log(Level.WARNING, LogFacade.CATALINA_START_WARNING_EXCEPTION, e);
+                return;
+            } finally {
+                try {
+                    inputStream.close();
+                } catch (IOException ioe) {}
+            }
+        }
+
+        // Start the new server
+        if (server instanceof Lifecycle) {
+            try {
+                server.initialize();
+            } catch (LifecycleException e) {
+                log.log(Level.SEVERE, LogFacade.CATALINA_START_SEVERE_EXCEPTION, e);
+            }
+        }
+
+        if (log.isLoggable(Level.INFO)) {
+            long t2 = System.currentTimeMillis();
+            String msg = MessageFormat.format(rb.getString(LogFacade.INIT_PROCESSED_EXCEPTION), (t2-t1));
+            log.log(Level.INFO, msg);
+        }
+
+    }
+
+
+    /* 
+     * Load using arguments
+     */
+    public void load(String args[]) {
+        setCatalinaHome();
+        setCatalinaBase();
+        try {
+            if (arguments(args))
+                load();
+        } catch (Exception e) {
+            log.log(Level.WARNING, LogFacade.ERROR_LOADING_CONFIGURATION_EXCEPTION, e);
+        }
+    }
+
+    public void create() {
+
+    }
+
+    public void destroy() {
+
+    }
+
+    /**
+     * Start a new server instance.
+     */
+    public void start() {
+
+        if (server == null) {
+            load();
+        }
+
+        long t1 = System.currentTimeMillis();
+
+        // Start the new server
+        if (server instanceof Lifecycle) {
+            try {
+                ((Lifecycle) server).start();
+            } catch (LifecycleException e) {
+                log.log(Level.SEVERE, LogFacade.CATALINA_START_SEVERE_EXCEPTION, e);
+            }
+        }
+
+        if (log.isLoggable(Level.INFO)) {
+            long t2 = System.currentTimeMillis();
+            String msg = MessageFormat.format(rb.getString(LogFacade.SERVER_STARTUP_INFO),
+                                              (t2 - t1));
+            log.log(Level.INFO, msg);
+        }
+
+        try {
+            // Register shutdown hook
+            Runtime.getRuntime().addShutdownHook(shutdownHook);
+        } catch (Throwable t) {
+            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
+            // fine without the shutdown hook.
+        }
+
+        if (await) {
+            await();
+            stop();
+        }
+
+    }
+
+
+    /**
+     * Stop an existing server instance.
+     */
+    public void stop() {
+
+        try {
+            // Remove the ShutdownHook first so that server.stop() 
+            // doesn't get invoked twice
+            Runtime.getRuntime().removeShutdownHook(shutdownHook);
+        } catch (Throwable t) {
+            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
+            // fine without the shutdown hook.
+        }
+
+        // Shut down the server
+        if (server instanceof Lifecycle) {
+            try {
+                ((Lifecycle) server).stop();
+            } catch (LifecycleException e) {
+                log.log(Level.SEVERE, LogFacade.CATALINA_STOP_EXCEPTION, e);
+            }
+        }
+
+    }
+
+
+    /**
+     * Await and shutdown.
+     */
+    public void await() {
+
+        server.await();
+
+    }
+
+
+    /**
+     * Print usage information for this application.
+     */
+    protected void usage() {
+
+        System.out.println
+            ("usage: java org.apache.catalina.startup.Catalina"
+             + " [ -config {pathname} ] [ -debug ]"
+             + " [ -nonaming ] { start | stop }");
+
+    }
+
+
+    // --------------------------------------- CatalinaShutdownHook Inner Class
+
+    // XXX Should be moved to embedded !
+    /**
+     * Shutdown hook which will perform a clean shutdown of Catalina if needed.
+     */
+    protected class CatalinaShutdownHook extends Thread {
+
+        public void run() {
+
+            if (server != null) {
+                Catalina.this.stop();
+            }
+            
+        }
+
+    }
+}
+
+
+// ------------------------------------------------------------ Private Classes
+
+
+/**
+ * Rule that sets the parent class loader for the top object on the stack,
+ * which must be a <code>Container</code>.
+ */
+
+final class SetParentClassLoaderRule extends Rule {
+
+    public SetParentClassLoaderRule(Digester digester,
+                                    ClassLoader parentClassLoader) {
+
+        super(digester);
+        this.parentClassLoader = parentClassLoader;
+
+    }
+
+    ClassLoader parentClassLoader = null;
+
+    public void begin(Attributes attributes) throws Exception {
+
+        if (digester.getDebug() >= 1)
+            digester.log("Setting parent class loader");
+
+        Container top = (Container) digester.peek();
+        top.setParentClassLoader(parentClassLoader);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/CatalinaProperties.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/CatalinaProperties.java
new file mode 100644
index 0000000..011293c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/CatalinaProperties.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.LogFacade;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utility class to read the bootstrap Catalina configuration.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.4 $ $Date: 2006/03/12 01:27:06 $
+ */
+
+public class CatalinaProperties {
+
+
+    // ----------------------------------------------------- Static Variables
+
+    private static final Logger log = LogFacade.getLogger();
+
+    private static Properties properties = null;
+
+    static {
+        loadProperties();
+    }
+
+    // ------------------------------------------------------- Public Methods
+
+    /**
+     * Return specified property value.
+     */
+    public static String getProperty(String name) {
+        return properties.getProperty(name);
+    }
+
+
+    /**
+     * Return specified property value.
+     */
+    public static String getProperty(String name, String defaultValue) {
+
+        return properties.getProperty(name, defaultValue);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Load properties.
+     */
+    private static void loadProperties() {
+
+        InputStream is = null;
+        Throwable error = null;
+
+        try {
+            String configUrl = getConfigUrl();
+            if (configUrl != null) {
+                is = (new URL(configUrl)).openStream();
+            }
+        } catch (Throwable t) {
+            // Ignore
+        }
+
+        if (is == null) {
+            try {
+                File home = new File(getCatalinaHome());
+                File conf = new File(home, "conf");
+                File properties = new File(conf, "catalina.properties");
+                is = new FileInputStream(properties);
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if (is == null) {
+            try {
+                is = CatalinaProperties.class.getResourceAsStream
+                    ("/org/apache/catalina/startup/catalina.properties");
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if (is != null) {
+            try {
+                properties = new Properties();
+                properties.load(is);
+            } catch (Throwable t) {
+                error = t;
+            } finally {
+                try {
+                    is.close();
+                } catch(Throwable t2) {
+                }
+            }
+        }
+
+        if ((is == null) || (error != null)) {
+            // Do something
+            log.log(Level.WARNING, LogFacade.FAILED_LOAD_CATALINA_PROPERTIES_EXCEPTION,
+                    error);
+            // That's fine - we have reasonable defaults.
+            properties = new Properties();
+        }
+
+        // Register the properties as system properties
+        Enumeration enumeration = properties.propertyNames();
+        while (enumeration.hasMoreElements()) {
+            String name = (String) enumeration.nextElement();
+            String value = properties.getProperty(name);
+            if (value != null) {
+                System.setProperty(name, value);
+            }
+        }
+
+    }
+
+
+    /**
+     * Get the value of the catalina.home environment variable.
+     */
+    private static String getCatalinaHome() {
+        return System.getProperty("catalina.home",
+                                  System.getProperty("user.dir"));
+    }
+
+
+    /**
+     * Get the value of the configuration URL.
+     */
+    private static String getConfigUrl() {
+        return System.getProperty("catalina.config");
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ClassLoaderFactory.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ClassLoaderFactory.java
new file mode 100644
index 0000000..6c293dc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ClassLoaderFactory.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.loader.StandardClassLoader;
+
+import java.io.File;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * <p>Utility class for building class loaders for Catalina.  The factory
+ * method requires the following parameters in order to build a new class
+ * loader (with suitable defaults in all cases):</p>
+ * <ul>
+ * <li>A set of directories containing unpacked classes (and resources)
+ *     that should be included in the class loader's
+ *     repositories.</li>
+ * <li>A set of directories containing classes and resources in JAR files.
+ *     Each readable JAR file discovered in these directories will be
+ *     added to the class loader's repositories.</li>
+ * <li><code>ClassLoader</code> instance that should become the parent of
+ *     the new class loader.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:06 $
+ */
+
+public final class ClassLoaderFactory {
+
+
+    // ------------------------------------------------------- Static Variables
+
+    private static final Logger log = LogFacade.getLogger();
+    /**
+     * Debugging detail level for processing the startup.
+     */
+    private static int debug = 0;
+
+
+    // ------------------------------------------------------ Static Properties
+
+
+    /**
+     * Return the debugging detail level.
+     */
+    public static int getDebug() {
+
+        return (debug);
+
+    }
+
+
+    /**
+     * Set the debugging detail level.
+     *
+     * @param newDebug The new debugging detail level
+     */
+    public static void setDebug(int newDebug) {
+
+        debug = newDebug;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * 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 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
+     */
+    public static ClassLoader createClassLoader(File unpacked[],
+                                                File packed[],
+                                                ClassLoader parent)
+        throws Exception {
+        return createClassLoader(unpacked, packed, null, parent);
+    }
+
+
+    /**
+     * 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
+     */
+    public static ClassLoader createClassLoader(File unpacked[],
+                                                File packed[],
+                                                URL urls[],
+                                                ClassLoader parent)
+        throws Exception {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Creating new class loader");
+
+        // Construct the "class path" for this class loader
+        Set<URL> set = new LinkedHashSet<URL>();
+
+        // Add unpacked directories
+        if (unpacked != null) {
+            for (int i = 0; i < unpacked.length; i++)  {
+                File file = unpacked[i];
+                if (!file.exists() || !file.canRead())
+                    continue;
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Including directory or JAR "
+                            + file.getAbsolutePath());
+                URL url = new URL("file", null,
+                                  file.getCanonicalPath() + File.separator);
+                set.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())
+                    continue;
+                String filenames[] = directory.list();
+                for (int j = 0; j < filenames.length; j++) {
+                    String filename = filenames[j].toLowerCase(Locale.ENGLISH);
+                    if (!filename.endsWith(".jar"))
+                        continue;
+                    File file = new File(directory, filenames[j]);
+                    if (log.isLoggable(Level.FINE))
+                        log.log(Level.FINE, "Including jar file " +
+                                file.getAbsolutePath());
+                    URL url = new URL("file", null,
+                                      file.getCanonicalPath());
+                    set.add(url);
+                }
+            }
+        }
+
+        // Add remote URLs
+        if (urls != null) {
+            for (int i = 0; i < urls.length; i++) {
+                set.add(urls[i]);
+            }
+        }
+
+        // Construct the class loader itself
+        final URL array[] = set.toArray(new URL[set.size()]);
+        final ClassLoader parentCL = parent;
+        StandardClassLoader classLoader = null;
+        if (parentCL == null)  {
+            classLoader = AccessController.doPrivileged(new PrivilegedAction<StandardClassLoader>() {
+                @Override
+                public StandardClassLoader run() {
+                    return new StandardClassLoader(array);
+                }
+            });
+        } else {
+            classLoader = AccessController.doPrivileged(new PrivilegedAction<StandardClassLoader>() {
+                @Override
+                public StandardClassLoader run() {
+                    return new StandardClassLoader(array, parentCL);
+                }
+            });
+        }
+        classLoader.setDelegate(true);
+        return (classLoader);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Constants.java
new file mode 100644
index 0000000..c958094
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Constants.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+/**
+ * String constants for the startup package.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @version $Revision: 1.6 $ $Date: 2007/02/20 20:16:56 $
+ */
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.startup";
+
+    public static final String ApplicationWebXml = "/WEB-INF/web.xml";
+    // START GlassFish 2439
+    public static final String DEFAULT_CONTEXT_XML = "config/context.xml";
+    // END GlassFish 2439
+    public static final String DefaultWebXml = "conf/web.xml";
+
+    public static final String TldDtdPublicId_11 =
+        "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN";
+    public static final String TldDtdResourcePath_11 =
+        "/javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd";
+
+    public static final String TldDtdPublicId_12 =
+        "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN";
+    public static final String TldDtdResourcePath_12 =
+        "/javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd";
+
+    public static final String TldSchemaPublicId_20 =
+        "web-jsptaglibrary_2_0.xsd";
+    public static final String TldSchemaResourcePath_20 =
+        "/javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd";
+
+    public static final String TLD_SCHEMA_PUBLIC_ID_21 =
+        "web-jsptaglibrary_2_1.xsd";
+    public static final String TLD_SCHEMA_RESOURCE_PATH_21 =
+        "/javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd";
+
+    public static final String WebDtdPublicId_22 =
+        "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN";
+    public static final String WebDtdResourcePath_22 =
+        "/javax/servlet/resources/web-app_2_2.dtd";
+
+    public static final String WebDtdPublicId_23 =
+        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN";
+    public static final String WebDtdResourcePath_23 =
+        "/javax/servlet/resources/web-app_2_3.dtd";
+
+    public static final String WebSchemaPublicId_24 =
+        "web-app_2_4.xsd";
+    public static final String WebSchemaResourcePath_24 =
+        "/javax/servlet/resources/web-app_2_4.xsd";
+
+    public static final String WebSchemaPublicId_25 =
+        "web-app_2_5.xsd";
+    public static final String WebSchemaResourcePath_25 =
+        "/javax/servlet/resources/web-app_2_5.xsd";
+
+    public static final String J2eeSchemaPublicId_14 =
+        "j2ee_1_4.xsd";
+    public static final String J2eeSchemaResourcePath_14 =
+        "/javax/servlet/resources/j2ee_1_4.xsd";
+
+    public static final String JAVA_EE_SCHEMA_PUBLIC_ID_5 =
+        "javaee_5.xsd";
+    public static final String JAVA_EE_SCHEMA_RESOURCE_PATH_5 =
+        "/javax/servlet/resources/javaee_5.xsd";
+
+    public static final String W3cSchemaPublicId_10 =
+        "xml.xsd";
+    public static final String W3cSchemaResourcePath_10 =
+        "/javax/servlet/resources/xml.xsd";
+
+    public static final String JspSchemaPublicId_20 =
+        "jsp_2_0.xsd";
+    public static final String JspSchemaResourcePath_20 =
+        "/javax/servlet/resources/jsp_2_0.xsd";
+    
+    public static final String JSP_SCHEMA_PUBLIC_ID_21 =
+        "jsp_2_1.xsd";
+    public static final String JSP_SCHEMA_RESOURCE_PATH_21 =
+        "/javax/servlet/resources/jsp_2_1.xsd";
+
+    public static final String J2eeWebServiceSchemaResourcePath_11 =
+            "/javax/servlet/resources/j2ee_web_services_1_1.xsd";
+    
+    public static final String J2eeWebServiceClientSchemaPublicId_11 =
+            "j2ee_web_services_client_1_1.xsd";
+    public static final String J2eeWebServiceClientSchemaResourcePath_11 =
+            "/javax/servlet/resources/j2ee_web_services_client_1_1.xsd";
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ContextConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ContextConfig.java
new file mode 100644
index 0000000..d60da10
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ContextConfig.java
@@ -0,0 +1,1258 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.*;
+import org.apache.catalina.authenticator.*;
+import org.apache.catalina.core.*;
+import org.apache.catalina.deploy.FilterDef;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.catalina.session.StandardManager;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSet;
+import org.glassfish.web.valve.GlassFishValve;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXParseException;
+
+import javax.servlet.ServletContext;
+import java.io.*;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.*;
+import java.util.logging.Logger;
+
+/**
+ * Startup event listener for a <b>Context</b> that configures the properties
+ * of that Context, and the associated defined servlets.
+ *
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @version $Revision: 1.14 $ $Date: 2007/02/20 20:16:56 $
+ */
+
+// START OF SJAS 8.0 BUG 5046959
+// public final class ContextConfig
+// NOTE: All the methods were originally private and changed to public.
+public class ContextConfig
+// END OF SJAS 8.0 BUG 5046959
+    implements LifecycleListener {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /*
+     * Custom mappings of login methods to authenticators
+     */
+    //START SJSAS 6202703
+    //private Map customAuthenticators;
+    protected Map<String, Authenticator> customAuthenticators;
+    //END SJSAS 6202703
+
+
+    /**
+     * The set of Authenticators that we know how to configure.  The key is
+     * the name of the implemented authentication method, and the value is
+     * the fully qualified Java class name of the corresponding Valve.
+     */
+    //START SJSAS 6202703
+    //private static Properties authenticators = null;
+    
+    protected static final Properties authenticators = new Properties();
+    //END SJSAS 6202703
+
+
+    private ClassLoader classLoader;
+
+
+    /**
+     * The Context we are associated with.
+     */
+    protected Context context = null;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    // START GlassFish 2439
+    /**
+     * The default web application's context file location.
+     */
+    protected String defaultContextXml = null;
+    // END GlassFish 2439
+    
+
+    /**
+     * The default web application's deployment descriptor location.
+     */
+    // BEGIN OF SJSAS 8.1 6172288  
+    // private String defaultWebXml = null;
+    protected String defaultWebXml = null;
+    
+    
+    /**
+     * Track any fatal errors during startup configuration processing.
+     */
+    // private boolean ok = false;
+    protected boolean ok = false;
+    // END OF SJSAS 8.1 6172288 
+
+
+    // START GlassFish 2439
+    /**
+     * Any parse error which occurred while parsing XML descriptors.
+     */
+    protected SAXParseException parseException = null;
+    // END GlassFish 2439 
+
+
+    // START GlassFish 2439
+    /**
+     * The <code>Digester</code> we will use to process web application
+     * context files.
+     */
+    protected static final Digester contextDigester =
+        createContextDigester();
+    // END GlassFish 2439
+
+
+    /**
+     * The <code>Digester</code> we will use to process web application
+     * deployment descriptor files.
+     */
+    // BEGIN OF SJSAS 8.1 6172288  
+    // private static Digester webDigester = null;
+    protected static final Digester webDigester =
+        createWebDigester();
+    // END OF SJSAS 8.1 6172288  
+    
+    
+    /**
+     * The <code>Rule</code> used to parse the web.xml
+     */
+    // BEGIN OF SJSAS 8.1 6172288  
+    // private static WebRuleSet webRuleSet = new WebRuleSet();
+    protected static final WebRuleSet webRuleSet = new WebRuleSet();
+    // END OF SJSAS 8.1 6172288  
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+    private static boolean xmlValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off XML namespace awarenes.
+     */
+    private static boolean xmlNamespaceAware = false;
+
+        
+    /**
+     * Static initializer
+     */
+    static {
+        authenticators.setProperty(
+            "BASIC", BasicAuthenticator.class.getName());
+        authenticators.setProperty(
+            "CLIENT-CERT", SSLAuthenticator.class.getName());
+        authenticators.setProperty(
+            "FORM", FormAuthenticator.class.getName());
+        authenticators.setProperty(
+            "NONE", NonLoginAuthenticator.class.getName());
+        authenticators.setProperty(
+            "DIGEST", DigestAuthenticator.class.getName());
+    }
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    public void setClassLoader(ClassLoader cl) {
+        this.classLoader = cl;
+    }
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (this.debug);
+
+    }
+
+
+    /**
+     * Set the debugging detail level for this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+        this.debug = debug;
+    }
+    
+    
+    // START GlassFish 2439
+    /**
+     * Return the location of the default deployment descriptor
+     */
+    public String getDefaultWebXml() {
+        if( defaultWebXml == null ) defaultWebXml=Constants.DefaultWebXml;
+        return (this.defaultWebXml);
+    }
+
+
+    /**
+     * Set the location of the default deployment descriptor
+     *
+     * @param path Absolute/relative path to the default web.xml
+     */
+    public void setDefaultWebXml(String path) {
+        this.defaultWebXml = path;
+    }
+    // END GlassFish 2439
+
+
+    /**
+     * Return the location of the default context file
+     */
+    public String getDefaultContextXml() {
+        if( defaultContextXml == null ) {
+            defaultContextXml=Constants.DEFAULT_CONTEXT_XML;
+        }
+
+        return (this.defaultContextXml);
+    }
+
+
+    /**
+     * Set the location of the default context file
+     *
+     * @param path Absolute/relative path to the default context.xml
+     */
+    public void setDefaultContextXml(String path) {
+        this.defaultContextXml = path;
+    }
+
+
+    /**
+     * Sets custom mappings of login methods to authenticators.
+     *
+     * @param customAuthenticators Custom mappings of login methods to
+     * authenticators
+     */
+    public void setCustomAuthenticators(Map<String, Authenticator> customAuthenticators) {
+        this.customAuthenticators = customAuthenticators;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the START event for an associated Context.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event)
+            throws LifecycleException {
+
+        // Identify the context we are associated with
+        try {
+            context = (Context) event.getLifecycle();
+        } catch (ClassCastException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.EVENT_DATA_IS_NOT_CONTEXT_EXCEPTION),
+                                              event.getLifecycle());
+            throw new LifecycleException(msg, e);
+        }
+
+        // Called from ContainerBase.addChild() -> StandardContext.start()
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.START_EVENT)) {
+            start();
+        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
+            stop();
+        // START GlassFish 2439
+        } else if (event.getType().equals(Lifecycle.INIT_EVENT)) {
+            init();
+        // END GlassFish 2439
+        }
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Process the application configuration file, if it exists.
+     */
+    protected void applicationConfig() throws LifecycleException {
+
+        String altDDName = null;
+
+        // Open the application web.xml file, if it exists
+        InputStream stream = null;
+        ServletContext servletContext = context.getServletContext();
+        if (servletContext != null) {
+            altDDName = (String)servletContext.getAttribute(
+                    Globals.ALT_DD_ATTR);
+            if (altDDName != null) {
+                try {
+                    stream = new FileInputStream(altDDName);
+                } catch (FileNotFoundException e) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.ALT_DD_FILE_NOT_FOUND_EXCEPTION),
+                                                      altDDName);
+                    throw new LifecycleException(msg);
+                }
+            }
+            else {
+                stream = servletContext.getResourceAsStream
+                    (Constants.ApplicationWebXml);
+            }
+        }
+        if (stream == null) {
+            /* PWC 6296257
+            log.info(sm.getString("contextConfig.applicationMissing") + " " + context);
+            //contextConfig.applicationMissing=PWC3013: Missing application web.xml, using defaults only
+            */
+            // START PWC 6296257
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, LogFacade.MISSING_APP_WEB_XML_FINE, context);
+            }
+            // END PWC 6296257
+            return;
+        }
+        
+        long t1=System.currentTimeMillis();
+
+        URL url=null;
+        // Process the application web.xml file
+        synchronized (webDigester) {
+            try {
+                if (altDDName != null) {
+                    url = new File(altDDName).toURL();
+                } else {
+                    url = servletContext.getResource(
+                                                Constants.ApplicationWebXml);
+                }
+                if( url!=null ) {
+                    InputSource is = new InputSource(url.toExternalForm());
+                    is.setByteStream(stream);
+                    webDigester.clear();
+                    webDigester.setDebug(getDebug());
+                    if (context instanceof StandardContext) {
+                        ((StandardContext) context).setReplaceWelcomeFiles(true);
+                    }
+                    webDigester.setUseContextClassLoader(false);
+                    webDigester.push(context);
+                    webDigester.parse(is);
+                } else {
+                    if (log.isLoggable(Level.INFO)) {
+                        log.log(Level.INFO, LogFacade.NO_WEB_XML_INFO);
+                    }
+                }
+            } catch (SAXParseException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.PARSE_ERROR_IN_APP_WEB_XML_EXCEPTION),
+                                                  new Object[] {e.getLineNumber(), e.getColumnNumber()});
+                throw new LifecycleException(msg, e);
+            } catch (Exception e) {
+                throw new LifecycleException(rb.getString(LogFacade.PARSE_ERROR_IN_APP_WEB_XML), e);
+            } finally {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    log.log(Level.SEVERE, LogFacade.ERROR_CLOSING_APP_WEB_XML_EXCEPTION,
+                            e);
+                }
+                webDigester.push(null);
+            }
+        }
+        webRuleSet.recycle();
+
+        long t2=System.currentTimeMillis();
+        if (context instanceof StandardContext) {
+            ((StandardContext) context).setStartupTime(t2-t1);
+        }
+    }
+
+
+    /**
+     * Set up a manager.
+     */
+    protected synchronized void managerConfig() {
+        if (context.getManager() == null) {
+            context.setManager(new StandardManager());
+        }
+    }
+
+
+    /**
+     * Set up an Authenticator automatically if required, and one has not
+     * already been configured.
+     */
+    protected synchronized void authenticatorConfig()
+            throws LifecycleException {
+
+        // Does this Context require an Authenticator?
+        /* START IASRI 4856062
+           // This constraints check is relocated to happen after
+           // setRealmName(). This allows apps which have no constraints
+           // and no authenticator to still have a realm name set in
+           // their RealmAdapater. This is only relevant in the case where
+           // the core ACLs are doing all access control AND the servlet
+           // wishes to call isUserInRole AND the application does have
+           // security-role-mapping elements in sun-web.xml. This is probably
+           // not an interesting scenario. But might as well allow it to
+           // work, maybe it is of some use.
+        SecurityConstraint constraints[] = context.findConstraints();
+        if ((constraints == null) || (constraints.length == 0))
+            return;
+        */
+        LoginConfig loginConfig = context.getLoginConfig();
+        if (loginConfig == null) {
+            loginConfig = new LoginConfig("NONE", null, null, null);
+            context.setLoginConfig(loginConfig);
+        }
+
+        // Has an authenticator been configured already?
+        if (context instanceof Authenticator)
+            return;
+        if (context instanceof ContainerBase) {
+            Pipeline pipeline = ((ContainerBase) context).getPipeline();
+            if (pipeline != null) {
+                GlassFishValve basic = pipeline.getBasic();
+                if ((basic != null) && (basic instanceof Authenticator))
+                    return;
+                GlassFishValve valves[] = pipeline.getValves();
+                for (int i = 0; i < valves.length; i++) {
+                    if (valves[i] instanceof Authenticator)
+                        return;
+                }
+            }
+        } else {
+            return;     // Cannot install a Valve even if it would be needed
+        }
+
+        // Has a Realm been configured for us to authenticate against?
+        /* START IASRI 4856062
+        if (context.getRealm() == null) {
+        */
+        // BEGIN IASRI 4856062
+        Realm rlm = context.getRealm();
+        if (rlm == null) {
+        // END IASRI 4856062
+            throw new LifecycleException(rb.getString(LogFacade.NO_REALM_BEEN_CONFIGURED_EXCEPTION));
+        }
+
+        // BEGIN IASRI 4856062
+        // If a realm is available set its name in the Realm(Adapter)
+        rlm.setRealmName(loginConfig.getRealmName(),
+                         loginConfig.getAuthMethod());
+        if (!context.hasConstraints()) {
+            return;
+        }
+        // END IASRI 4856062
+
+        /*
+         * First check to see if there is a custom mapping for the login
+         * method. If so, use it. Otherwise, check if there is a mapping in
+         * org/apache/catalina/startup/Authenticators.properties.
+         */
+        GlassFishValve authenticator = null;
+        if (customAuthenticators != null) {
+            /* PWC 6392537
+            authenticator = (Valve)
+                customAuthenticators.get(loginConfig.getAuthMethod());
+            */
+            // START PWC 6392537
+            String loginMethod = loginConfig.getAuthMethod();
+            if (loginMethod != null
+                    && customAuthenticators.containsKey(loginMethod)) {
+                authenticator = getGlassFishValveAuthenticator(loginMethod);
+                if (authenticator == null) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_CONFIG_AUTHENTICATOR_EXCEPTION),
+                                                      loginMethod);
+                    throw new LifecycleException(msg);
+                }
+            }
+            // END PWC 6392537
+        }
+
+        if (authenticator == null) {
+            // Identify the class name of the Valve we should configure
+            String authenticatorName = null;
+
+            // BEGIN RIMOD 4808402
+            // If login-config is given but auth-method is null, use NONE
+            // so that NonLoginAuthenticator is picked
+            String authMethod = loginConfig.getAuthMethod();
+            if (authMethod == null) {
+                authMethod = "NONE";
+            }
+            authenticatorName = authenticators.getProperty(authMethod);
+            // END RIMOD 4808402
+            /* RIMOD 4808402
+            authenticatorName =
+                    authenticators.getProperty(loginConfig.getAuthMethod());
+            */
+
+            if (authenticatorName == null) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_CONFIG_AUTHENTICATOR_EXCEPTION),
+                                                  loginConfig.getAuthMethod());
+                throw new LifecycleException(msg);
+            }
+
+            // Instantiate and install an Authenticator of the requested class
+            try {
+                Class authenticatorClass = Class.forName(authenticatorName);
+                authenticator = (GlassFishValve) authenticatorClass.newInstance();
+            } catch (Throwable t) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_INSTANTIATE_AUTHENTICATOR_EXCEPTION),
+                                                  authenticatorName);
+                throw new LifecycleException(msg, t);
+            }
+        }
+
+        if (authenticator != null && context instanceof ContainerBase) {
+            Pipeline pipeline = ((ContainerBase) context).getPipeline();
+            if (pipeline != null) {
+                ((ContainerBase) context).addValve(authenticator);
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, LogFacade.CONFIGURED_AUTHENTICATOR_FINE, loginConfig.getAuthMethod());
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private GlassFishValve getGlassFishValveAuthenticator(String loginMethod) {
+        return (GlassFishValve) customAuthenticators.get(loginMethod);
+    }
+
+    /**
+     * Create and return a Digester configured to process the
+     * web application deployment descriptor (web.xml).
+     */
+    // BEGIN OF SJSAS 8.1 6172288  
+    // private static Digester createWebDigester() {
+    public static Digester createWebDigester() {
+    // END OF SJSAS 8.1 6172288  
+        return createWebXmlDigester(xmlNamespaceAware, xmlValidation);
+    }
+
+
+    /*
+     * Create and return a Digester configured to process the
+     * web application deployment descriptor (web.xml).
+     */
+    public static Digester createWebXmlDigester(boolean namespaceAware,
+                                                boolean validation) {
+        DigesterFactory df = org.glassfish.internal.api.Globals.get(
+            DigesterFactory.class);
+        Digester digester = df.newDigester(xmlValidation,
+            xmlNamespaceAware, webRuleSet);
+        digester.getParser();
+        return digester;
+    }
+
+
+    // START GlassFish 2439 
+    /**
+     * Create and return a Digester configured to process the
+     * context configuration descriptor for an application.
+     */
+    protected static Digester createContextDigester() {
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        RuleSet contextRuleSet = new ContextRuleSet("", false);
+        digester.addRuleSet(contextRuleSet);
+        RuleSet namingRuleSet = new NamingRuleSet("Context/");
+        digester.addRuleSet(namingRuleSet);
+        digester.getParser();
+        return digester;
+    }
+    // END GlassFish 2439
+
+
+    protected String getBaseDir() {
+        Container engineC=context.getParent().getParent();
+        if( engineC instanceof StandardEngine ) {
+            return ((StandardEngine)engineC).getBaseDir();
+        }
+        return System.getProperty("catalina.base");
+    }
+
+    /**
+     * Process the default configuration file, if it exists.
+     * The default config must be read with the container loader - so
+     * container servlets can be loaded
+     */
+    protected void defaultConfig() throws LifecycleException {
+        long t1 = System.currentTimeMillis();
+
+        // Open the default web.xml file, if it exists
+        if (defaultWebXml == null && context instanceof StandardContext) {
+            defaultWebXml=((StandardContext)context).getDefaultWebXml();
+        }
+        // set the default if we don't have any overrides
+        if (defaultWebXml == null) getDefaultWebXml();
+
+        File file = new File(this.defaultWebXml);
+        if (!file.isAbsolute()) {
+            file = new File(getBaseDir(), this.defaultWebXml);
+        }
+        
+        InputStream stream = null;
+        InputSource source = null;
+
+        try {
+            if ( ! file.exists() ) {
+                // Use getResource and getResourceAsStream
+                stream = getClass().getClassLoader()
+                    .getResourceAsStream(defaultWebXml);
+                if( stream != null ) {
+                    source = new InputSource
+                            (getClass().getClassLoader()
+                            .getResource(defaultWebXml).toString());
+                } 
+
+                if (stream == null) { 
+                    // maybe embedded
+                    stream = getClass().getClassLoader()
+                        .getResourceAsStream("web-embed.xml");
+                    if( stream != null ) {
+                        source = new InputSource
+                        (getClass().getClassLoader()
+                                .getResource("web-embed.xml").toString());
+                    }                                         
+                }
+
+                if( stream== null ) {
+                    if (log.isLoggable(Level.INFO)) {
+                        log.log(Level.INFO, LogFacade.NO_DEFAULT_WEB_XML_INFO);
+                    }
+                    // no default web.xml
+                    return;
+                }
+            } else {
+                source =
+                    new InputSource("file://" + file.getAbsolutePath());
+                stream = new FileInputStream(file);
+            }
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.MISSING_DEFAULT_WEB_XML_EXCEPTION),
+                                              new Object[] {defaultWebXml, file});
+            throw new LifecycleException(msg, e);
+        }
+
+        // Process the default web.xml file
+        synchronized (webDigester) {
+            try {
+                source.setByteStream(stream);
+                webDigester.setDebug(getDebug());
+                // JFA
+                if (context instanceof StandardContext)
+                    ((StandardContext) context).setReplaceWelcomeFiles(true);
+                webDigester.clear();
+                webDigester.setClassLoader(this.getClass().getClassLoader());
+                webDigester.setUseContextClassLoader(false);
+                webDigester.push(context);
+                webDigester.parse(source);
+            } catch (SAXParseException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.PARSE_ERROR_IN_DEFAULT_WEB_XML_EXCEPTION),
+                                                  new Object[] {e.getLineNumber(), e.getColumnNumber()});
+                throw new LifecycleException(msg, e);
+            } catch (Exception e) {
+                throw new LifecycleException(rb.getString(LogFacade.PARSE_ERROR_IN_DEFAULT_WEB_XML), e);
+            } finally {
+                try {
+                    if (stream != null) {
+                        stream.close();
+                    }
+                } catch (IOException e) {
+                    log.log(Level.SEVERE, LogFacade.ERROR_CLOSING_DEFAULT_WEB_XML_EXCEPTION, e);
+                }
+            }
+        }
+        webRuleSet.recycle();
+        
+        long t2=System.currentTimeMillis();
+        if( (t2-t1) > 200 && log.isLoggable(Level.FINE) )
+            log.log(Level.FINE, "Processed default web.xml " + file + " "  + ( t2-t1));
+    }
+
+
+    // START GlassFish 2439
+    /**
+     * Process the default configuration file, if it exists.
+     */
+    protected void contextConfig() {
+
+        if( defaultContextXml==null ) getDefaultContextXml();
+
+        if (!context.getOverride()) {
+            processContextConfig(new File(getBaseDir()), defaultContextXml);
+        }
+
+        if (context.getConfigFile() != null)
+            processContextConfig(new File(context.getConfigFile()), null);
+
+    }
+
+
+    /**
+     * Process a context.xml.
+     */
+    protected void processContextConfig(File baseDir, String resourceName) {
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Processing context [" + context.getName() +
+                    "] configuration file " + baseDir + " " + resourceName);
+
+        InputSource source = null;
+        InputStream stream = null;
+
+        File file = baseDir;
+        if (resourceName != null) {
+            file = new File(baseDir, resourceName);
+        }
+        try {
+            if ( !file.exists() ) {
+                if (resourceName != null) {
+                    // Use getResource and getResourceAsStream
+                    stream = getClass().getClassLoader()
+                        .getResourceAsStream(resourceName);
+                    if( stream != null ) {
+                        source = new InputSource
+                            (getClass().getClassLoader()
+                            .getResource(resourceName).toString());
+                    }
+                }
+            } else {
+                source =
+                    new InputSource("file://" + file.getAbsolutePath());
+                stream = new FileInputStream(file);
+                // Add as watched resource so that cascade reload occurs if a default
+                // config file is modified/added/removed
+                context.addWatchedResource(file.getAbsolutePath());
+            }
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.MISSING_DEFAULT_WEB_XML_EXCEPTION),
+                                              new Object[] {resourceName, file});
+            log.log(Level.SEVERE, msg, e);
+        }
+
+        if (source == null) {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch(IOException e) {
+                    log.log(Level.SEVERE, LogFacade.ERROR_CLOSING_DEFAULT_WEB_XML_EXCEPTION, e);
+                }
+            }
+
+            return;
+        }
+
+        synchronized (contextDigester) {
+            try {
+                source.setByteStream(stream);
+                contextDigester.setClassLoader(classLoader);
+                contextDigester.setUseContextClassLoader(false);
+                contextDigester.push(context.getParent());
+                contextDigester.push(context);
+                contextDigester.setErrorHandler(new ContextErrorHandler());
+                contextDigester.parse(source);
+                if (parseException != null) {
+                    ok = false;
+                }
+                if (log.isLoggable(Level.FINE))
+                    log.log(Level.FINE, "Successfully processed context [" +
+                            context.getName() + "] configuration file " +
+                            baseDir + " " + resourceName);
+            } catch (SAXParseException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.PARSE_ERROR_IN_DEFAULT_WEB_XML_EXCEPTION),
+                                                  new Object[] {e.getLineNumber(), e.getColumnNumber()});
+                log.log(Level.SEVERE, LogFacade.PARSE_ERROR_IN_DEFAULT_WEB_XML, e);
+                log.log(Level.SEVERE, msg);
+                ok = false;
+            } catch (Exception e) {
+                log.log(Level.SEVERE, LogFacade.PARSE_ERROR_IN_DEFAULT_WEB_XML, e);
+                ok = false;
+            } finally {
+                //contextDigester.reset();
+                parseException = null;
+                try {
+                    if (stream != null) {
+                        stream.close();
+                    }
+                } catch (IOException e) {
+                    log.log(Level.SEVERE, LogFacade.ERROR_CLOSING_DEFAULT_WEB_XML_EXCEPTION, e);
+                }
+            }
+        }
+
+    }
+    // END GlassFish 2439
+
+        
+    /**
+     * Adjust docBase.
+     */
+    protected void fixDocBase()
+        throws IOException {
+        
+        Host host = (Host) context.getParent();
+        String appBase = host.getAppBase();
+        
+        boolean unpackWARs = true;
+        if (host instanceof StandardHost) {
+            unpackWARs = ((StandardHost) host).isUnpackWARs() 
+                && ((StandardContext) context).getUnpackWAR();
+        }
+
+        File canonicalAppBase = null;
+        if (appBase != null) {
+            canonicalAppBase = new File(appBase);
+            if (canonicalAppBase.isAbsolute()) {
+                canonicalAppBase = canonicalAppBase.getCanonicalFile();
+            } else {
+                canonicalAppBase = 
+                    new File(System.getProperty("catalina.base"), appBase)
+                    .getCanonicalFile();
+            }
+        }
+
+        String docBase = context.getDocBase();
+        if (docBase == null) {
+            // Trying to guess the docBase according to the path
+            String path = context.getPath();
+            if (path == null) {
+                return;
+            }
+            if (path.equals("")) {
+                docBase = "ROOT";
+            } else {
+                if (path.startsWith("/")) {
+                    docBase = path.substring(1).replace('/', '#');
+                } else {
+                    docBase = path.replace('/', '#');
+                }
+            }
+        }
+
+        File file = new File(docBase);
+        if (!file.isAbsolute() && canonicalAppBase != null) {
+            docBase = (new File(canonicalAppBase, docBase)).getPath();
+        } else {
+            docBase = file.getCanonicalPath();
+        }
+        file = new File(docBase);
+        String origDocBase = docBase;
+        
+        String pathName = context.getPath();
+        if (pathName.equals("")) {
+            pathName = "ROOT";
+        } else {
+            // Context path must start with '/'
+            pathName = pathName.substring(1).replace('/','#');
+        }
+        if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory() && unpackWARs) {
+            URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/");
+            docBase = ExpandWar.expand(host, war, pathName);
+            file = new File(docBase);
+            docBase = file.getCanonicalPath();
+            if (context instanceof StandardContext) {
+                ((StandardContext) context).setOriginalDocBase(origDocBase);
+            }
+        } else if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") &&
+                !file.isDirectory() && !unpackWARs) {
+            URL war =
+                new URL("jar:" + (new File (docBase)).toURI().toURL() + "!/");
+            ExpandWar.validate(host, war, pathName);
+        } else {
+            File docDir = new File(docBase);
+            if (!docDir.exists()) {
+                File warFile = new File(docBase + ".war");
+                if (warFile.exists()) {
+                    URL war =
+                        new URL("jar:" + warFile.toURI().toURL() + "!/");
+                    if (unpackWARs) {
+                        docBase = ExpandWar.expand(host, war, pathName);
+                        file = new File(docBase);
+                        docBase = file.getCanonicalPath();
+                    } else {
+                        docBase = warFile.getCanonicalPath();
+                        ExpandWar.validate(host, war, pathName);
+                    }
+                }
+                if (context instanceof StandardContext) {
+                    ((StandardContext) context).setOriginalDocBase(origDocBase);
+                }
+            }
+        }
+
+        if (canonicalAppBase != null &&
+                docBase.startsWith(canonicalAppBase.getPath())) {
+            docBase = docBase.substring(canonicalAppBase.getPath().length());
+            docBase = docBase.replace(File.separatorChar, '/');
+            if (docBase.startsWith("/")) {
+                docBase = docBase.substring(1);
+            }
+        } else {
+            docBase = docBase.replace(File.separatorChar, '/');
+        }
+
+        context.setDocBase(docBase);
+
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Context (if any)
+     *
+     * @param message Message to be logged
+     *
+    private void log(String message) {
+
+        org.apache.catalina.Logger logger = null;
+        if (context != null)
+            logger = context.getLogger();
+        if (logger != null)
+            logger.log("ContextConfig[" + context.getName() + "]: " + message);
+        else
+            log.info( message );
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Context (if any)
+     *
+     * @param message Message to be logged
+     * @param throwable Associated exception
+     *
+    private void log(String message, Throwable throwable) {
+
+        org.apache.catalina.Logger logger = null;
+        if (context != null)
+            logger = context.getLogger();
+        if (logger != null)
+            logger.log("ContextConfig[" + context.getName() + "] "
+                       + message, throwable);
+        else {
+            log.log(Level.SEVERE, message, throwable );
+        }
+    }
+    */
+
+
+    
+    // START GlassFish 2439
+    /**
+     * Process a "init" event for this Context.
+     */
+    protected void init() {
+        // Called from StandardContext.init()
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, LogFacade.CONTEXT_CONFIG_INIT_FINE);
+        context.setConfigured(false);
+        ok = true;
+
+        contextConfig();
+        
+        try {
+            fixDocBase();
+        } catch (IOException e) {
+            log.log(Level.SEVERE, LogFacade.FIXING_DOC_BASE_EXCEPTION, e);
+        }
+
+
+    }
+    // END GlassFish 2439
+
+
+    /**
+     * Process a "start" event for this Context - in background
+     */
+    protected synchronized void start() throws LifecycleException {
+        if (log.isLoggable(Level.FINEST)) {
+            log.log(Level.FINEST, LogFacade.PROCESSING_START_FINEST);
+        }
+
+        context.setConfigured(false);
+
+        // Set properties based on DefaultContext
+        Container container = context.getParent();
+        if( !context.getOverride() ) {
+            if( container instanceof Host ) {
+             
+                // Reset the value only if the attribute wasn't
+                // set on the context.
+                xmlValidation = context.getXmlValidation();
+                if (!xmlValidation) {
+                    xmlValidation = ((Host)container).getXmlValidation();
+                }
+                
+                xmlNamespaceAware = context.getXmlNamespaceAware();
+                if (!xmlNamespaceAware){
+                    xmlNamespaceAware 
+                                = ((Host)container).getXmlNamespaceAware();
+                }
+
+            }
+        }
+
+        // Process the default and application web.xml files
+        defaultConfig();
+        applicationConfig();
+        validateSecurityRoles();
+
+        // Configure an authenticator if we need one
+        authenticatorConfig();
+
+        // Configure a manager
+        managerConfig();
+
+        // Dump the contents of this pipeline if requested
+        if ((log.isLoggable(Level.FINEST)) &&
+                (context instanceof ContainerBase)) {
+            log.log(Level.FINEST, "Pipline Configuration:");
+            Pipeline pipeline = ((ContainerBase) context).getPipeline();
+            GlassFishValve valves[] = null;
+            if (pipeline != null)
+                valves = pipeline.getValves();
+            if (valves != null) {
+                for (int i = 0; i < valves.length; i++) {
+                    log.log(Level.FINEST, "  " + valves[i].getInfo());
+                }
+            }
+            log.log(Level.FINEST, "======================");
+        }
+
+        // Make our application available because no problems
+        // were encountered
+        context.setConfigured(true);
+    }
+
+
+    /**
+     * Process a "stop" event for this Context.
+     */
+    protected synchronized void stop() {
+
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, LogFacade.PROCESSING_STOP_FINEST);
+
+        int i;
+
+        // Removing children
+        Container[] children = context.findChildren();
+        for (i = 0; i < children.length; i++) {
+            context.removeChild(children[i]);
+        }
+
+        // Removing application parameters
+/*        ApplicationParameter[] applicationParameters =
+            context.findApplicationParameters();
+        for (i = 0; i < applicationParameters.length; i++) {
+            context.removeApplicationParameter
+                (applicationParameters[i].getName());
+        }
+*/
+        // Removing security constraints
+        context.removeConstraints();
+
+        // Removing Ejbs
+        /*
+        ContextEjb[] contextEjbs = context.findEjbs();
+        for (i = 0; i < contextEjbs.length; i++) {
+            context.removeEjb(contextEjbs[i].getName());
+        }
+        */
+
+        // Removing environments
+        /*
+        ContextEnvironment[] contextEnvironments = context.findEnvironments();
+        for (i = 0; i < contextEnvironments.length; i++) {
+            context.removeEnvironment(contextEnvironments[i].getName());
+        }
+        */
+
+        // Removing errors pages
+        context.removeErrorPages();
+
+        // Removing filter defs
+        FilterDef[] filterDefs = context.findFilterDefs();
+        for (i = 0; i < filterDefs.length; i++) {
+            context.removeFilterDef(filterDefs[i]);
+        }
+
+        // Removing filter maps
+        context.removeFilterMaps();
+
+        // Removing local ejbs
+        /*
+        ContextLocalEjb[] contextLocalEjbs = context.findLocalEjbs();
+        for (i = 0; i < contextLocalEjbs.length; i++) {
+            context.removeLocalEjb(contextLocalEjbs[i].getName());
+        }
+        */
+
+        // Removing Mime mappings
+        String[] mimeMappings = context.findMimeMappings();
+        for (i = 0; i < mimeMappings.length; i++) {
+            context.removeMimeMapping(mimeMappings[i]);
+        }
+
+        // Removing parameters
+        String[] parameters = context.findParameters();
+        for (i = 0; i < parameters.length; i++) {
+            context.removeParameter(parameters[i]);
+        }
+
+        // Removing resource env refs
+        /*
+        String[] resourceEnvRefs = context.findResourceEnvRefs();
+        for (i = 0; i < resourceEnvRefs.length; i++) {
+            context.removeResourceEnvRef(resourceEnvRefs[i]);
+        }
+        */
+
+        // Removing resource links
+        /*
+        ContextResourceLink[] contextResourceLinks =
+            context.findResourceLinks();
+        for (i = 0; i < contextResourceLinks.length; i++) {
+            context.removeResourceLink(contextResourceLinks[i].getName());
+        }
+        */
+
+        // Removing resources
+        /*
+        ContextResource[] contextResources = context.findResources();
+        for (i = 0; i < contextResources.length; i++) {
+            context.removeResource(contextResources[i].getName());
+        }
+        */
+
+        // Removing sercurity role
+        context.removeSecurityRoles();
+
+        // Removing servlet mappings
+        String[] servletMappings = context.findServletMappings();
+        for (i = 0; i < servletMappings.length; i++) {
+            context.removeServletMapping(servletMappings[i]);
+        }
+
+        // FIXME : Removing status pages
+
+        // Removing welcome files
+        context.removeWelcomeFiles();
+
+        // Removing wrapper lifecycles
+        context.removeWrapperLifecycles();
+
+        // Removing wrapper listeners
+        context.removeWrapperListeners();
+
+        ok = true;
+
+    }
+
+    /**
+     * Validate the usage of security role names in the web application
+     * deployment descriptor.  If any problems are found, issue warning
+     * messages (for backwards compatibility) and add the missing roles.
+     * (To make these problems fatal instead, simply set the <code>ok</code>
+     * instance variable to <code>false</code> as well).
+     */
+    protected void validateSecurityRoles() {
+
+        // Check role names used in <security-constraint> elements
+        Iterator<SecurityConstraint> iter =
+            context.getConstraints().iterator(); 
+        while (iter.hasNext()) {
+            for (String role : iter.next().findAuthRoles()) {
+                if (!"*".equals(role) &&
+                        !context.hasSecurityRole(role)) {
+                    if (log.isLoggable(Level.INFO)) {
+                        log.log(Level.INFO, LogFacade.SECURITY_ROLE_NAME_USED_IN_AUTH_WITHOUT_DEFINITION,
+                                new Object[] {role, context.getName()});
+                    }
+                    context.addSecurityRole(role);
+                }
+            }
+        }
+
+        // Check role names used in <servlet> elements
+        Container wrappers[] = context.findChildren();
+        for (int i = 0; i < wrappers.length; i++) {
+            Wrapper wrapper = (Wrapper) wrappers[i];
+            String runAs = wrapper.getRunAs();
+            if ((runAs != null) && !context.hasSecurityRole(runAs)) {
+                if (log.isLoggable(Level.INFO)) {
+                    log.log(Level.INFO, LogFacade.SECURITY_ROLE_NAME_USED_IN_RUNAS_WITHOUT_DEFINITION,
+                            new Object[] {runAs, context.getName()});
+                }
+                context.addSecurityRole(runAs);
+            }
+            String names[] = wrapper.findSecurityReferences();
+            for (int j = 0; j < names.length; j++) {
+                String link = wrapper.findSecurityReference(names[j]);
+                if ((link != null) && !context.hasSecurityRole(link)) {
+                    if (log.isLoggable(Level.INFO)) {
+                        log.log(Level.INFO, LogFacade.SECURITY_ROLE_NAME_USED_IN_LINK_WITHOUT_DEFINITION,
+                                new Object[] {link, context.getName()});
+                    }
+                    context.addSecurityRole(link);
+                }
+            }
+        }
+
+    }
+
+
+    // START GlassFish 2439
+    protected class ContextErrorHandler
+        implements ErrorHandler {
+
+        public void error(SAXParseException exception) {
+            parseException = exception;
+        }
+
+        public void fatalError(SAXParseException exception) {
+            parseException = exception;
+        }
+
+        public void warning(SAXParseException exception) {
+            parseException = exception;
+        }
+
+    }
+    // END GlassFish 2439
+
+
+} //end of public class
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ContextRuleSet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ContextRuleSet.java
new file mode 100644
index 0000000..ae255d1
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ContextRuleSet.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Loader;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.apache.tomcat.util.digester.RuleSetBase;
+import org.xml.sax.Attributes;
+
+import java.lang.reflect.Constructor;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a
+ * Context or DefaultContext definition element.  To enable parsing of a
+ * DefaultContext, be sure to specify a prefix that ends with "/Default".</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.6 $ $Date: 2007/08/22 23:38:51 $
+ */
+
+public class ContextRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    // START GlassFish 2439
+    /**
+     * Should the context be created.
+     */
+    protected boolean create = true;
+    // END GlassFish 2439
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public ContextRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public ContextRuleSet(String prefix) {
+
+        super();
+        this.prefix = prefix;
+
+    }
+
+
+    // START GlassFish 2439
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public ContextRuleSet(String prefix, boolean create) {
+
+        super();
+        this.prefix = prefix;
+        this.create = create;
+
+    }
+    // END GlassFish 2439
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>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.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+        
+        if (create) {            
+            digester.addObjectCreate(prefix + "Context",
+                    "org.apache.catalina.core.StandardContext", "className");
+            digester.addSetProperties(prefix + "Context");
+        } else {            
+            digester.addRule(prefix + "Context", new SetContextPropertiesRule());
+        }
+        
+        if (create) {            
+            digester.addRule(prefix + "Context",
+                             new LifecycleListenerRule
+                                 (digester,
+                                  "org.apache.catalina.startup.ContextConfig",
+                                  "configClass"));
+            digester.addSetNext(prefix + "Context",
+                                "addChild",
+                                "org.apache.catalina.Container");
+        }                        
+        digester.addCallMethod(prefix + "Context/InstanceListener",
+                               "addInstanceListener", 0);
+                            
+        digester.addObjectCreate(prefix + "Context/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Listener");
+        digester.addSetNext(prefix + "Context/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+                            
+        digester.addObjectCreate(prefix + "Context/Loader",
+                            "org.apache.catalina.loader.WebappLoader",
+                            "className"); 
+        digester.addSetProperties(prefix + "Context/Loader");
+        digester.addSetNext(prefix + "Context/Loader",
+                            "setLoader",
+                            "org.apache.catalina.Loader");
+
+        digester.addObjectCreate(prefix + "Context/Manager",
+                                 "org.apache.catalina.session.StandardManager",
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Manager");
+        digester.addSetNext(prefix + "Context/Manager",
+                            "setManager",
+                            "org.apache.catalina.Manager");
+
+        digester.addObjectCreate(prefix + "Context/Manager/Store",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Manager/Store");
+        digester.addSetNext(prefix + "Context/Manager/Store",
+                            "setStore",
+                            "org.apache.catalina.Store");
+
+        digester.addObjectCreate(prefix + "Context/Parameter",
+                                 "org.apache.catalina.deploy.ApplicationParameter");
+        digester.addSetProperties(prefix + "Context/Parameter");
+        digester.addSetNext(prefix + "Context/Parameter",
+                            "addApplicationParameter",
+                            "org.apache.catalina.deploy.ApplicationParameter");
+
+        digester.addObjectCreate(prefix + "Context/Realm",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Realm");
+        digester.addSetNext(prefix + "Context/Realm",
+                            "setRealm",
+                            "org.apache.catalina.Realm");
+
+        digester.addObjectCreate(prefix + "Context/Resources",
+                                 "org.apache.naming.resources.FileDirContext",
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Resources");
+        digester.addSetNext(prefix + "Context/Resources",
+                            "setResources",
+                            "javax.naming.directory.DirContext");
+
+        digester.addObjectCreate(prefix + "Context/ResourceLink",
+                                 "org.apache.catalina.deploy.ContextResourceLink");
+        digester.addSetProperties(prefix + "Context/ResourceLink");
+        digester.addSetNext(prefix + "Context/ResourceLink",
+                            "addResourceLink",
+                            "org.apache.catalina.deploy.ContextResourceLink");
+
+        digester.addObjectCreate(prefix + "Context/Valve",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Context/Valve");
+        digester.addSetNext(prefix + "Context/Valve",
+                            "addValve",
+                            "org.glassfish.web.valve.GlassFishValve");
+
+        digester.addCallMethod(prefix + "Context/WatchedResource",
+                               "addWatchedResource", 0);
+
+        digester.addCallMethod(prefix + "Context/WrapperLifecycle",
+                               "addWrapperLifecycle", 0);
+
+        digester.addCallMethod(prefix + "Context/WrapperListener",
+                               "addWrapperListener", 0);
+
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Are we processing a DefaultContext element?
+     */
+    protected boolean isDefaultContext() {
+
+        return (prefix.endsWith("/Default"));
+
+    }
+
+
+}
+
+
+// ----------------------------------------------------------- Private Classes
+
+
+/**
+ * Rule that creates a new <code>Loader</code> instance, with the parent
+ * class loader associated with the top object on the stack (which must be
+ * a <code>Container</code>), and pushes it on to the stack.
+ */
+
+final class CreateLoaderRule extends Rule {
+
+    public CreateLoaderRule(Digester digester, String loaderClass,
+                            String attributeName) {
+
+        super(digester);
+        this.loaderClass = loaderClass;
+        this.attributeName = attributeName;
+
+    }
+
+    private String attributeName;
+
+    private String loaderClass;
+
+    public void begin(Attributes attributes) throws Exception {
+
+        // Look up the required parent class loader
+        Container container = (Container) digester.peek();
+        ClassLoader parentClassLoader = container.getParentClassLoader();
+
+        // Instantiate a new Loader implementation object
+        String className = loaderClass;
+        if (attributeName != null) {
+            String value = attributes.getValue(attributeName);
+            if (value != null)
+                className = value;
+        }
+        Class<?> clazz = Class.forName(className);
+        Class types[] = { ClassLoader.class };
+        Object args[] = { parentClassLoader };
+        Constructor constructor = clazz.getDeclaredConstructor(types);
+        Loader loader = (Loader) constructor.newInstance(args);
+
+        // Push the new loader onto the stack
+        digester.push(loader);
+        if (digester.getDebug() >= 1)
+            digester.log("new " + loader.getClass().getName());
+
+    }
+
+    public void end() throws Exception {
+
+        Loader loader = (Loader) digester.pop();
+        if (digester.getDebug() >= 1)
+            digester.log("pop " + loader.getClass().getName());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java
new file mode 100644
index 0000000..8e3f48a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.Container;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * <p>Rule that copies the <code>parentClassLoader</code> property from the
+ * next-to-top item on the stack (which must be a <code>Container</code>)
+ * to the top item on the stack (which must also be a
+ * <code>Container</code>).</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:06 $
+ */
+
+public class CopyParentClassLoaderRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this Rule.
+     *
+     * @param digester Digester we are associated with
+     */
+    public CopyParentClassLoaderRule(Digester digester) {
+
+        super(digester);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        if (digester.getDebug() >= 1)
+            digester.log("Copying parent class loader");
+        Container child = (Container) digester.peek(0);
+        Object parent = digester.peek(1);
+        Method method =
+            parent.getClass().getMethod("getParentClassLoader", new Class[0]);
+        ClassLoader classLoader =
+            (ClassLoader) method.invoke(parent, new Object[0]);
+        child.setParentClassLoader(classLoader);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/DigesterFactory.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/DigesterFactory.java
new file mode 100644
index 0000000..2236f94
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/DigesterFactory.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import com.sun.logging.LogDomains;
+import org.apache.catalina.LogFacade;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSet;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.jvnet.hk2.annotations.Optional;
+import org.jvnet.hk2.annotations.Service;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.net.URL;
+import java.util.logging.Logger;
+
+/**
+ * Wrapper class around the Digester that hide Digester's initialization details
+ *
+ * @author Jean-Francois Arcand
+ */
+@Service
+public class DigesterFactory {
+
+    /**
+     * Used to resolve DTDs and XML Schemas of JavaEE.
+     */
+    @Inject
+    @Named("web")
+    @Optional
+    EntityResolver entityResolver;
+
+    /**
+     * Create a <code>Digester</code> parser with no <code>Rule</code>
+     * associated and XML validation turned off.
+     */
+    public Digester newDigester(){
+        return newDigester(false, false, null);
+    }
+
+    
+    /**
+     * Create a <code>Digester</code> parser with XML validation turned off.
+     * @param rule an instance of <code>Rule</code> used for parsing the xml.
+     */
+    public Digester newDigester(RuleSet rule){
+        return newDigester(false,false,rule);
+    }
+
+    /**
+     * Create a <code>Digester</code> parser.
+     * @param xmlValidation turn on/off xml validation
+     * @param xmlNamespaceAware turn on/off namespace validation
+     * @param rule an instance of <code>Rule</code??? used for parsing the xml.
+     */
+    public Digester newDigester(boolean xmlValidation,
+                                boolean xmlNamespaceAware,
+                                RuleSet rule) {
+
+        Digester digester = new Digester();
+        digester.setNamespaceAware(xmlNamespaceAware);
+        digester.setValidating(xmlValidation);
+        digester.setUseContextClassLoader(true);
+        
+        String parserName = 
+                digester.getFactory().getClass().getName();
+        if (parserName.indexOf("xerces")!=-1) {
+            digester = patchXerces(digester);
+        }
+
+        if (xmlValidation) {
+            // Xerces 2.3 and up has a special way to turn on validation
+            // for both DTD and Schema
+            if (parserName.indexOf("xerces")!=-1) {
+                turnOnXercesValidation(digester);
+            } else {
+                turnOnValidation(digester);
+            }
+        }
+
+        digester.setEntityResolver(entityResolver);
+        if ( rule != null )
+            digester.addRuleSet(rule);
+
+        return (digester);
+    }
+
+
+    /**
+     * Patch Xerces for backward compatibility.
+     */
+    private Digester patchXerces(Digester digester){
+        // This feature is needed for backward compatibility with old DDs
+        // which used Java encoding names such as ISO8859_1 etc.
+        // with Crimson (bug 4701993). By default, Xerces does not
+        // support ISO8859_1.
+        try{
+            digester.setFeature(
+                "http://apache.org/xml/features/allow-java-encodings", true);
+        } catch(ParserConfigurationException e){
+                // log("contextConfig.registerLocalSchema", e);
+        } catch(SAXNotRecognizedException e){
+                // log("contextConfig.registerLocalSchema", e);
+        } catch(SAXNotSupportedException e){
+                // log("contextConfig.registerLocalSchema", e);
+        }
+        return digester;
+    }
+
+    /**
+     * Turn on DTD and/or validation (based on the parser implementation)
+     */
+    protected void turnOnValidation(Digester digester){
+        URL url = DigesterFactory.class.getResource(Constants.WebSchemaResourcePath_24);
+        digester.setSchema(url.toString());
+    }
+
+
+    /** 
+     * Turn on schema AND DTD validation on Xerces parser.
+     */
+    protected void turnOnXercesValidation(Digester digester){
+        try{
+            digester.setFeature(
+                "http://apache.org/xml/features/validation/dynamic",
+                true);
+            digester.setFeature(
+                "http://apache.org/xml/features/validation/schema",
+                true);
+        } catch(ParserConfigurationException e){
+            // log("contextConfig.registerLocalSchema", e);
+        } catch(SAXNotRecognizedException e){
+            // log("contextConfig.registerLocalSchema", e);
+        } catch(SAXNotSupportedException e){
+            // log("contextConfig.registerLocalSchema", e);
+        }
+    }
+
+    protected static final Logger _logger = LogFacade.getLogger();
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Embedded.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Embedded.java
new file mode 100644
index 0000000..a4708aa
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Embedded.java
@@ -0,0 +1,1089 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.core.*;
+import org.glassfish.web.util.IntrospectionUtils;
+import org.apache.catalina.*;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.net.ServerSocketFactory;
+import org.apache.catalina.security.SecurityConfig;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.ServerInfo;
+import org.glassfish.web.valve.GlassFishValve;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Convenience class to embed a Catalina servlet container environment
+ * inside another application.  You must call the methods of this class in the
+ * following order to ensure correct operation.
+ *
+ * <ul>
+ * <li>Instantiate a new instance of this class.</li>
+ * <li>Set the relevant properties of this object itself.  In particular,
+ *     you will want to establish the default Logger to be used, as well
+ *     as the default Realm if you are using container-managed security.</li>
+ * <li>Call <code>createEngine()</code> to create an Engine object, and then
+ *     call its property setters as desired.</li>
+ * <li>Call <code>createHost()</code> to create at least one virtual Host
+ *     associated with the newly created Engine, and then call its property
+ *     setters as desired.  After you customize this Host, add it to the
+ *     corresponding Engine with <code>engine.addChild(host)</code>.</li>
+ * <li>Call <code>createContext()</code> to create at least one Context
+ *     associated with each newly created Host, and then call its property
+ *     setters as desired.  You <strong>SHOULD</strong> create a Context with
+ *     a pathname equal to a zero-length string, which will be used to process
+ *     all requests not mapped to some other Context.  After you customize
+ *     this Context, add it to the corresponding Host with
+ *     <code>host.addChild(context)</code>.</li>
+ * <li>Call <code>addEngine()</code> to attach this Engine to the set of
+ *     defined Engines for this object.</li>
+ * <li>Call <code>createConnector()</code> to create at least one TCP/IP
+ *     connector, and then call its property setters as desired.</li>
+ * <li>Call <code>addConnector()</code> to attach this Connector to the set
+ *     of defined Connectors for this object.  The added Connector will use
+ *     the most recently added Engine to process its received requests.</li>
+ * <li>Repeat the above series of steps as often as required (although there
+ *     will typically be only one Engine instance created).</li>
+ * <li>Call <code>start()</code> to initiate normal operations of all the
+ *     attached components.</li>
+ * </ul>
+ *
+ * After normal operations have begun, you can add and remove Connectors,
+ * Engines, Hosts, and Contexts on the fly.  However, once you have removed
+ * a particular component, it must be thrown away -- you can create a new one
+ * with the same characteristics if you merely want to do a restart.
+ * <p>
+ * To initiate a normal shutdown, call the <code>stop()</code> method of
+ * this object.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong>:  The <code>main()</code> method of
+ * this class is a simple example that exercizes the features of dynamically
+ * starting and stopping various components.  You can execute this by executing
+ * the following steps (on a Unix platform):
+ * <pre>
+ *   cd $CATALINA_HOME
+ *   ./bin/catalina.sh embedded
+ * </pre>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.12 $ $Date: 2007/03/29 00:59:41 $
+ */
+
+public class Embedded  extends StandardService {
+
+    protected static final Logger log = LogFacade.getLogger();
+    protected static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class with default properties.
+     */
+    public Embedded() {
+
+        this(null, null);
+
+    }
+
+
+    /**
+     * Construct a new instance of this class with specified properties.
+     *
+     * @param logger Logger implementation to be inherited by all components
+     *  (unless overridden further down the container hierarchy)
+     * @param realm Realm implementation to be inherited by all components
+     *  (unless overridden further down the container hierarchy)
+     */
+    public Embedded(org.apache.catalina.Logger logger, Realm realm) {
+
+        super();
+        setLogger(logger);
+        setRealm(realm);
+        setSecurityProtection();
+        
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Is naming enabled ?
+     */
+    protected boolean useNaming = true;
+
+
+    /**
+     * The set of Engines that have been deployed in this server.  Normally
+     * there will only be one.
+     */
+    protected Engine engines[] = new Engine[0];
+
+
+    /**
+     * Custom mappings of login methods to authenticators
+     */
+    protected HashMap<String, Authenticator> authenticators;
+
+
+    /**
+     * Descriptive information about this server implementation.
+     */
+    protected static final String info =
+        "org.apache.catalina.startup.Embedded/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The default logger to be used by this component itself.  Unless this
+     * is overridden, log messages will be writted to standard output.
+     */
+    protected org.apache.catalina.Logger logger = null;
+
+
+    /**
+     * The default realm to be used by all containers associated with
+     * this compoennt.
+     */
+    protected Realm realm = null;
+
+    /**
+     * The socket factory that will be used when a <code>secure</code>
+     * Connector is created.  If a standard Connector is created, the
+     * internal (to the Connector class default socket factory class)
+     * will be used instead.
+     */
+    protected String socketFactory =
+        "org.apache.catalina.net.SSLSocketFactory";
+
+
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+    /**
+     * Use await.
+     */
+    protected boolean await = false;
+
+    protected boolean embeddedDirectoryListing = false;
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return true if naming is enabled.
+     */
+    public boolean isUseNaming() {
+
+        return (this.useNaming);
+
+    }
+
+
+    /**
+     * Enables or disables naming support.
+     *
+     * @param useNaming The new use naming value
+     */
+    public void setUseNaming(boolean useNaming) {
+
+        boolean oldUseNaming = this.useNaming;
+        this.useNaming = useNaming;
+        support.firePropertyChange("useNaming", Boolean.valueOf(oldUseNaming),
+                                   Boolean.valueOf(this.useNaming));
+
+    }
+
+
+    /**
+     * Return the Logger for this component.
+     */
+    public org.apache.catalina.Logger getLogger() {
+        return (this.logger);
+    }
+
+
+    /**
+     * Set the Logger for this component.
+     *
+     * @param logger The new logger
+     */
+    public void setLogger(org.apache.catalina.Logger logger) {
+        org.apache.catalina.Logger oldLogger = this.logger;
+        this.logger = logger;
+        support.firePropertyChange("logger", oldLogger, this.logger);
+    }
+
+
+    /**
+     * Return the default Realm for our Containers.
+     */
+    public Realm getRealm() {
+        return (this.realm);
+    }
+
+
+    /**
+     * Set the default Realm for our Containers.
+     *
+     * @param realm The new default realm
+     */
+    public void setRealm(Realm realm) {
+
+        Realm oldRealm = this.realm;
+        this.realm = realm;
+        support.firePropertyChange("realm", oldRealm, this.realm);
+
+    }
+
+
+    /**
+     * Return the secure socket factory class name.
+     */
+    public String getSocketFactory() {
+
+        return (this.socketFactory);
+
+    }
+
+
+    /**
+     * Set the secure socket factory class name.
+     *
+     * @param socketFactory The new secure socket factory class name
+     */
+    public void setSocketFactory(String socketFactory) {
+
+        this.socketFactory = socketFactory;
+
+    }
+
+    public void setAwait(boolean b) {
+        await = b;
+    }
+
+    public boolean isAwait() {
+        return await;
+    }
+
+    public void setCatalinaHome( String s ) {
+        System.setProperty( "catalina.home", s);
+    }
+
+    public void setCatalinaBase( String s ) {
+        System.setProperty( "catalina.base", s);
+    }
+
+    public String getCatalinaHome() {
+        return System.getProperty("catalina.home");
+    }
+
+    public String getCatalinaBase() {
+        return System.getProperty("catalina.base");
+    }
+
+    public void setDirectoryListing(boolean listings) {
+        embeddedDirectoryListing = listings;
+    }
+
+    public boolean isDirectoryListing() {
+        return embeddedDirectoryListing;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Add a new Connector to the set of defined Connectors.  The newly
+     * added Connector will be associated with the most recently added Engine.
+     *
+     * @param connector The connector to be added
+     *
+     * @exception IllegalStateException if no engines have been added yet
+     */
+    public synchronized void addConnector(Connector connector) {
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Adding connector (" + connector.getInfo() + ")");
+        }
+
+        // Make sure we have a Container to send requests to
+        if (engines.length < 1)
+            throw new IllegalStateException
+                (rb.getString(LogFacade.NO_ENGINES_DEFINED));
+
+        /*
+         * Add the connector. This will set the connector's container to the
+         * most recently added Engine
+         */
+        super.addConnector(connector);
+    }
+
+
+    /**
+     * Add a new Engine to the set of defined Engines.
+     *
+     * @param engine The engine to be added
+     */
+    public synchronized void addEngine(Engine engine) {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Adding engine (" + engine.getInfo() + ")");
+
+        // Add this Engine to our set of defined Engines
+        Engine results[] = new Engine[engines.length + 1];
+        for (int i = 0; i < engines.length; i++)
+            results[i] = engines[i];
+        results[engines.length] = engine;
+        engines = results;
+
+        // Start this Engine if necessary
+        if (started && (engine instanceof Lifecycle)) {
+            try {
+                ((Lifecycle) engine).start();
+            } catch (LifecycleException e) {
+                log.log(Level.SEVERE, LogFacade.ENGINE_START_EXCEPTION, e);
+            }
+        }
+
+        this.container = engine;
+    }
+
+
+    public Engine[] getEngines() {
+        return engines;
+    }
+    
+    
+    /**
+     * Create, configure, and return a new TCP/IP socket connector
+     * based on the specified properties.
+     *
+     * @param address InetAddress to bind to, or <code>null</code> if the
+     * connector is supposed to bind to all addresses on this server
+     * @param port Port number to listen to
+     * @param secure true if the generated connector is supposed to be
+     * SSL-enabled, and false otherwise
+     */
+    public Connector createConnector(InetAddress address, int port,
+                                     boolean secure) {
+	return createConnector(address != null? address.toString() : null,
+			       port, secure);
+    }
+
+    public Connector createConnector(String address, int port,
+                                     boolean secure) {
+        String protocol = "http";
+        if (secure) {
+            protocol = "https";
+        }
+
+        return createConnector(address, port, protocol);
+    }
+
+
+    public Connector createConnector(InetAddress address, int port,
+                                     String protocol) {
+	return createConnector(address != null? address.toString() : null,
+			       port, protocol);
+    }
+
+    public Connector createConnector(String address, int port,
+				     String protocol) {
+
+        Connector connector = null;
+
+	if (address != null) {
+	    /*
+	     * InetAddress.toString() returns a string of the form
+	     * "<hostname>/<literal_IP>". Get the latter part, so that the
+	     * address can be parsed (back) into an InetAddress using
+	     * InetAddress.getByName().
+	     */
+	    int index = address.indexOf('/');
+	    if (index != -1) {
+		address = address.substring(index + 1);
+	    }
+	}
+
+	if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Creating connector for address='" +
+                    ((address == null) ? "ALL" : address) +
+                    "' port='" + port + "' protocol='" + protocol + "'");
+	}
+
+        try {
+
+            Class clazz = 
+                Class.forName("org.apache.catalina.connector.Connector");
+            connector = (Connector) clazz.newInstance();
+
+            if (address != null) {
+                IntrospectionUtils.setProperty(connector, "address", 
+                                               "" + address);
+            }
+            IntrospectionUtils.setProperty(connector, "port", "" + port);
+
+            if (protocol.equals("ajp")) {
+                IntrospectionUtils.setProperty
+                    (connector, "protocolHandlerClassName",
+                     "org.apache.jk.server.JkCoyoteHandler");
+            } else if (protocol.equals("https")) {
+                connector.setScheme("https");
+                connector.setSecure(true);
+                try {
+                    Class serverSocketFactoryClass = Class.forName
+                       ("org.apache.catalina.connector.CoyoteServerSocketFactory");
+                    ServerSocketFactory factory = 
+                        (ServerSocketFactory) 
+                        serverSocketFactoryClass.newInstance();
+                    connector.setFactory(factory);
+                } catch (Exception e) {
+                    log.log(Level.SEVERE, LogFacade.COULD_NOT_LOAD_SSL_SERVER_SOCKET_FACTORY_EXCEPTION);
+                }
+            }
+
+        } catch (Exception e) {
+            log.log(Level.SEVERE, LogFacade.COULD_NOT_CREATE_CONNECTOR_EXCEPTION);
+        }
+
+        return (connector);
+
+    }
+
+    /**
+     * Create, configure, and return a Context that will process all
+     * HTTP requests received from one of the associated Connectors,
+     * and directed to the specified context path on the virtual host
+     * to which this Context is connected.
+     * <p>
+     * After you have customized the properties, listeners, and Valves
+     * for this Context, you must attach it to the corresponding Host
+     * by calling:
+     * <pre>
+     *   host.addChild(context);
+     * </pre>
+     * which will also cause the Context to be started if the Host has
+     * already been started.
+     *
+     * @param path Context path of this application ("" for the default
+     *  application for this host, must start with a slash otherwise)
+     * @param docBase Absolute pathname to the document base directory
+     *  for this web application
+     *
+     * @exception IllegalArgumentException if an invalid parameter
+     *  is specified
+     */
+    public Context createContext(String path, String docBase) {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Creating context '" + path + "' with docBase '" +
+                    docBase + "'");
+
+        StandardContext context = new StandardContext();
+
+        context.setDebug(debug);
+        context.setDocBase(docBase);
+        context.setPath(path);
+        
+        ContextConfig config = new ContextConfig();
+        config.setCustomAuthenticators(authenticators);
+        config.setDebug(debug);
+        ((Lifecycle) context).addLifecycleListener(config);
+
+        return (context);
+
+    }
+
+
+    /**
+     * Create, configure, and return an Engine that will process all
+     * HTTP requests received from one of the associated Connectors,
+     * based on the specified properties.
+     */
+    public Engine createEngine() {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Creating engine");
+
+        StandardEngine engine = new StandardEngine();
+
+        engine.setDebug(debug);
+        // Default host will be set to the first host added
+        engine.setLogger(logger);       // Inherited by all children
+        engine.setRealm(realm);         // Inherited by all children
+
+        return (engine);
+
+    }
+
+
+    /**
+     * Create, configure, and return a Host that will process all
+     * HTTP requests received from one of the associated Connectors,
+     * and directed to the specified virtual host.
+     * <p>
+     * After you have customized the properties, listeners, and Valves
+     * for this Host, you must attach it to the corresponding Engine
+     * by calling:
+     * <pre>
+     *   engine.addChild(host);
+     * </pre>
+     * which will also cause the Host to be started if the Engine has
+     * already been started.  If this is the default (or only) Host you
+     * will be defining, you may also tell the Engine to pass all requests
+     * not assigned to another virtual host to this one:
+     * <pre>
+     *   engine.setDefaultHost(host.getName());
+     * </pre>
+     *
+     * @param name Canonical name of this virtual host
+     * @param appBase Absolute pathname to the application base directory
+     *  for this virtual host
+     *
+     * @exception IllegalArgumentException if an invalid parameter
+     *  is specified
+     */
+    public Host createHost(String name, String appBase) {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Creating host '" + name + "' with appBase '" +
+                    appBase + "'");
+
+        StandardHost host = new StandardHost();
+
+        host.setAppBase(appBase);
+        host.setDebug(debug);
+        host.setName(name);
+
+        return (host);
+
+    }
+
+
+    /**
+     * Create and return a class loader manager that can be customized, and
+     * then attached to a Context, before it is started.
+     *
+     * @param parent ClassLoader that will be the parent of the one
+     *  created by this Loader
+     */
+    public Loader createLoader(ClassLoader parent) {
+
+        if (log.isLoggable(Level.FINEST))
+            log.log(Level.FINEST, "Creating Loader with parent class loader '" +
+                    parent + "'");
+
+        WebappLoader loader = new WebappLoader(parent);
+        return (loader);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Server implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    /**
+     * Remove the specified Context from the set of defined Contexts for its
+     * associated Host.  If this is the last Context for this Host, the Host
+     * will also be removed.
+     *
+     * @param context The Context to be removed
+     */
+    public synchronized void removeContext(Context context) {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Removing context[" + context.getPath() + "]");
+
+        // Is this Context actually among those that are defined?
+        boolean found = false;
+        for (int i = 0; i < engines.length; i++) {
+            Container hosts[] = engines[i].findChildren();
+            for (int j = 0; j < hosts.length; j++) {
+                Container contexts[] = hosts[j].findChildren();
+                for (int k = 0; k < contexts.length; k++) {
+                    if (context == (Context) contexts[k]) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (found)
+                    break;
+            }
+            if (found)
+                break;
+        }
+        if (!found)
+            return;
+
+        // Remove this Context from the associated Host
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, " Removing this Context");
+        context.getParent().removeChild(context);
+
+    }
+
+
+    /**
+     * Remove the specified Engine from the set of defined Engines, along with
+     * all of its related Hosts and Contexts.  All associated Connectors are
+     * also removed.
+     *
+     * @param engine The Engine to be removed
+     */
+    public synchronized void removeEngine(Engine engine) {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Removing engine (" + engine.getInfo() + ")");
+
+        // Is the specified Engine actually defined?
+        int j = -1;
+        for (int i = 0; i < engines.length; i++) {
+            if (engine == engines[i]) {
+                j = i;
+                break;
+            }
+        }
+        if (j < 0)
+            return;
+
+        // Remove any Connector that is using this Engine
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, " Removing related Containers");
+        while (true) {
+            int n = -1;
+            for (int i = 0; i < connectors.length; i++) {
+                if (connectors[i].getContainer() == (Container) engine) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                break;
+            // START SJSAS 6231069
+            //removeConnector(connectors[n]);
+            try{
+                removeConnector(connectors[n]);
+            } catch (Exception ex){
+                log.log(Level.SEVERE, LogFacade.CONNECTOR_STOP_EXCEPTION, ex);
+            }
+            // END SJSAS 6231069
+        }
+
+        // Stop this Engine if necessary
+        if (engine instanceof Lifecycle) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, " Stopping this Engine");
+            try {
+                ((Lifecycle) engine).stop();
+            } catch (LifecycleException e) {
+                log.log(Level.SEVERE,LogFacade. ENGINE_STOP_EXCEPTION, e);
+            }
+        }
+
+        // Remove this Engine from our set of defined Engines
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, " Removing this Engine");
+        int k = 0;
+        Engine results[] = new Engine[engines.length - 1];
+        for (int i = 0; i < engines.length; i++) {
+            if (i != j)
+                results[k++] = engines[i];
+        }
+        engines = results;
+
+    }
+
+
+    /**
+     * Remove the specified Host, along with all of its related Contexts,
+     * from the set of defined Hosts for its associated Engine.  If this is
+     * the last Host for this Engine, the Engine will also be removed.
+     *
+     * @param host The Host to be removed
+     */
+    public synchronized void removeHost(Host host) {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Removing host[" + host.getName() + "]");
+
+        // Is this Host actually among those that are defined?
+        boolean found = false;
+        for (int i = 0; i < engines.length; i++) {
+            Container hosts[] = engines[i].findChildren();
+            for (int j = 0; j < hosts.length; j++) {
+                if (host == (Host) hosts[j]) {
+                    found = true;
+                    break;
+
+                }
+            }
+            if (found)
+                break;
+        }
+        if (!found)
+            return;
+
+        // Remove this Host from the associated Engine
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, " Removing this Host");
+        host.getParent().removeChild(host);
+
+    }
+
+
+    // START PWC 6392537
+    /*
+     * Maps the specified login method to the specified authenticator, allowing
+     * the mappings in org/apache/catalina/startup/Authenticators.properties
+     * to be overridden.
+     *
+     * <p>If <code>authenticator</code> is null, the associated login method
+     * will be disabled.
+     *
+     * @param authenticator Authenticator to handle authentication for the
+     * specified login method, or <code>null</code> if the specified login
+     * method is to be disabled
+     * @param loginMethod Login method that maps to the specified authenticator
+     *
+     * @throws IllegalArgumentException if the specified authenticator is not
+     * null and does not implement the org.apache.catalina.Valve interface
+     */
+    public synchronized void addAuthenticator(Authenticator authenticator,
+                                 String loginMethod) {
+        if ((authenticator != null) && !(authenticator instanceof GlassFishValve)) {
+            throw new IllegalArgumentException(rb.getString(LogFacade.AUTH_IS_NOT_VALVE_EXCEPTION));
+        }
+        if (authenticators == null) {
+            authenticators = new HashMap<String, Authenticator>();
+        }
+        authenticators.put(loginMethod, authenticator);
+    }
+    // END PWC 6392537
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this Embedded instance.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    @Override
+    public void start() throws LifecycleException {
+
+        /* SJSAS 5022949
+        if( log.isInfoEnabled() )
+            log.info("Starting tomcat server");
+         */
+        // START SJSAS 6340446
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "Starting Servlet container component of "
+                    + ServerInfo.getServerInfo());
+        }
+        // END SJSAS 6340446
+
+        // Validate the setup of our required system properties
+        initDirs();
+
+        // Initialize some naming specific properties
+        initNaming();
+
+        // Validate and update our current component state
+        if (started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.SERVICE_BEEN_STARTED_EXCEPTION));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+        initialized = true;
+
+        // Start our defined Connectors first
+        for (int i = 0; i < connectors.length; i++) {
+            connectors[i].initialize();
+            if (connectors[i] instanceof Lifecycle)
+                ((Lifecycle) connectors[i]).start();
+        }
+
+        // Start our defined Engines second
+        for (int i = 0; i < engines.length; i++) {
+            if (engines[i] instanceof Lifecycle)
+                ((Lifecycle) engines[i]).start();
+        }
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    @Override
+    public void stop() throws LifecycleException {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "Stopping embedded server");
+
+        // Validate and update our current component state
+        if (!started)
+            throw new LifecycleException
+                (rb.getString(LogFacade.SERVICE_NOT_BEEN_STARTED_EXCEPTION));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        // Stop our defined Connectors first
+        for (int i = 0; i < connectors.length; i++) {
+            if (connectors[i] instanceof Lifecycle)
+                ((Lifecycle) connectors[i]).stop();
+        }
+
+        // Stop our defined Engines second
+        for (int i = 0; i < engines.length; i++) {
+            if (engines[i] instanceof Lifecycle)
+                ((Lifecycle) engines[i]).stop();
+        }
+
+    }
+
+    @Override
+    public void destroy() throws LifecycleException {
+        if( started ) stop();
+        if (initialized) {
+            initialized = false;
+        }
+    }
+
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /** Initialize naming - this should only enable java:env and root naming.
+     * If tomcat is embeded in an application that already defines those -
+     * it shouldn't do it.
+     *
+     * XXX The 2 should be separated, you may want to enable java: but not
+     * the initial context and the reverse
+     * XXX Can we "guess" - i.e. lookup java: and if something is returned assume
+     * false ?
+     * XXX We have a major problem with the current setting for java: url
+     */
+    protected void initNaming() {
+        // Setting additional variables
+        if (!useNaming) {
+            // START SJSAS 5031700
+            //log.info( "Catalina naming disabled");
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Catalina naming disabled");
+            }
+            // END SJSAS 5031700
+            System.setProperty("catalina.useNaming", "false");
+        } else {
+            System.setProperty("catalina.useNaming", "true");
+            String value = "org.apache.naming";
+            String oldValue =
+                System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
+            if (oldValue != null) {
+                value = value + ":" + oldValue;
+            }
+            System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "Setting naming prefix=" + value);
+            value = System.getProperty
+                (javax.naming.Context.INITIAL_CONTEXT_FACTORY);
+            if (value == null) {
+                System.setProperty
+                    (javax.naming.Context.INITIAL_CONTEXT_FACTORY,
+                     "org.apache.naming.java.javaURLContextFactory");
+            } else {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, "INITIAL_CONTEXT_FACTORY alread set " + value);
+                }
+            }
+        }
+    }
+
+
+    protected void initDirs() {
+
+        String catalinaHome = System.getProperty("catalina.home");
+        if (catalinaHome == null) {
+            // Backwards compatibility patch for J2EE RI 1.3
+            String j2eeHome = System.getProperty("com.sun.enterprise.home");
+            if (j2eeHome != null) {
+                catalinaHome=System.getProperty("com.sun.enterprise.home");
+            } else if (System.getProperty("catalina.base") != null) {
+                catalinaHome = System.getProperty("catalina.base");
+            } else {
+                // Use IntrospectionUtils and guess the dir
+                catalinaHome = IntrospectionUtils.guessInstall
+                    ("catalina.home", "catalina.base", "catalina.jar");
+                if (catalinaHome == null) {
+                    catalinaHome = IntrospectionUtils.guessInstall
+                        ("tomcat.install", "catalina.home", "tomcat.jar");
+                }
+            }
+        }
+        // last resort - for minimal/embedded cases. 
+        if(catalinaHome==null) {
+            catalinaHome=System.getProperty("user.dir");
+        }
+        if (catalinaHome != null) {
+            File home = new File(catalinaHome);
+            if (!home.isAbsolute()) {
+                try {
+                    catalinaHome = home.getCanonicalPath();
+                } catch (IOException e) {
+                    catalinaHome = home.getAbsolutePath();
+                }
+            }
+            System.setProperty("catalina.home", catalinaHome);
+        }
+
+        if (System.getProperty("catalina.base") == null) {
+            System.setProperty("catalina.base",
+                               catalinaHome);
+        } else {
+            String catalinaBase = System.getProperty("catalina.base");
+            File base = new File(catalinaBase);
+            if (!base.isAbsolute()) {
+                try {
+                    catalinaBase = base.getCanonicalPath();
+                } catch (IOException e) {
+                    catalinaBase = base.getAbsolutePath();
+                }
+            }
+            System.setProperty("catalina.base", catalinaBase);
+        } 
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+    /**
+     * Customize the specified context to have its own log file instead of
+     * inheriting the default one.  This is just an example of what you can
+     * do; pretty much anything (such as installing special Valves) can
+     * be done prior to calling <code>start()</code>.
+     *
+     * @param context Context to receive a specialized logger
+     *
+    private static void customize(Context context) {
+
+        // Create a customized file logger for this context
+        String basename = context.getPath();
+        if (basename.length() < 1)
+            basename = "ROOT";
+        else
+            basename = basename.substring(1);
+
+        FileLogger special = new FileLogger();
+        special.setPrefix(basename + "_log.");
+        special.setSuffix(".txt");
+        special.setTimestamp(true);
+
+        // Override the default logger for this context
+        context.setLogger(special);
+
+    }
+    */
+
+    /**
+     * Set the security package access/protection.
+     */
+    protected void setSecurityProtection(){
+        if (System.getSecurityManager() != null) {
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                @Override
+                public Void run() {
+                    SecurityConfig securityConfig = SecurityConfig.newInstance();
+                    securityConfig.setPackageDefinition();
+                    securityConfig.setPackageAccess();
+                    return null;
+                }
+            });
+        }
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/EngineConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/EngineConfig.java
new file mode 100644
index 0000000..167328f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/EngineConfig.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.Logger;
+import org.apache.catalina.core.StandardEngine;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+
+
+/**
+ * Startup event listener for a <b>Engine</b> that configures the properties
+ * of that Engine, and the associated defined contexts.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:07 $
+ */
+
+public final class EngineConfig
+    implements LifecycleListener {
+
+    // ----------------------------------------------------- Static Variables
+
+    private static final java.util.logging.Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The Engine we are associated with.
+     */
+    private Engine engine = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+        return (this.debug);
+    }
+
+
+    /**
+     * Set the debugging detail level for this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+        this.debug = debug;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the START event for an associated Engine.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        // Identify the engine we are associated with
+        try {
+            engine = (Engine) event.getLifecycle();
+            if (engine instanceof StandardEngine) {
+                int engineDebug = ((StandardEngine) engine).getDebug();
+                if (engineDebug > this.debug)
+                    this.debug = engineDebug;
+            }
+        } catch (ClassCastException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.LIFECYCLE_EVENT_DATA_IS_NOT_ENGINE_EXCEPTION),
+                                              event.getLifecycle());
+            log(msg, e);
+            return;
+        }
+
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.START_EVENT))
+            start();
+        else if (event.getType().equals(Lifecycle.STOP_EVENT))
+            stop();
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Log a message on the Logger associated with our Engine (if any)
+     *
+     * @param message Message to be logged
+     */
+    private void log(String message) {
+        Logger logger = null;
+        if (engine != null) {
+            logger = engine.getLogger();
+        }
+        if (logger != null) {
+            logger.log("EngineConfig: " + message);
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.ENGINE_CONFIG, message);
+            }
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Engine (if any)
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    private void log(String message, Throwable t) {
+        Logger logger = null;
+        if (engine != null) {
+            logger = engine.getLogger();
+        }
+        if (logger != null) {
+            logger.log("EngineConfig: " + message, t, Logger.WARNING);
+        } else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ENGINE_CONFIG),
+                                              message);
+            log.log(Level.WARNING, msg, t);
+        }
+    }
+
+
+    /**
+     * Process a "start" event for this Engine.
+     */
+    private void start() {
+
+        if (debug > 0)
+            log(rb.getString(LogFacade.ENGINE_CONFIG_PROCESSING_START_INFO));
+
+    }
+
+
+    /**
+     * Process a "stop" event for this Engine.
+     */
+    private void stop() {
+
+        if (debug > 0)
+            log(rb.getString(LogFacade.ENGINE_CONFIG_PROCESSING_STOP_INFO));
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/EngineRuleSet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/EngineRuleSet.java
new file mode 100644
index 0000000..941d0c3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/EngineRuleSet.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a
+ * Engine definition element.  This <code>RuleSet</code> does NOT include
+ * any rules for nested Host or DefaultContext elements, which should
+ * be added via instances of <code>HostRuleSet</code> or
+ * <code>ContextRuleSet</code>, respectively.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:06 $
+ */
+
+public class EngineRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public EngineRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public EngineRuleSet(String prefix) {
+
+        super();
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>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.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        digester.addObjectCreate(prefix + "Engine",
+                                 "org.apache.catalina.core.StandardEngine",
+                                 "className");
+        digester.addSetProperties(prefix + "Engine");
+        digester.addRule(prefix + "Engine",
+                         new LifecycleListenerRule
+                         (digester,
+                          "org.apache.catalina.startup.EngineConfig",
+                          "engineConfigClass"));
+        digester.addSetNext(prefix + "Engine",
+                            "setContainer",
+                            "org.apache.catalina.Container");
+
+        digester.addObjectCreate(prefix + "Engine/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Listener");
+        digester.addSetNext(prefix + "Engine/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addObjectCreate(prefix + "Engine/Logger",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Logger");
+        digester.addSetNext(prefix + "Engine/Logger",
+                            "setLogger",
+                            "org.apache.catalina.Logger");
+
+        digester.addObjectCreate(prefix + "Engine/Realm",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Realm");
+        digester.addSetNext(prefix + "Engine/Realm",
+                            "setRealm",
+                            "org.apache.catalina.Realm");
+
+        digester.addObjectCreate(prefix + "Engine/Valve",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Engine/Valve");
+        digester.addSetNext(prefix + "Engine/Valve",
+                            "addValve",
+                            "org.apache.catalina.Valve");
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ExpandWar.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ExpandWar.java
new file mode 100644
index 0000000..6e3db73
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/ExpandWar.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.Host;
+import org.apache.catalina.LogFacade;
+
+import java.io.*;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.nio.channels.FileChannel;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Expand out a WAR in a Host's appBase.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @author Glenn L. Nielsen
+ * @version $Revision: 1.3 $
+ */
+
+public class ExpandWar {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    /**
+     * Expand the WAR file found at the specified URL into an unpacked
+     * directory structure, and return the absolute pathname to the expanded
+     * directory.
+     *
+     * @param host Host war is being installed for
+     * @param war URL of the web application archive to be expanded
+     *  (must start with "jar:")
+     *
+     * @exception IllegalArgumentException if this is not a "jar:" URL
+     * @exception IOException if an input/output error was encountered
+     *  during expansion
+     */
+    public static String expand(Host host, URL war)
+        throws IOException {
+
+        // Calculate the directory name of the expanded directory
+        String pathname = war.toString().replace('\\', '/');
+        if (pathname.endsWith("!/")) {
+            pathname = pathname.substring(0, pathname.length() - 2);
+        }
+        int period = pathname.lastIndexOf('.');
+        if (period >= pathname.length() - 4)
+            pathname = pathname.substring(0, period);
+        int slash = pathname.lastIndexOf('/');
+        if (slash >= 0) {
+            pathname = pathname.substring(slash + 1);
+        }
+        return expand(host, war, pathname);
+
+    }
+
+
+    /**
+     * Expand the WAR file found at the specified URL into an unpacked
+     * directory structure, and return the absolute pathname to the expanded
+     * directory.
+     *
+     * @param host Host war is being installed for
+     * @param war URL of the web application archive to be expanded
+     *  (must start with "jar:")
+     * @param pathname Context path name for web application
+     *
+     * @exception IllegalArgumentException if this is not a "jar:" URL or if the
+     *            WAR file is invalid
+     * @exception IOException if an input/output error was encountered
+     *  during expansion
+     */
+    public static String expand(Host host, URL war, String pathname)
+        throws IOException {
+
+        // Make sure that there is no such directory already existing
+        File appBase = new File(host.getAppBase());
+        if (!appBase.isAbsolute()) {
+            appBase = new File(System.getProperty("catalina.base"),
+                               host.getAppBase());
+        }
+        if (!appBase.exists() || !appBase.isDirectory()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.APP_NOT_EXIST_EXCEPTION),
+                                              appBase.getAbsolutePath());
+            throw new IOException(msg);
+        }
+        File docBase = new File(appBase, pathname);
+        if (docBase.exists()) {
+            // War file is already installed
+            return (docBase.getAbsolutePath());
+        }
+
+        // Create the new document base directory
+        if (!docBase.mkdir()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_CREATE_DIRECTORY_EXCEPTION),
+                                              docBase);
+            throw new IOException(msg);
+        }
+
+        // Expand the WAR into the new document base directory
+        String canonicalDocBasePrefix = docBase.getCanonicalPath();
+        if (!canonicalDocBasePrefix.endsWith(File.separator)) {
+            canonicalDocBasePrefix += File.separator;
+        }
+        JarURLConnection juc = (JarURLConnection) war.openConnection();
+        juc.setUseCaches(false);
+        JarFile jarFile = null;
+        InputStream input = null;
+        boolean success = false;
+        try {
+            jarFile = juc.getJarFile();
+            Enumeration jarEntries = jarFile.entries();
+            while (jarEntries.hasMoreElements()) {
+                JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
+                String name = jarEntry.getName();
+                File expandedFile = new File(docBase, name);
+                if (!expandedFile.getCanonicalPath().startsWith(
+                        canonicalDocBasePrefix)) {
+                    // Trying to expand outside the docBase
+                    // Throw an exception to stop the deployment
+                    String msg = MessageFormat.format(rb.getString(LogFacade.ARCHIVE_IS_MALFORMED_EXCEPTION),
+                                                      new Object[] {war, name});
+                    throw new IllegalArgumentException(msg);
+                }
+                int last = name.lastIndexOf('/');
+                if (last >= 0) {
+                    File parent = new File(docBase,
+                                           name.substring(0, last));
+                    if (!parent.mkdirs() && !parent.isDirectory()) {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_CREATE_DIRECTORY_EXCEPTION),
+                                                          parent);
+                        throw new IOException(msg);
+                    }
+                }
+                if (name.endsWith("/")) {
+                    continue;
+                }
+                input = jarFile.getInputStream(jarEntry);
+                expand(input, expandedFile);
+                long lastModified = jarEntry.getTime();
+                if ((lastModified != -1) && (lastModified != 0)) {
+                    if (!expandedFile.setLastModified(lastModified)) {
+                        if (log.isLoggable(Level.WARNING)) {
+                            log.log(Level.WARNING, LogFacade.FAILED_SET_LAST_MODIFIED_TIME_EXCEPTION,
+                                    expandedFile.getAbsolutePath());
+                        }
+                    }
+                }
+                input.close();
+                input = null;
+            }
+            success = true;
+        } catch (IOException e) {
+            throw e;
+        } finally {
+            if (!success) {
+                // If something went wrong, delete expanded dir to keep things 
+                // clean
+                deleteDir(docBase);
+            }
+            if (input != null) {
+                try {
+                    input.close();
+                } catch (Throwable t) {
+                    ;
+                }
+                input = null;
+            }
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (Throwable t) {
+                    ;
+                }
+                jarFile = null;
+            }
+        }
+
+        // Return the absolute path to our new document base directory
+        return (docBase.getAbsolutePath());
+
+    }
+
+
+    /**
+     * Validate the WAR file found at the specified URL.
+     *
+     * @param host Host war is being installed for
+     * @param war URL of the web application archive to be validated
+     *  (must start with "jar:")
+     * @param pathname Context path name for web application
+     *
+     * @exception IllegalArgumentException if this is not a "jar:" URL or if the
+     *            WAR file is invalid
+     * @exception IOException if an input/output error was encountered
+     *            during validation
+     */
+    public static void validate(Host host, URL war, String pathname)
+        throws IOException {
+
+        // Make the appBase absolute
+        File appBase = new File(host.getAppBase());
+        if (!appBase.isAbsolute()) {
+            appBase = new File(System.getProperty("catalina.base"),
+                               host.getAppBase());
+        }
+        
+        File docBase = new File(appBase, pathname);
+
+        // Calculate the document base directory
+        String canonicalDocBasePrefix = docBase.getCanonicalPath();
+        if (!canonicalDocBasePrefix.endsWith(File.separator)) {
+            canonicalDocBasePrefix += File.separator;
+        }
+        JarURLConnection juc = (JarURLConnection) war.openConnection();
+        juc.setUseCaches(false);
+        JarFile jarFile = null;
+        try {
+            jarFile = juc.getJarFile();
+            Enumeration<JarEntry> jarEntries = jarFile.entries();
+            while (jarEntries.hasMoreElements()) {
+                JarEntry jarEntry = jarEntries.nextElement();
+                String name = jarEntry.getName();
+                File expandedFile = new File(docBase, name);
+                if (!expandedFile.getCanonicalPath().startsWith(
+                        canonicalDocBasePrefix)) {
+                    // Entry located outside the docBase
+                    // Throw an exception to stop the deployment
+                    String msg = MessageFormat.format(rb.getString(LogFacade.ARCHIVE_IS_MALFORMED_EXCEPTION),
+                                                    new Object[] {war, name});
+                    throw new IllegalArgumentException(msg);
+                }
+            }
+        } catch (IOException e) {
+            throw e;
+        } finally {
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+                jarFile = null;
+            }
+        }
+    }
+
+
+    /**
+     * Copy the specified file or directory to the destination.
+     *
+     * @param src File object representing the source
+     * @param dest File object representing the destination
+     */
+    public static boolean copy(File src, File dest) {
+        
+        boolean result = true;
+        
+        String files[] = null;
+        if (src.isDirectory()) {
+            files = src.list();
+            result = dest.mkdir();
+        } else {
+            files = new String[1];
+            files[0] = "";
+        }
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; (i < files.length) && result; i++) {
+            File fileSrc = new File(src, files[i]);
+            File fileDest = new File(dest, files[i]);
+            if (fileSrc.isDirectory()) {
+                result = copy(fileSrc, fileDest);
+            } else {
+                FileChannel ic = null;
+                FileChannel oc = null;
+                try {
+                    ic = (new FileInputStream(fileSrc)).getChannel();
+                    oc = (new FileOutputStream(fileDest)).getChannel();
+                    ic.transferTo(0, ic.size(), oc);
+                } catch (IOException e) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_COPYING_EXCEPTION),
+                                                      new Object[] {fileSrc, fileDest});
+                    log.log(Level.SEVERE, msg, e);
+                    result = false;
+                } finally {
+                    if (ic != null) {
+                        try {
+                            ic.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                    if (oc != null) {
+                        try {
+                            oc.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                }
+            }
+        }
+        return result;
+        
+    }
+    
+    
+    /**
+     * Delete the specified directory, including all of its contents and
+     * sub-directories recursively. Any failure will be logged.
+     *
+     * @param dir File object representing the directory to be deleted
+     */
+    public static boolean delete(File dir) {
+        // Log failure by default
+        return delete(dir, true);
+    }
+
+    /**
+     * Delete the specified directory, including all of its contents and
+     * sub-directories recursively.
+     *
+     * @param dir File object representing the directory to be deleted
+     * @param logFailure <code>true</code> if failure to delete the resource
+     *                   should be logged
+     */
+    public static boolean delete(File dir, boolean logFailure) {
+        boolean result;
+        if (dir.isDirectory()) {
+            result = deleteDir(dir, logFailure);
+        } else {
+            if (dir.exists()) {
+                result = dir.delete();
+            } else {
+                result = true;
+            }
+        }
+        if (logFailure && !result) {
+            log.log(Level.SEVERE, LogFacade.DELETE_DIR_EXCEPTION, dir.getAbsolutePath());
+        }
+        return result;
+     }
+
+    
+    /**
+     * Delete the specified directory, including all of its contents and
+     * sub-directories recursively. Any failure will be logged.
+     *
+     * @param dir File object representing the directory to be deleted
+     */
+    public static boolean deleteDir(File dir) {
+        return deleteDir(dir, true);
+    }
+
+    /**
+     * Delete the specified directory, including all of its contents and
+     * sub-directories recursively.
+     *
+     * @param dir File object representing the directory to be deleted
+     * @param logFailure <code>true</code> if failure to delete the resource
+     *                   should be logged
+     */
+    public static boolean deleteDir(File dir, boolean logFailure) {
+
+        String files[] = dir.list();
+        if (files == null) {
+            files = new String[0];
+        }
+        for (int i = 0; i < files.length; i++) {
+            File file = new File(dir, files[i]);
+            if (file.isDirectory()) {
+                deleteDir(file, logFailure);
+            } else {
+                if (!file.delete() && logFailure) {
+                    log.log(Level.SEVERE, LogFacade.DELETE_DIR_EXCEPTION, file.getAbsolutePath());
+                }
+            }
+        }
+
+        boolean result;
+        if (dir.exists()) {
+            result = dir.delete();
+        } else {
+            result = true;
+        }
+        
+        if (logFailure && !result) {
+            log.log(Level.SEVERE, LogFacade.DELETE_DIR_EXCEPTION, dir.getAbsolutePath());
+        }
+        
+        return result;
+    }
+
+
+    /**
+     * Expand the specified input stream into the specified directory, creating
+     * a file named from the specified relative path.
+     *
+     * @param input InputStream to be copied
+     * @param docBase Document base directory into which we are expanding
+     * @param name Relative pathname of the file to be created
+     *
+     * @exception IOException if an input/output error occurs
+     *
+     * @deprecated
+     */
+    protected static void expand(InputStream input, File docBase, String name)
+        throws IOException {
+
+        File file = new File(docBase, name);
+        expand(input, file);
+    }
+
+    /**
+     * Expand the specified input stream into the specified file.
+     *
+     * @param input InputStream to be copied
+     * @param file The file to be created
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private static void expand(InputStream input, File file)
+        throws IOException {
+        BufferedOutputStream output = null;
+        try {
+            output = 
+                new BufferedOutputStream(new FileOutputStream(file));
+            byte buffer[] = new byte[2048];
+            while (true) {
+                int n = input.read(buffer);
+                if (n <= 0)
+                    break;
+                output.write(buffer, 0, n);
+            }
+        } finally {
+            if (output != null) {
+                try {
+                    output.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HomesUserDatabase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HomesUserDatabase.java
new file mode 100644
index 0000000..93fbb05
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HomesUserDatabase.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+
+/**
+ * Concrete implementation of the <strong>UserDatabase</code> interface
+ * considers all directories in a directory whose pathname is specified
+ * to our constructor to be "home" directories for those users.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:08 $
+ */
+
+public final class HomesUserDatabase
+    implements UserDatabase {
+
+
+    // --------------------------------------------------------- Constructors
+
+
+    /**
+     * Initialize a new instance of this user database component.
+     */
+    public HomesUserDatabase() {
+
+        super();
+
+    }
+
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of home directories for all defined users, keyed by username.
+     */
+    private Hashtable<String, String> homes = new Hashtable<String, String>();
+
+
+    /**
+     * The UserConfig listener with which we are associated.
+     */
+    private UserConfig userConfig = null;
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the UserConfig listener with which we are associated.
+     */
+    public UserConfig getUserConfig() {
+
+        return (this.userConfig);
+
+    }
+
+
+    /**
+     * Set the UserConfig listener with which we are associated.
+     *
+     * @param userConfig The new UserConfig listener
+     */
+    public void setUserConfig(UserConfig userConfig) {
+
+        this.userConfig = userConfig;
+        init();
+
+    }
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return an absolute pathname to the home directory for the specified user.
+     *
+     * @param user User for which a home directory should be retrieved
+     */
+    public String getHome(String user) {
+
+        return homes.get(user);
+
+    }
+
+
+    /**
+     * Return an enumeration of the usernames defined on this server.
+     */
+    public Enumeration<String> getUsers() {
+
+        return (homes.keys());
+
+    }
+
+
+    // ------------------------------------------------------ Private Methods
+
+
+    /**
+     * Initialize our set of users and home directories.
+     */
+    private void init() {
+
+        String homeBase = userConfig.getHomeBase();
+        File homeBaseDir = new File(homeBase);
+        if (!homeBaseDir.exists() || !homeBaseDir.isDirectory())
+            return;
+        String homeBaseFiles[] = homeBaseDir.list();
+
+        for (int i = 0; i < homeBaseFiles.length; i++) {
+            File homeDir = new File(homeBaseDir, homeBaseFiles[i]);
+            if (!homeDir.isDirectory() || !homeDir.canRead())
+                continue;
+            homes.put(homeBaseFiles[i], homeDir.toString());
+        }
+
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HostConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HostConfig.java
new file mode 100644
index 0000000..bbc8877
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HostConfig.java
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.core.StandardHost;
+import org.apache.naming.resources.ResourceAttributes;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import java.io.*;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Startup event listener for a <b>Host</b> that configures the properties
+ * of that Host, and the associated defined contexts.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.4 $ $Date: 2006/10/03 20:19:13 $
+ */
+
+public class HostConfig
+    implements LifecycleListener {
+    
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * App base.
+     */
+    private File appBase = null;
+
+
+    /**
+     * Config base.
+     */
+    private File configBase = null;
+
+
+    /**
+     * The Java class name of the Context configuration class we should use.
+     */
+    protected String configClass = "org.apache.catalina.startup.ContextConfig";
+
+
+    /**
+     * The Java class name of the Context implementation we should use.
+     */
+    protected String contextClass = "org.apache.catalina.core.StandardContext";
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    protected int debug = 0;
+
+
+    /**
+     * The names of applications that we have auto-deployed (to avoid
+     * double deployment attempts).
+     */
+    protected List<String> deployed = new ArrayList<String>();
+
+
+    /**
+     * The Host we are associated with.
+     */
+    protected Host host = null;
+
+    /**
+     * Should we deploy XML Context config files?
+     */
+    private boolean deployXML = false;
+
+
+    /**
+     * Should we unpack WAR files when auto-deploying applications in the
+     * <code>appBase</code> directory?
+     */
+    private boolean unpackWARs = false;
+
+
+    /**
+     * Last modified dates of the web.xml files of the contexts, keyed by
+     * context name.
+     */
+    private Map<String, Long> webXmlLastModified = new HashMap<String, Long>();
+
+
+    /**
+     * Last modified dates of the Context xml files of the contexts, keyed by
+     * context name.
+     */
+    private Map<String, Long> contextXmlLastModified = new HashMap<String, Long>();
+
+
+    /**
+     * Last modified dates of the source WAR files, keyed by WAR name.
+     */
+    private HashMap<String, Long> warLastModified = new HashMap<String, Long>();
+
+
+    /**
+     * Attribute value used to turn on/off XML validation
+     */
+    private boolean xmlValidation = false;
+
+
+    /**
+     * Attribute value used to turn on/off XML namespace awarenes.
+     */
+    private boolean xmlNamespaceAware = false;
+
+
+    /**
+     * The list of Wars in the appBase to be ignored because they are invalid
+     * (e.g. contain /../ sequences).
+     */
+    protected Set<String> invalidWars = new HashSet<String>();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Context configuration class name.
+     */
+    public String getConfigClass() {
+
+        return (this.configClass);
+
+    }
+
+
+    /**
+     * Set the Context configuration class name.
+     *
+     * @param configClass The new Context configuration class name.
+     */
+    public void setConfigClass(String configClass) {
+
+        this.configClass = configClass;
+
+    }
+
+
+    /**
+     * Return the Context implementation class name.
+     */
+    public String getContextClass() {
+
+        return (this.contextClass);
+
+    }
+
+
+    /**
+     * Set the Context implementation class name.
+     *
+     * @param contextClass The new Context implementation class name.
+     */
+    public void setContextClass(String contextClass) {
+
+        this.contextClass = contextClass;
+
+    }
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (this.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 deploy XML config file flag for this component.
+     */
+    public boolean isDeployXML() {
+
+        return (this.deployXML);
+
+    }
+
+
+    /**
+     * Set the deploy XML config file flag for this component.
+     *
+     * @param deployXML The new deploy XML flag
+     */
+    public void setDeployXML(boolean deployXML) {
+
+        this.deployXML= deployXML;
+
+    }
+
+
+    /**
+     * Return the unpack WARs flag.
+     */
+    public boolean isUnpackWARs() {
+
+        return (this.unpackWARs);
+
+    }
+
+
+    /**
+     * Set the unpack WARs flag.
+     *
+     * @param unpackWARs The new unpack WARs flag
+     */
+    public void setUnpackWARs(boolean unpackWARs) {
+
+        this.unpackWARs = unpackWARs;
+
+    }
+    
+    
+     /**
+     * Set the validation feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlValidation true to enable xml instance validation
+     */
+    public void setXmlValidation(boolean xmlValidation){
+        this.xmlValidation = xmlValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlValidation.
+     * @return true if validation is enabled.
+     *
+     */
+    public boolean getXmlValidation(){
+        return xmlValidation;
+    }
+
+    /**
+     * Get the server.xml <host> attribute's xmlNamespaceAware.
+     * @return true if namespace awarenes is enabled.
+     *
+     */
+    public boolean getXmlNamespaceAware(){
+        return xmlNamespaceAware;
+    }
+
+
+    /**
+     * Set the namespace aware feature of the XML parser used when
+     * parsing xml instances.
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean xmlNamespaceAware){
+        this.xmlNamespaceAware=xmlNamespaceAware;
+    }    
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the START event for an associated Host.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        if (event.getType().equals("check"))
+            check();
+
+        // Identify the host we are associated with
+        try {
+            host = (Host) event.getLifecycle();
+            if (host instanceof StandardHost) {
+                int hostDebug = ((StandardHost) host).getDebug();
+                if (hostDebug > this.debug) {
+                    this.debug = hostDebug;
+                }
+                setDeployXML(((StandardHost) host).isDeployXML());
+                setUnpackWARs(((StandardHost) host).isUnpackWARs());
+                setXmlNamespaceAware(((StandardHost) host).getXmlNamespaceAware());
+                setXmlValidation(((StandardHost) host).getXmlValidation());
+            }
+        } catch (ClassCastException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.LIFECYCLE_OBJECT_NOT_HOST_EXCEPTION),
+                                              event.getLifecycle());
+            log.log(Level.SEVERE, msg, e);
+            return;
+        }
+
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.START_EVENT))
+            start();
+        else if (event.getType().equals(Lifecycle.STOP_EVENT))
+            stop();
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a File object representing the "application root" directory
+     * for our associated Host.
+     */
+    protected File appBase() {
+
+        if (appBase != null) {
+            return appBase;
+        }
+
+        File file = new File(host.getAppBase());
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"),
+                            host.getAppBase());
+        try {
+            appBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            appBase = file;
+        }
+        return (appBase);
+
+    }
+
+
+    /**
+     * Return a File object representing the "configuration root" directory
+     * for our associated Host.
+     */
+    protected File configBase() {
+
+        if (configBase != null) {
+            return configBase;
+        }
+
+        File file = new File(System.getProperty("catalina.base"), "conf");
+        Container parent = host.getParent();
+        if ((parent != null) && (parent instanceof Engine)) {
+            file = new File(file, parent.getName());
+        }
+        file = new File(file, host.getName());
+        try {
+            configBase = file.getCanonicalFile();
+        } catch (IOException e) {
+            configBase = file;
+        }
+        return (configBase);
+
+    }
+
+
+    /**
+     * Deploy applications for any directories or WAR files that are found
+     * in our "application root" directory.
+     */
+    protected void deployApps() {
+
+        if (!(host instanceof Deployer))
+            return;
+
+        File appBase = appBase();
+        if (!appBase.exists() || !appBase.isDirectory())
+            return;
+        File configBase = configBase();
+        if (configBase.exists() && configBase.isDirectory()) {
+            String configFiles[] = configBase.list();
+            deployDescriptors(configBase, configFiles);
+        }
+
+        String files[] = appBase.list();
+        deployWARs(appBase, files);
+        deployDirectories(appBase, files);
+
+    }
+
+
+    /**
+     * Deploy XML context descriptors.
+     */
+    protected void deployDescriptors(File configBase, String[] files) {
+
+        if (!deployXML)
+           return;
+
+        for (int i = 0; i < files.length; i++) {
+
+            if (files[i].equalsIgnoreCase("META-INF"))
+                continue;
+            if (files[i].equalsIgnoreCase("WEB-INF"))
+                continue;
+            if (deployed.contains(files[i]))
+                continue;
+            File dir = new File(configBase, files[i]);
+            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
+
+                deployed.add(files[i]);
+
+                // Calculate the context path and make sure it is unique
+                String file = files[i].substring(0, files[i].length() - 4);
+                String contextPath = "/" + file.replace('_', '/');
+                if (file.equals("ROOT")) {
+                    contextPath = "";
+                }
+
+                // Assume this is a configuration descriptor and deploy it
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, LogFacade.DEPLOYING_CONFIG_DESCRIPTOR, files[i]);
+                }
+                try {
+                    if (host.findChild(contextPath) != null) {
+                        if ((deployed.contains(file))
+                            || (deployed.contains(file + ".war"))) {
+                            // If this is a newly added context file and 
+                            // it overrides a context with a simple path, 
+                            // that was previously deployed by the auto
+                            // deployer, undeploy the context
+                            ((Deployer) host).remove(contextPath);
+                        } else {
+                            continue;
+                        }
+                    }
+                    URL config =
+                        new URL("file", null, dir.getCanonicalPath());
+                    ((Deployer) host).install(config, null);
+                } catch (Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_DEPLOYING_CONFIG_DESCRIPTOR_EXCEPTION),
+                                                      files[i]);
+                    log.log(Level.SEVERE, msg, t);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Deploy WAR files.
+     */
+    protected void deployWARs(File appBase, String[] files) {
+
+        for (int i = 0; i < files.length; i++) {
+
+            if (files[i].equalsIgnoreCase("META-INF"))
+                continue;
+            if (files[i].equalsIgnoreCase("WEB-INF"))
+                continue;
+            if (deployed.contains(files[i]))
+                continue;
+            File dir = new File(appBase, files[i]);
+            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") && dir.isFile()
+                    && !invalidWars.contains(files[i])) {
+
+                deployed.add(files[i]);
+
+                // Calculate the context path and make sure it is unique
+                String contextPath = "/" + files[i];
+                int period = contextPath.lastIndexOf(".");
+                if (period >= 0)
+                    contextPath = contextPath.substring(0, period);
+
+                // Check for WARs with /../ /./ or similar sequences in the name
+                if (!validateContextPath(appBase, contextPath)) {
+                    log.log(Level.SEVERE, LogFacade.INVALID_WAR_NAME_EXCEPTION, files[i]);
+                    invalidWars.add(files[i]);
+                    continue;
+                }
+
+                if (contextPath.equals("/ROOT"))
+                    contextPath = "";
+                if (host.findChild(contextPath) != null)
+                    continue;
+
+                // Checking for a nested /META-INF/context.xml
+                JarFile jar = null;
+                JarEntry entry = null;
+                InputStream istream = null;
+                BufferedOutputStream ostream = null;
+                File xml = new File
+                    (configBase, files[i].substring
+                     (0, files[i].lastIndexOf(".")) + ".xml");
+                if (!xml.exists()) {
+                    try {
+                        jar = new JarFile(dir);
+                        entry = jar.getJarEntry("META-INF/context.xml");
+                        if (entry != null) {
+                            istream = jar.getInputStream(entry);
+                            ostream =
+                                new BufferedOutputStream
+                                (new FileOutputStream(xml), 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();
+                            ostream = null;
+                            istream.close();
+                            istream = null;
+                            entry = null;
+                            jar.close();
+                            jar = null;
+                            deployDescriptors(configBase(), configBase.list());
+                            return;
+                        }
+                    } catch (IOException e) {
+                        // Ignore and continue
+                    } finally {
+                        if (ostream != null) {
+                            try {
+                                ostream.close();
+                            } catch (Throwable t) {
+                                ;
+                            }
+                            ostream = null;
+                        }
+                        if (istream != null) {
+                            try {
+                                istream.close();
+                            } catch (Throwable t) {
+                                ;
+                            }
+                            istream = null;
+                        }
+                        entry = null;
+                        if (jar != null) {
+                            try {
+                                jar.close();
+                            } catch (Throwable t) {
+                                ;
+                            }
+                            jar = null;
+                        }
+                    }
+                }
+
+                if (isUnpackWARs()) {
+
+                    // Expand and deploy this application as a directory
+                    if (log.isLoggable(Level.FINE)) {
+                        log.log(Level.FINE, LogFacade.EXPANDING_WEB_APP, files[i]);
+                    }
+                    URL url = null;
+                    String path = null;
+                    try {
+                        url = new URL("jar:file:" +
+                                      dir.getCanonicalPath() + "!/");
+                        path = ExpandWar.expand(host, url);
+                    } catch (IOException e) {
+                        // JAR decompression failure
+                        log.log(Level.WARNING, LogFacade.EXPANDING_WEB_APP_EXCEPTION, files[i]);
+                        continue;
+                    } catch (Throwable t) {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.EXPANDING_WEB_APP_ARCHIVE_EXCEPTION),
+                                                          files[i]);
+                        log.log(Level.SEVERE, msg, t);
+                        continue;
+                    }
+                    try {
+                        if (path != null) {
+                            url = new URL("file:" + path);
+                            ((Deployer) host).install(contextPath, url);
+                        }
+                    } catch (Throwable t) {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.EXPANDING_WEB_APP_ARCHIVE_EXCEPTION),
+                                                          files[i]);
+                        log.log(Level.SEVERE, msg, t);
+                    }
+
+                } else {
+
+                    // Deploy the application in this WAR file
+                    if (log.isLoggable(Level.INFO)) {
+                        log.log(Level.INFO, LogFacade.DEPLOYING_WEB_APP_ARCHIVE, files[i]);
+                    }
+                    try {
+                        URL url = new URL("file", null,
+                                          dir.getCanonicalPath());
+                        url = new URL("jar:" + url.toString() + "!/");
+                        ((Deployer) host).install(contextPath, url);
+                    } catch (Throwable t) {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_DEPLOYING_WEB_APP_ARCHIVE_EXCEPTION),
+                                                          files[i]);
+                        log.log(Level.SEVERE, msg, t);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Deploy directories.
+     */
+    protected void deployDirectories(File appBase, String[] files) {
+
+        for (int i = 0; i < files.length; i++) {
+
+            if (files[i].equalsIgnoreCase("META-INF"))
+                continue;
+            if (files[i].equalsIgnoreCase("WEB-INF"))
+                continue;
+            if (deployed.contains(files[i]))
+                continue;
+            File dir = new File(appBase, files[i]);
+            if (dir.isDirectory()) {
+
+                deployed.add(files[i]);
+
+                // Make sure there is an application configuration directory
+                // This is needed if the Context appBase is the same as the
+                // web server document root to make sure only web applications
+                // are deployed and not directories for web space.
+                File webInf = new File(dir, "/WEB-INF");
+                if (!webInf.exists() || !webInf.isDirectory() ||
+                    !webInf.canRead())
+                    continue;
+
+                // Calculate the context path and make sure it is unique
+                String contextPath = "/" + files[i];
+                if (files[i].equals("ROOT"))
+                    contextPath = "";
+                if (host.findChild(contextPath) != null)
+                    continue;
+
+                // Deploy the application in this directory
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, LogFacade.DEPLOYING_WEB_APP_DIR, files[i]);
+                }
+                long t1=System.currentTimeMillis();
+                try {
+                    URL url = new URL("file", null, dir.getCanonicalPath());
+                    ((Deployer) host).install(contextPath, url);
+                } catch (Throwable t) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_DEPLOYING_WEB_APP_DIR),
+                                                      files[i]);
+                    log.log(Level.SEVERE, msg, t);
+                }
+                long t2=System.currentTimeMillis();
+                if( (t2-t1) > 200 && log.isLoggable(Level.FINE) )
+                    log.log(Level.FINE, "Deployed " + files[i] + " " + (t2-t1));
+            }
+
+        }
+
+    }
+
+
+    private boolean validateContextPath(File appBase, String contextPath) {
+        // More complicated than the ideal as the canonical path may or may
+        // not end with File.separator for a directory
+        
+        StringBuilder docBase;
+        String canonicalDocBase = null;
+        
+        try {
+            String canonicalAppBase = appBase.getCanonicalPath();
+            docBase = new StringBuilder(canonicalAppBase);
+            if (canonicalAppBase.endsWith(File.separator)) {
+                docBase.append(contextPath.substring(1).replace(
+                        '/', File.separatorChar));
+            } else {
+                docBase.append(contextPath.replace('/', File.separatorChar));
+            }
+            // At this point docBase should be canonical but will not end
+            // with File.separator
+            
+            canonicalDocBase =
+                (new File(docBase.toString())).getCanonicalPath();
+    
+            // If the canonicalDocBase ends with File.separator, add one to
+            // docBase before they are compared
+            if (canonicalDocBase.endsWith(File.separator)) {
+                docBase.append(File.separator);
+            }
+        } catch (IOException ioe) {
+            return false;
+        }
+        
+        // Compare the two. If they are not the same, the contextPath must
+        // have /../ like sequences in it 
+        return canonicalDocBase.equals(docBase.toString());
+    }
+
+
+    /**
+     * Check deployment descriptors last modified date.
+     */
+    protected void checkContextLastModified() {
+
+        if (!(host instanceof Deployer))
+            return;
+
+        Deployer deployer = (Deployer) host;
+
+        String[] contextNames = deployer.findDeployedApps();
+
+        for (int i = 0; i < contextNames.length; i++) {
+
+            String contextName = contextNames[i];
+            Context context = deployer.findDeployedApp(contextName);
+
+            if (!(context instanceof Lifecycle))
+                continue;
+
+            try {
+                DirContext resources = context.getResources();
+                if (resources == null) {
+                    // This can happen if there was an error initializing
+                    // the context
+                    continue;
+                }
+                ResourceAttributes webXmlAttributes = 
+                    (ResourceAttributes) 
+                    resources.getAttributes("/WEB-INF/web.xml");
+                ResourceAttributes webInfAttributes = 
+                    (ResourceAttributes) 
+                    resources.getAttributes("/WEB-INF");
+                long newLastModified = webXmlAttributes.getLastModified();
+                long webInfLastModified = webInfAttributes.getLastModified();
+                Long lastModified = webXmlLastModified.get(contextName);
+                if (lastModified == null) {
+                    webXmlLastModified.put
+                        (contextName, Long.valueOf(newLastModified));
+                } else {
+                    if (lastModified.longValue() != newLastModified) {
+                        if (newLastModified > (webInfLastModified + 5000)) {
+                            webXmlLastModified.remove(contextName);
+                            restartContext(context);
+                        } else {
+                            webXmlLastModified.put
+                                (contextName, Long.valueOf(newLastModified));
+                        }
+                    }
+                }
+            } catch (NamingException e) {
+                ; // Ignore
+            }
+
+            Long lastModified = contextXmlLastModified.get(contextName);
+            String configBase = configBase().getPath();
+            String configFileName = context.getConfigFile();
+            if (configFileName != null) {
+                File configFile = new File(configFileName);
+                if (!configFile.isAbsolute()) {
+                    configFile = new File(System.getProperty("catalina.base"),
+                                          configFile.getPath());
+                }
+                long newLastModified = configFile.lastModified();
+                if (lastModified == null) {
+                    contextXmlLastModified.put
+                        (contextName, Long.valueOf(newLastModified));
+                } else {
+                    if (lastModified.longValue() != newLastModified) {
+                        contextXmlLastModified.remove(contextName);
+                        String fileName = configFileName;
+                        if (fileName.startsWith(configBase)) {
+                            fileName = 
+                                fileName.substring(configBase.length() + 1);
+                            try {
+                                deployed.remove(fileName);
+                                if (host.findChild(contextName) != null) {
+                                    ((Deployer) host).remove(contextName);
+                                }
+                            } catch (Throwable t) {
+                                String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_UNDEPLOYING_JAR_FILE_EXCEPTION),
+                                                                  fileName);
+                                log.log(Level.SEVERE, msg, t);
+                            }
+                            deployApps();
+                        }
+                    }
+                }
+            }
+
+        }
+
+        // Check for WAR modification
+        if (isUnpackWARs()) {
+            File appBase = appBase();
+            if (!appBase.exists() || !appBase.isDirectory())
+                return;
+            String files[] = appBase.list();
+
+            for (int i = 0; i < files.length; i++) {
+                if (files[i].endsWith(".war")) {
+                    File dir = new File(appBase, files[i]);
+                    Long lastModified = warLastModified.get(files[i]);
+                    long dirLastModified = dir.lastModified();
+                    if (lastModified == null) {
+                        warLastModified.put
+                            (files[i], Long.valueOf(dir.lastModified()));
+                    } else if (dirLastModified > lastModified.longValue()) {
+                        // The WAR has been modified: redeploy
+                        String expandedDir = files[i];
+                        int period = expandedDir.lastIndexOf(".");
+                        if (period >= 0)
+                            expandedDir = expandedDir.substring(0, period);
+                        File expanded = new File(appBase, expandedDir);
+                        String contextPath = "/" + expandedDir;
+                        if (contextPath.equals("/ROOT"))
+                            contextPath = "";
+                        if (dirLastModified > expanded.lastModified()) {
+                            try {
+                                // Undeploy current application
+                                deployed.remove(files[i]);
+                                deployed.remove(expandedDir + ".xml");
+                                if (host.findChild(contextPath) != null) {
+                                    ((Deployer) host).remove(contextPath, 
+                                                             false);
+                                    ExpandWar.deleteDir(expanded);
+                                }
+                            } catch (Throwable t) {
+                                String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_UNDEPLOYING_JAR_FILE_EXCEPTION),
+                                                                  files[i]);
+                                log.log(Level.SEVERE, msg, t);
+                            }
+                            deployApps();
+                        }
+                        // If deployment was successful, reset 
+                        // the last modified values
+                        if (host.findChild(contextPath) != null) {
+                            webXmlLastModified.remove(contextPath);
+                            warLastModified.put
+                                (files[i], Long.valueOf(dir.lastModified()));
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+
+    protected boolean restartContext(Context context) {
+        boolean result = true;
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.RESTART_CONTEXT_INFO, context.getName());
+        }
+
+        /*
+        try {
+            StandardContext sctx=(StandardContext)context;
+            sctx.reload();
+        } catch( Exception ex ) {
+            log.warn("Erorr stopping context " + context.getName()  +  " " +
+                    ex.toString());
+        }
+        */
+        try {
+            ((Lifecycle) context).stop();
+        } catch( Exception ex ) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_DURING_CONTEXT_STOP_EXCEPTION),
+                                              context.getName());
+            log.log(Level.WARNING, msg, ex);
+        }
+        // if the context was not started ( for example an error in web.xml)
+        // we'll still get to try to start
+        try {
+            ((Lifecycle) context).start();
+        } catch (Exception e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_DURING_CONTEXT_RESTART_EXCEPTION),
+                                              context.getName());
+            log.log(Level.WARNING, msg, e);
+            result = false;
+        }
+        
+        return result;
+    }
+
+
+    /**
+     * Expand the WAR file found at the specified URL into an unpacked
+     * directory structure, and return the absolute pathname to the expanded
+     * directory.
+     *
+     * @param war URL of the web application archive to be expanded
+     *  (must start with "jar:")
+     *
+     * @exception IllegalArgumentException if this is not a "jar:" URL
+     * @exception IOException if an input/output error was encountered
+     *  during expansion
+     */
+    protected String expand(URL war) throws IOException {
+
+        return ExpandWar.expand(host,war);
+    }
+
+
+    /**
+     * Expand the specified input stream into the specified directory, creating
+     * a file named from the specified relative path.
+     *
+     * @param input InputStream to be copied
+     * @param docBase Document base directory into which we are expanding
+     * @param name Relative pathname of the file to be created
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected void expand(InputStream input, File docBase, String name)
+        throws IOException {
+
+        ExpandWar.expand(input,docBase,name);
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Host (if any)
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+
+        org.apache.catalina.Logger logger = null;
+        if (host != null)
+            logger = host.getLogger();
+        if (logger != null)
+            logger.log("HostConfig[" + host.getName() + "]: " + message);
+        else
+            log.info(message);
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Host (if any)
+     *
+     * @param message Message to be logged
+     * @param throwable Associated exception
+     */
+    protected void log(String message, Throwable throwable) {
+
+        org.apache.catalina.Logger logger = null;
+        if (host != null)
+            logger = host.getLogger();
+        if (logger != null)
+            logger.log("HostConfig[" + host.getName() + "] "
+                       + message, throwable);
+        else {
+            log.log(Level.SEVERE, message, throwable);
+        }
+
+    }
+
+
+    /**
+     * Process a "start" event for this Host.
+     */
+    public void start() {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, LogFacade.PROCESSING_START);
+        if (host.getDeployOnStartup()) {
+            deployApps();
+        } else {
+            // Deploy descriptors anyway (it should be equivalent to being
+            // part of server.xml)
+            File configBase = configBase();
+            if (configBase.exists() && configBase.isDirectory()) {
+                String configFiles[] = configBase.list();
+                deployDescriptors(configBase, configFiles);
+            }
+        } 
+
+    }
+
+
+    /**
+     * Process a "stop" event for this Host.
+     */
+    public void stop() {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, LogFacade.PROCESSING_STOP);
+        undeployApps();
+
+        appBase = null;
+        configBase = null;
+
+    }
+
+
+    /**
+     * Undeploy all deployed applications.
+     */
+    protected void undeployApps() {
+
+        if (!(host instanceof Deployer))
+            return;
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, LogFacade.UNDEPLOYING_WEB_APP);
+
+        String contextPaths[] = ((Deployer) host).findDeployedApps();
+        for (int i = 0; i < contextPaths.length; i++) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, LogFacade.UNDEPLOYING_CONTEXT, contextPaths[i]);
+            }
+            try {
+                ((Deployer) host).remove(contextPaths[i]);
+            } catch (Throwable t) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_UNDEPLOYING_WEB_APP_EXCEPTION),
+                                                  contextPaths[i]);
+                log.log(Level.SEVERE, msg, t);
+            }
+        }
+
+        webXmlLastModified.clear();
+        deployed.clear();
+
+    }
+
+
+    /**
+     * Deploy webapps.
+     */
+    protected void check() {
+
+        if (host.getAutoDeploy()) {
+            // Deploy apps if the Host allows auto deploying
+            deployApps();
+            // Check for web.xml modification
+            checkContextLastModified();
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HostRuleSet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HostRuleSet.java
new file mode 100644
index 0000000..82c8ba3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/HostRuleSet.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a
+ * Host definition element.  This <code>RuleSet</code> does NOT include
+ * any rules for nested Context or DefaultContext elements, which should
+ * be added via instances of <code>ContextRuleSet</code>.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:06 $
+ */
+
+public class HostRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public HostRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public HostRuleSet(String prefix) {
+
+        super();
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>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.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        digester.addObjectCreate(prefix + "Host",
+                                 "org.apache.catalina.core.StandardHost",
+                                 "className");
+        digester.addSetProperties(prefix + "Host");
+        digester.addRule(prefix + "Host",
+                         new CopyParentClassLoaderRule(digester));
+        digester.addRule(prefix + "Host",
+                         new LifecycleListenerRule
+                         (digester,
+                          "org.apache.catalina.startup.HostConfig",
+                          "hostConfigClass"));
+        digester.addSetNext(prefix + "Host",
+                            "addChild",
+                            "org.apache.catalina.Container");
+
+        digester.addCallMethod(prefix + "Host/Alias",
+                               "addAlias", 0);
+
+        digester.addObjectCreate(prefix + "Host/Cluster",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Cluster");
+        digester.addSetNext(prefix + "Host/Cluster",
+                            "setCluster",
+                            "org.apache.catalina.Cluster");
+
+        digester.addObjectCreate(prefix + "Host/Listener",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Listener");
+        digester.addSetNext(prefix + "Host/Listener",
+                            "addLifecycleListener",
+                            "org.apache.catalina.LifecycleListener");
+
+        digester.addObjectCreate(prefix + "Host/Logger",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Logger");
+        digester.addSetNext(prefix + "Host/Logger",
+                            "setLogger",
+                            "org.apache.catalina.Logger");
+
+        digester.addObjectCreate(prefix + "Host/Realm",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Realm");
+        digester.addSetNext(prefix + "Host/Realm",
+                            "setRealm",
+                            "org.apache.catalina.Realm");
+
+        digester.addObjectCreate(prefix + "Host/Valve",
+                                 null, // MUST be specified in the element
+                                 "className");
+        digester.addSetProperties(prefix + "Host/Valve");
+        digester.addSetNext(prefix + "Host/Valve",
+                            "addValve",
+                            "org.apache.catalina.Valve");
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/LifecycleListenerRule.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/LifecycleListenerRule.java
new file mode 100644
index 0000000..61115f8
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/LifecycleListenerRule.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Rule that creates a new <code>LifecycleListener</code> instance,
+ * and associates it with the top object on the stack (which must
+ * implement <code>LifecycleListener</code>).</p>
+ */
+
+public class LifecycleListenerRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this Rule.
+     *
+     * @param digester Digester we are associated with
+     * @param listenerClass Default name of the LifecycleListener
+     *  implementation class to be created
+     * @param attributeName Name of the attribute that optionally
+     *  includes an override name of the LifecycleListener class
+     */
+    public LifecycleListenerRule(Digester digester, String listenerClass,
+                                 String attributeName) {
+
+        super(digester);
+        this.listenerClass = listenerClass;
+        this.attributeName = attributeName;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The attribute name of an attribute that can override the
+     * implementation class name.
+     */
+    private String attributeName;
+
+
+    /**
+     * The name of the <code>LifecycleListener</code> implementation class.
+     */
+    private String listenerClass;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        // Instantiate a new LifecyleListener implementation object
+        String className = listenerClass;
+        if (attributeName != null) {
+            String value = attributes.getValue(attributeName);
+            if (value != null)
+                className = value;
+        }
+        Class clazz = Class.forName(className);
+        LifecycleListener listener =
+            (LifecycleListener) clazz.newInstance();
+
+        // Add this LifecycleListener to our associated component
+        Lifecycle lifecycle = (Lifecycle) digester.peek();
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/NamingRuleSet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/NamingRuleSet.java
new file mode 100644
index 0000000..707052c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/NamingRuleSet.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the JNDI Enterprise Naming
+ * Context resource declaration elements.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:07 $
+ */
+
+public class NamingRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public NamingRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public NamingRuleSet(String prefix) {
+
+        super();
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>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.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+
+        digester.addObjectCreate(prefix + "Ejb",
+                                 "org.apache.catalina.deploy.ContextEjb");
+        digester.addSetProperties(prefix + "Ejb");
+        digester.addSetNext(prefix + "Ejb",
+                            "addEjb",
+                            "org.apache.catalina.deploy.ContextEjb");
+
+        digester.addObjectCreate(prefix + "Environment",
+                                 "org.apache.catalina.deploy.ContextEnvironment");
+        digester.addSetProperties(prefix + "Environment");
+        digester.addSetNext(prefix + "Environment",
+                            "addEnvironment",
+                            "org.apache.catalina.deploy.ContextEnvironment");
+
+        digester.addObjectCreate(prefix + "LocalEjb",
+                                 "org.apache.catalina.deploy.ContextLocalEjb");
+        digester.addSetProperties(prefix + "LocalEjb");
+        digester.addSetNext(prefix + "LocalEjb",
+                            "addLocalEjb",
+                            "org.apache.catalina.deploy.ContextLocalEjb");
+
+        digester.addObjectCreate(prefix + "Resource",
+                                 "org.apache.catalina.deploy.ContextResource");
+        digester.addSetProperties(prefix + "Resource");
+        digester.addSetNext(prefix + "Resource",
+                            "addResource",
+                            "org.apache.catalina.deploy.ContextResource");
+
+        digester.addCallMethod(prefix + "ResourceEnvRef",
+                               "addResourceEnvRef", 2);
+        digester.addCallParam(prefix + "ResourceEnvRef/name", 0);
+        digester.addCallParam(prefix + "ResourceEnvRef/type", 1);
+
+        digester.addObjectCreate(prefix + "ResourceParams",
+                                 "org.apache.catalina.deploy.ResourceParams");
+        digester.addSetProperties(prefix + "ResourceParams");
+        digester.addSetNext(prefix + "ResourceParams",
+                            "addResourceParams",
+                            "org.apache.catalina.deploy.ResourceParams");
+
+        digester.addCallMethod(prefix + "ResourceParams/parameter",
+                               "addParameter", 2);
+        digester.addCallParam(prefix + "ResourceParams/parameter/name", 0);
+        digester.addCallParam(prefix + "ResourceParams/parameter/value", 1);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/PasswdUserDatabase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/PasswdUserDatabase.java
new file mode 100644
index 0000000..c21e0aa
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/PasswdUserDatabase.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+
+/**
+ * Concrete implementation of the <strong>UserDatabase</code> interface
+ * that processes the <code>/etc/passwd</code> file on a Unix system.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:09 $
+ */
+
+public final class PasswdUserDatabase
+    implements UserDatabase {
+
+
+    // --------------------------------------------------------- Constructors
+
+
+    /**
+     * Initialize a new instance of this user database component.
+     */
+    public PasswdUserDatabase() {
+
+        super();
+
+    }
+
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * The pathname of the Unix password file.
+     */
+    private static final String PASSWORD_FILE = "/etc/passwd";
+
+
+    /**
+     * The set of home directories for all defined users, keyed by username.
+     */
+    private Hashtable<String, String> homes = new Hashtable<String, String>();
+
+
+    /**
+     * The UserConfig listener with which we are associated.
+     */
+    private UserConfig userConfig = null;
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the UserConfig listener with which we are associated.
+     */
+    public UserConfig getUserConfig() {
+
+        return (this.userConfig);
+
+    }
+
+
+    /**
+     * Set the UserConfig listener with which we are associated.
+     *
+     * @param userConfig The new UserConfig listener
+     */
+    public void setUserConfig(UserConfig userConfig) {
+
+        this.userConfig = userConfig;
+        init();
+
+    }
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return an absolute pathname to the home directory for the specified user.
+     *
+     * @param user User for which a home directory should be retrieved
+     */
+    public String getHome(String user) {
+
+        return homes.get(user);
+
+    }
+
+
+    /**
+     * Return an enumeration of the usernames defined on this server.
+     */
+    public Enumeration<String> getUsers() {
+
+        return (homes.keys());
+
+    }
+
+
+    // ------------------------------------------------------ Private Methods
+
+
+    /**
+     * Initialize our set of users and home directories.
+     */
+    private void init() {
+
+        BufferedReader reader = null;
+        try {
+
+            reader = new BufferedReader(new FileReader(PASSWORD_FILE));
+
+            while (true) {
+
+                // Accumulate the next line
+                StringBuilder buffer = new StringBuilder();
+                while (true) {
+                    int ch = reader.read();
+                    if ((ch < 0) || (ch == '\n'))
+                        break;
+                    buffer.append((char) ch);
+                }
+                String line = buffer.toString();
+                if (line.length() < 1)
+                    break;
+
+                // Parse the line into constituent elements
+                int n = 0;
+                String tokens[] = new String[7];
+                for (int i = 0; i < tokens.length; i++)
+                    tokens[i] = null;
+                while (n < tokens.length) {
+                    String token = null;
+                    int colon = line.indexOf(':');
+                    if (colon >= 0) {
+                        token = line.substring(0, colon);
+                        line = line.substring(colon + 1);
+                    } else {
+                        token = line;
+                        line = "";
+                    }
+                    tokens[n++] = token;
+                }
+
+                // Add this user and corresponding directory
+                if ((tokens[0] != null) && (tokens[5] != null))
+                    homes.put(tokens[0], tokens[5]);
+
+            }
+
+            reader.close();
+            reader = null;
+
+        } catch (Exception e) {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException f) {
+                    ;
+                }
+                reader = null;
+            }
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetAllPropertiesRule.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetAllPropertiesRule.java
new file mode 100644
index 0000000..c59edd7
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetAllPropertiesRule.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.glassfish.web.util.IntrospectionUtils;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+/**
+ * Rule that uses the introspection utils to set properties.
+ * 
+ * @author Remy Maucherat
+ */
+public class SetAllPropertiesRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        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);
+            IntrospectionUtils.setProperty(digester.peek(), name, value);
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetContextPropertiesRule.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetContextPropertiesRule.java
new file mode 100644
index 0000000..da67ada
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetContextPropertiesRule.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.glassfish.web.util.IntrospectionUtils;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+/**
+ * Rule that uses the introspection utils to set properties of a context 
+ * (everything except "path").
+ * 
+ * @author Remy Maucherat
+ */
+public class SetContextPropertiesRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(String namespace, String nameX, Attributes attributes)
+        throws Exception {
+
+        for (int i = 0; i < attributes.getLength(); i++) {
+            String name = attributes.getLocalName(i);
+            if ("".equals(name)) {
+                name = attributes.getQName(i);
+            }
+            if ("path".equals(name)) { 
+                continue;
+            }
+            String value = attributes.getValue(i);
+            IntrospectionUtils.setProperty(digester.peek(), name, value);
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetDocBaseRule.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetDocBaseRule.java
new file mode 100644
index 0000000..979f438
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/SetDocBaseRule.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Deployer;
+import org.apache.catalina.Host;
+import org.apache.catalina.core.StandardHost;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.xml.sax.Attributes;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Locale;
+
+/**
+ * <p>Rule that modifies the docBase of the host, setting it appropriately,
+ * before adding the Context to the parent Host.</p>
+ * 
+ * @author Remy Maucherat
+ */
+public class SetDocBaseRule extends Rule {
+
+
+    // -------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this Rule.
+     *
+     * @param digester Digester we are associated with
+     */
+    public SetDocBaseRule(Digester digester) {
+        super(digester);
+    }
+
+
+    // -------------------------------------------------- Instance Variables
+
+
+    // ------------------------------------------------------ Public Methods
+
+
+    /**
+     * Handle the beginning of an XML element.
+     *
+     * @param attributes The attributes of this element
+     *
+     * @exception Exception if a processing error occurs
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        Context child = (Context) digester.peek(0);
+        Deployer parent = (Deployer) digester.peek(1);
+        Host host = null;
+        if (!(parent instanceof StandardHost)) {
+            Method method = parent.getClass().getMethod("getHost",(Class[])null);
+            host = (Host) method.invoke(parent,(Object[])null);
+        } else {
+            host = (Host) parent;
+        }
+        String appBase = host.getAppBase();
+
+        boolean unpackWARs = true;
+        if (host instanceof StandardHost) {
+            unpackWARs = ((StandardHost) host).isUnpackWARs();
+        }
+        if (!unpackWARs 
+            && !("true".equals(attributes.getValue("unpackWAR")))) {
+            return;
+        }
+        if ("false".equals(attributes.getValue("unpackWAR"))) {
+            return;
+        }
+
+        File canonicalAppBase = new File(appBase);
+        if (canonicalAppBase.isAbsolute()) {
+            canonicalAppBase = canonicalAppBase.getCanonicalFile();
+        } else {
+            canonicalAppBase = 
+                new File(System.getProperty("catalina.base"), appBase)
+                .getCanonicalFile();
+        }
+
+        String docBase = child.getDocBase();
+        if (docBase == null) {
+            // Trying to guess the docBase according to the path
+            String path = child.getPath();
+            if (path == null) {
+                return;
+            }
+            if (path.equals("")) {
+                docBase = "ROOT";
+            } else {
+                if (path.startsWith("/")) {
+                    docBase = path.substring(1);
+                } else {
+                    docBase = path;
+                }
+            }
+        }
+
+        File file = new File(docBase);
+        if (!file.isAbsolute()) {
+            docBase = (new File(canonicalAppBase, docBase)).getPath();
+        } else {
+            docBase = file.getCanonicalPath();
+        }
+
+        if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
+            URL war = new URL("jar:" + (new File(docBase)).toURL() + "!/");
+            String contextPath = child.getPath();
+            if (contextPath.equals("")) {
+                contextPath = "ROOT";
+            }
+            docBase = ExpandWar.expand(host, war, contextPath);
+            file = new File(docBase);
+            docBase = file.getCanonicalPath();
+        } else {
+            File docDir = new File(docBase);
+            if (!docDir.exists()) {
+                File warFile = new File(docBase + ".war");
+                if (warFile.exists()) {
+                    URL war = new URL("jar:" + warFile.toURL() + "!/");
+                    docBase = ExpandWar.expand(host, war, child.getPath());
+                    file = new File(docBase);
+                    docBase = file.getCanonicalPath();
+                }
+            }
+        }
+
+        if (docBase.startsWith(canonicalAppBase.getPath())) {
+            docBase = docBase.substring
+                (canonicalAppBase.getPath().length() + 1);
+        }
+        docBase = docBase.replace(File.separatorChar, '/');
+
+        child.setDocBase(docBase);
+
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Tool.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Tool.java
new file mode 100644
index 0000000..097f57f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/Tool.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.LogFacade;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * <p>General purpose wrapper for command line tools that should execute in an
+ * environment with the common class loader environment set up by Catalina.
+ * This should be executed from a command line script that conforms to
+ * the following requirements:</p>
+ * <ul>
+ * <li>Passes the <code>catalina.home</code> system property configured with
+ *     the pathname of the Tomcat installation directory.</li>
+ * <li>Sets the system classpath to include <code>bootstrap.jar</code> and
+ *     <code>$JAVA_HOME/lib/tools.jar</code>.</li>
+ * </ul>
+ *
+ * <p>The command line to execute the tool looks like:</p>
+ * <pre>
+ *   java -classpath $CLASSPATH org.apache.catalina.startup.Tool \
+ *     ${options} ${classname} ${arguments}
+ * </pre>
+ *
+ * <p>with the following replacement contents:
+ * <ul>
+ * <li><strong>${options}</strong> - Command line options for this Tool wrapper.
+ *     The following options are supported:
+ *     <ul>
+ *     <li><em>-ant</em> : Set the <code>ant.home</code> system property
+ *         to corresponding to the value of <code>catalina.home</code>
+ *         (useful when your command line tool runs Ant).</li>
+ *     <li><em>-common</em> : Add <code>common/classes</code> and
+ *         <code>common/lib</codE) to the class loader repositories.</li>
+ *     <li><em>-debug</em> : Enable debugging messages from this wrapper.</li>
+ *     <li><em>-server</em> : Add <code>server/classes</code> and
+ *         <code>server/lib</code> to the class loader repositories.</li>
+ *     <li><em>-shared</em> : Add <code>shared/classes</code> and
+ *         <code>shared/lib</code> to the class loader repositories.</li>
+ *     </ul>
+ * <li><strong>${classname}</strong> - Fully qualified Java class name of the
+ *     application's main class.</li>
+ * <li><strong>${arguments}</strong> - Command line arguments to be passed to
+ *     the application's <code>main()</code> method.</li>
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:07 $
+ */
+
+public final class Tool {
+
+
+    // ------------------------------------------------------- Static Variables
+
+    /**
+     * Set <code>ant.home</code> system property?
+     */
+    private static boolean ant = false;
+
+
+    /**
+     * The pathname of our installation base directory.
+     */
+    private static String catalinaHome = System.getProperty("catalina.home");
+
+
+    /**
+     * Include common classes in the repositories?
+     */
+    private static boolean common = false;
+
+
+    /**
+     * Enable debugging detail messages?
+     *
+    private static boolean debug = false;
+    */
+
+    private static final Logger log = LogFacade.getLogger();
+
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    /**
+     * Include server classes in the repositories?
+     */
+    private static boolean server = false;
+
+
+    /**
+     * Include shared classes in the repositories?
+     */
+    private static boolean shared = false;
+
+
+    // ----------------------------------------------------------- Main Program
+
+
+    /**
+     * The main program for the bootstrap.
+     *
+     * @param args Command line arguments to be processed
+     */
+    public static void main(String args[]) {
+
+        // Verify that "catalina.home" was passed.
+        if (catalinaHome == null) {
+            log.log(Level.SEVERE, LogFacade.MUST_SET_SYS_PROPERTY);
+            System.exit(1);
+        }
+
+        // Process command line options
+        int index = 0;
+        while (true) {
+            if (index == args.length) {
+                usage();
+                System.exit(1);
+            }
+            if ("-ant".equals(args[index]))
+                ant = true;
+            else if ("-common".equals(args[index]))
+                common = true;
+            //else if ("-debug".equals(args[index]))
+            //    debug = true;
+            else if ("-server".equals(args[index]))
+                server = true;
+            else if ("-shared".equals(args[index]))
+                shared = true;
+            else
+                break;
+            index++;
+        }
+        if (index > args.length) {
+            usage();
+            System.exit(1);
+        }
+
+        // Set "ant.home" if requested
+        if (ant)
+            System.setProperty("ant.home", catalinaHome);
+
+        // Construct the class loader we will be using
+        ClassLoader classLoader = null;
+        try {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "Constructing class loader");
+                ClassLoaderFactory.setDebug(1);
+            }
+            ArrayList<File> packed = new ArrayList<File>();
+            ArrayList<File> unpacked = new ArrayList<File>();
+            unpacked.add(new File(catalinaHome, "classes"));
+            packed.add(new File(catalinaHome, "lib"));
+            if (common) {
+                unpacked.add(new File(catalinaHome,
+                                      "common" + File.separator + "classes"));
+                packed.add(new File(catalinaHome,
+                                    "common" + File.separator + "lib"));
+            }
+            if (server) {
+                unpacked.add(new File(catalinaHome,
+                                      "server" + File.separator + "classes"));
+                packed.add(new File(catalinaHome,
+                                    "server" + File.separator + "lib"));
+            }
+            if (shared) {
+                unpacked.add(new File(catalinaHome,
+                                      "shared" + File.separator + "classes"));
+                packed.add(new File(catalinaHome,
+                                    "shared" + File.separator + "lib"));
+            }
+            classLoader =
+                ClassLoaderFactory.createClassLoader
+                (unpacked.toArray(new File[unpacked.size()]),
+                 packed.toArray(new File[packed.size()]),
+                 null);
+        } catch (Throwable t) {
+            log.log(Level.SEVERE, LogFacade.CLASS_LOADER_CREATION_EXCEPTION, t);
+            System.exit(1);
+        }
+        Thread.currentThread().setContextClassLoader(classLoader);
+
+        // Load our application class
+        Class<?> clazz = null;
+        String className = args[index++];
+        try {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "Loading application class " + className);
+            clazz = classLoader.loadClass(className);
+        } catch (Throwable t) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CREATING_INSTANCE_EXCEPTION),
+                                              className);
+            log.log(Level.SEVERE, msg, t);
+            System.exit(1);
+        }
+
+        // Locate the static main() method of the application class
+        Method method = null;
+        String params[] = new String[args.length - index];
+        System.arraycopy(args, index, params, 0, params.length);
+        try {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "Identifying main() method");
+            String methodName = "main";
+            Class paramTypes[] = new Class[1];
+            paramTypes[0] = params.getClass();
+            method = clazz.getMethod(methodName, paramTypes);
+        } catch (Throwable t) {
+            log.log(Level.SEVERE, LogFacade.LOCATING_MAIN_METHOD_EXCEPTION, t);
+            System.exit(1);
+        }
+
+        // Invoke the main method of the application class
+        try {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "Calling main() method");
+            Object paramValues[] = new Object[1];
+            paramValues[0] = params;
+            method.invoke(null, paramValues);
+        } catch (Throwable t) {
+            log.log(Level.SEVERE, LogFacade.CALLING_MAIN_METHOD_EXCEPTION, t);
+            System.exit(1);
+        }
+
+    }
+
+
+    /**
+     * Display usage information about this tool.
+     */
+    private static void usage() {
+
+        if (log.isLoggable(Level.INFO)) {
+            log.log(Level.INFO, LogFacade.USAGE_INFO);
+        }
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/UserConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/UserConfig.java
new file mode 100644
index 0000000..eb27cb5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/UserConfig.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+import org.apache.catalina.*;
+import org.apache.catalina.Logger;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.digester.ObjectParamRule;
+
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+import java.util.logging.*;
+
+/**
+ * Startup event listener for a <b>Host</b> that configures Contexts (web
+ * applications) for all defined "users" who have a web application in a
+ * directory with the specified name in their home directories.  The context
+ * path of each deployed application will be set to <code>~xxxxx</code>, where
+ * xxxxx is the username of the owning user for that web application
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:10 $
+ */
+
+public final class UserConfig
+    implements LifecycleListener {
+
+    // ----------------------------------------------------- Static Variables
+
+    private static final java.util.logging.Logger log = LogFacade.getLogger();
+
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The Java class name of the Context configuration class we should use.
+     */
+    private String configClass = ContextConfig.class.getName();
+
+    /**
+     * The Java class name of the Context implementation we should use.
+     */
+    private String contextClass = StandardContext.class.getName();
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 999;
+
+    /**
+     * The directory name to be searched for within each user home directory.
+     */
+    private String directoryName = "public_html";
+
+    /**
+     * The base directory containing user home directories.
+     */
+    private String homeBase = null;
+
+    /**
+     * The Host we are associated with.
+     */
+    private Host host = null;
+
+    /**
+     * The Java class name of the user database class we should use.
+     */
+    private String userClass =
+        "org.apache.catalina.startup.PasswdUserDatabase";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Context configuration class name.
+     */
+    public String getConfigClass() {
+        return (this.configClass);
+    }
+
+
+    /**
+     * Set the Context configuration class name.
+     *
+     * @param configClass The new Context configuration class name.
+     */
+    public void setConfigClass(String configClass) {
+        this.configClass = configClass;
+    }
+
+
+    /**
+     * Return the Context implementation class name.
+     */
+    public String getContextClass() {
+        return (this.contextClass);
+    }
+
+
+    /**
+     * Set the Context implementation class name.
+     *
+     * @param contextClass The new Context implementation class name.
+     */
+    public void setContextClass(String contextClass) {
+        this.contextClass = contextClass;
+    }
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+        return (this.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 directory name for user web applications.
+     */
+    public String getDirectoryName() {
+        return (this.directoryName);
+    }
+
+
+    /**
+     * Set the directory name for user web applications.
+     *
+     * @param directoryName The new directory name
+     */
+    public void setDirectoryName(String directoryName) {
+        this.directoryName = directoryName;
+    }
+
+
+    /**
+     * Return the base directory containing user home directories.
+     */
+    public String getHomeBase() {
+        return (this.homeBase);
+    }
+
+
+    /**
+     * Set the base directory containing user home directories.
+     *
+     * @param homeBase The new base directory
+     */
+    public void setHomeBase(String homeBase) {
+        this.homeBase = homeBase;
+    }
+
+
+    /**
+     * Return the user database class name for this component.
+     */
+    public String getUserClass() {
+        return (this.userClass);
+    }
+
+
+    /**
+     * Set the user database class name for this component.
+     */
+    public void setUserClass(String userClass) {
+        this.userClass = userClass;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the START event for an associated Host.
+     *
+     * @param event The lifecycle event that has occurred
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        // Identify the host we are associated with
+        try {
+            host = (Host) event.getLifecycle();
+        } catch (ClassCastException e) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.LIFECYCLE_OBJECT_NOT_HOST_EXCEPTION),
+                                              event.getLifecycle());
+            log(msg, e);
+            return;
+        }
+
+        // Process the event that has occurred
+        if (event.getType().equals(Lifecycle.START_EVENT))
+            start();
+        else if (event.getType().equals(Lifecycle.STOP_EVENT))
+            stop();
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Deploy a web application for any user who has a web application present
+     * in a directory with a specified name within their home directory.
+     */
+    private void deploy() {
+
+        if (debug >= 1)
+            log(rb.getString(LogFacade.DEPLOYING_USER_WEB_APP_INFO));
+
+        // Load the user database object for this host
+        UserDatabase database = null;
+        try {
+            Class clazz = Class.forName(userClass);
+            database = (UserDatabase) clazz.newInstance();
+            database.setUserConfig(this);
+        } catch (Exception e) {
+            log(rb.getString(LogFacade.LOADING_USER_DATABASE_EXCEPTION), e);
+            return;
+        }
+
+        // Deploy the web application (if any) for each defined user
+        Enumeration users = database.getUsers();
+        while (users.hasMoreElements()) {
+            String user = (String) users.nextElement();
+            String home = database.getHome(user);
+            deploy(user, home);
+        }
+
+    }
+
+
+    /**
+     * Deploy a web application for the specified user if they have such an
+     * application in the defined directory within their home directory.
+     *
+     * @param user Username owning the application to be deployed
+     * @param home Home directory of this user
+     */
+    private void deploy(String user, String home) {
+
+        // Does this user have a web application to be deployed?
+        String contextPath = "/~" + user;
+        if (host.findChild(contextPath) != null)
+            return;
+        File app = new File(home, directoryName);
+        if (!app.exists() || !app.isDirectory())
+            return;
+        /*
+        File dd = new File(app, "/WEB-INF/web.xml");
+        if (!dd.exists() || !dd.isFile() || !dd.canRead())
+            return;
+        */
+        String msg = MessageFormat.format(rb.getString(LogFacade.DEPLOYING_WEB_APP_FOR_USER_INFO),
+                                          user);
+        log(msg);
+
+        // Deploy the web application for this user
+        try {
+            Class clazz = Class.forName(contextClass);
+            Context context =
+              (Context) clazz.newInstance();
+            context.setPath(contextPath);
+            context.setDocBase(app.toString());
+            if (context instanceof Lifecycle) {
+                clazz = Class.forName(configClass);
+                LifecycleListener listener =
+                  (LifecycleListener) clazz.newInstance();
+                ((Lifecycle) context).addLifecycleListener(listener);
+            }
+            host.addChild(context);
+        } catch (Exception e) {
+            String deployWebAppMsg = MessageFormat.format(rb.getString(LogFacade.DEPLOYING_WEB_APP_FOR_USER_EXCEPTION),
+                                              user);
+            log(deployWebAppMsg, e);
+        }
+
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Host (if any)
+     *
+     * @param message Message to be logged
+     */
+    private void log(String message) {
+        Logger logger = null;
+        if (host != null) {
+            logger = host.getLogger();
+            if (logger != null) {
+                logger.log("UserConfig[" + host.getName() + "]: " + message);
+            } else {
+                if (log.isLoggable(Level.INFO)) {
+                    log.log(Level.INFO, LogFacade.USER_CONFIG, new Object[] {host.getName(), message});
+                }
+            }
+        } else {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.USER_CONFIG_NULL, message);
+            }
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Host (if any)
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    private void log(String message, Throwable t) {
+        Logger logger = null;
+        if (host != null) {
+            logger = host.getLogger();
+            if (logger != null) {
+                logger.log("UserConfig[" + host.getName() + "] "
+                        + message, t, Logger.WARNING);
+            } else {
+                String msg = MessageFormat.format(rb.getString(LogFacade.USER_CONFIG),
+                                                  new Object[] {host.getName(), message});
+                log.log(Level.WARNING, msg, t);
+            }
+        } else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.USER_CONFIG_NULL),
+                                              message);
+            log.log(Level.WARNING, msg, t);
+        }
+    }
+
+
+    /**
+     * Process a "start" event for this Host.
+     */
+    private void start() {
+
+        if (debug > 0)
+            log(rb.getString(LogFacade.PROCESSING_START_INFO));
+
+        deploy();
+
+    }
+
+
+    /**
+     * Process a "stop" event for this Host.
+     */
+    private void stop() {
+
+        if (debug > 0)
+            log(rb.getString(LogFacade.PROCESSING_STOP_INFO));
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/UserDatabase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/UserDatabase.java
new file mode 100644
index 0000000..61920b3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/UserDatabase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import java.util.Enumeration;
+
+
+/**
+ * Abstraction of the set of users defined by the operating system on the
+ * current server platform.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:11 $
+ */
+
+public interface UserDatabase {
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the UserConfig listener with which we are associated.
+     */
+    public UserConfig getUserConfig();
+
+
+    /**
+     * Set the UserConfig listener with which we are associated.
+     *
+     * @param userConfig The new UserConfig listener
+     */
+    public void setUserConfig(UserConfig userConfig);
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return an absolute pathname to the home directory for the specified user.
+     *
+     * @param user User for which a home directory should be retrieved
+     */
+    public String getHome(String user);
+
+
+    /**
+     * Return an enumeration of the usernames defined on this server.
+     */
+    public Enumeration getUsers();
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/startup/WebRuleSet.java b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/WebRuleSet.java
new file mode 100644
index 0000000..279b2a1
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/startup/WebRuleSet.java
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.startup;
+
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.deploy.SecurityConstraint;
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.Rule;
+import org.apache.tomcat.util.digester.RuleSetBase;
+import org.xml.sax.Attributes;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a web application
+ * deployment descriptor (<code>/WEB-INF/web.xml</code>) resource.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.5 $ $Date: 2007/01/23 00:06:56 $
+ */
+
+public class WebRuleSet extends RuleSetBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The matching pattern prefix to use for recognizing our elements.
+     */
+    protected String prefix = null;
+    
+    
+    /**
+     * The <code>SetSessionConfig</code> rule used to parse the web.xml
+     */
+    protected SetSessionConfig sessionConfig;
+    
+    
+    /**
+     * The <code>SetLoginConfig</code> rule used to parse the web.xml
+     */
+    protected SetLoginConfig loginConfig;
+
+    
+    /**
+     * The <code>SetJspConfig</code> rule used to parse the web.xml
+     */    
+    protected SetJspConfig jspConfig;
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the default
+     * matching pattern prefix.
+     */
+    public WebRuleSet() {
+
+        this("");
+
+    }
+
+
+    /**
+     * Construct an instance of this <code>RuleSet</code> with the specified
+     * matching pattern prefix.
+     *
+     * @param prefix Prefix for matching pattern rules (including the
+     *  trailing slash character)
+     */
+    public WebRuleSet(String prefix) {
+
+        super();
+        this.prefix = prefix;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>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.</p>
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester) {
+        sessionConfig = new SetSessionConfig(digester);
+        jspConfig = new SetJspConfig(digester);
+        loginConfig = new SetLoginConfig(digester);
+        
+        digester.addRule(prefix + "web-app",
+                         new SetPublicIdRule(digester, "setPublicId"));
+
+        digester.addCallMethod(prefix + "web-app/context-param",
+                               "addParameter", 2);
+        digester.addCallParam(prefix + "web-app/context-param/param-name", 0);
+        digester.addCallParam(prefix + "web-app/context-param/param-value", 1);
+
+        digester.addCallMethod(prefix + "web-app/display-name",
+                               "setDisplayName", 0);
+
+        digester.addRule(prefix + "web-app/distributable",
+                         new SetDistributableRule(digester));
+
+        digester.addObjectCreate(prefix + "web-app/ejb-local-ref",
+                                 "org.apache.catalina.deploy.ContextLocalEjb");
+        digester.addSetNext(prefix + "web-app/ejb-local-ref",
+                            "addLocalEjb",
+                            "org.apache.catalina.deploy.ContextLocalEjb");
+
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-link",
+                               "setLink", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-type",
+                               "setType", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/local",
+                               "setLocal", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-local-ref/local-home",
+                               "setHome", 0);
+
+        digester.addObjectCreate(prefix + "web-app/ejb-ref",
+                                 "org.apache.catalina.deploy.ContextEjb");
+        digester.addSetNext(prefix + "web-app/ejb-ref",
+                            "addEjb",
+                            "org.apache.catalina.deploy.ContextEjb");
+
+        digester.addCallMethod(prefix + "web-app/ejb-ref/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-link",
+                               "setLink", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-type",
+                               "setType", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/home",
+                               "setHome", 0);
+        digester.addCallMethod(prefix + "web-app/ejb-ref/remote",
+                               "setRemote", 0);
+
+        digester.addObjectCreate(prefix + "web-app/env-entry",
+                                 "org.apache.catalina.deploy.ContextEnvironment");
+        digester.addSetNext(prefix + "web-app/env-entry",
+                            "addEnvironment",
+                            "org.apache.catalina.deploy.ContextEnvironment");
+
+        digester.addCallMethod(prefix + "web-app/env-entry/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-type",
+                               "setType", 0);
+        digester.addCallMethod(prefix + "web-app/env-entry/env-entry-value",
+                               "setValue", 0);
+
+        digester.addObjectCreate(prefix + "web-app/error-page",
+                                 "org.apache.catalina.deploy.ErrorPage");
+        digester.addSetNext(prefix + "web-app/error-page",
+                            "addErrorPage",
+                            "org.apache.catalina.deploy.ErrorPage");
+
+        digester.addCallMethod(prefix + "web-app/error-page/error-code",
+                               "setErrorCode", 0);
+        digester.addCallMethod(prefix + "web-app/error-page/exception-type",
+                               "setExceptionType", 0);
+        digester.addCallMethod(prefix + "web-app/error-page/location",
+                               "setLocation", 0);
+
+        digester.addObjectCreate(prefix + "web-app/filter",
+                                 "org.apache.catalina.deploy.FilterDef");
+        digester.addSetNext(prefix + "web-app/filter",
+                            "addFilterDef",
+                            "org.apache.catalina.deploy.FilterDef");
+
+        digester.addCallMethod(prefix + "web-app/filter/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/filter/display-name",
+                               "setDisplayName", 0);
+        digester.addCallMethod(prefix + "web-app/filter/filter-class",
+                               "setFilterClass", 0);
+        digester.addCallMethod(prefix + "web-app/filter/filter-name",
+                               "setFilterName", 0);
+        digester.addCallMethod(prefix + "web-app/filter/large-icon",
+                               "setLargeIcon", 0);
+        digester.addCallMethod(prefix + "web-app/filter/small-icon",
+                               "setSmallIcon", 0);
+
+        digester.addCallMethod(prefix + "web-app/filter/init-param",
+                               "addInitParameter", 2);
+        digester.addCallParam(prefix + "web-app/filter/init-param/param-name",
+                              0);
+        digester.addCallParam(prefix + "web-app/filter/init-param/param-value",
+                              1);
+
+        digester.addObjectCreate(prefix + "web-app/filter-mapping",
+                                 "org.apache.catalina.deploy.FilterMaps");
+        digester.addSetNext(prefix + "web-app/filter-mapping",
+                            "addFilterMaps",
+                            "org.apache.catalina.deploy.FilterMaps");
+
+        digester.addCallMethod(prefix + "web-app/filter-mapping/filter-name",
+                               "setFilterName", 0);
+        digester.addCallMethod(prefix + "web-app/filter-mapping/servlet-name",
+                               "addServletName", 0);
+        digester.addCallMethod(prefix + "web-app/filter-mapping/url-pattern",
+                               "addURLPattern", 0);
+        // added by Greg Murray
+        digester.addCallMethod(prefix + "web-app/filter-mapping/dispatcher",
+                               "setDispatcher", 0);
+
+         digester.addCallMethod(prefix + "web-app/listener/listener-class",
+                                "addApplicationListener", 0);
+         
+        digester.addRule(prefix + "web-app/jsp-config",
+                         jspConfig);
+        
+        digester.addCallMethod(prefix + "web-app/jsp-config/jsp-property-group/url-pattern",
+                               "addJspMapping", 0);
+
+        digester.addCallMethod(prefix + "web-app/listener/listener-class",
+                               "addApplicationListener", 0);
+        
+        digester.addRule(prefix + "web-app/login-config",
+                         loginConfig);
+
+        digester.addObjectCreate(prefix + "web-app/login-config",
+                                 "org.apache.catalina.deploy.LoginConfig");
+        digester.addSetNext(prefix + "web-app/login-config",
+                            "setLoginConfig",
+                            "org.apache.catalina.deploy.LoginConfig");
+
+        digester.addCallMethod(prefix + "web-app/login-config/auth-method",
+                               "setAuthMethod", 0);
+        digester.addCallMethod(prefix + "web-app/login-config/realm-name",
+                               "setRealmName", 0);
+        digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-error-page",
+                               "setErrorPage", 0);
+        digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-login-page",
+                               "setLoginPage", 0);
+
+        digester.addCallMethod(prefix + "web-app/mime-mapping",
+                               "addMimeMapping", 2);
+        digester.addCallParam(prefix + "web-app/mime-mapping/extension", 0);
+        digester.addCallParam(prefix + "web-app/mime-mapping/mime-type", 1);
+
+        digester.addCallMethod(prefix + "web-app/resource-env-ref",
+                               "addResourceEnvRef", 2);
+        digester.addCallParam(prefix + "web-app/resource-env-ref/resource-env-ref-name", 0);
+        digester.addCallParam(prefix + "web-app/resource-env-ref/resource-env-ref-type", 1);
+
+        digester.addObjectCreate(prefix + "web-app/message-destination",
+                                 "org.apache.catalina.deploy.MessageDestination");
+        digester.addSetNext(prefix + "web-app/message-destination",
+                            "addMessageDestination",
+                            "org.apache.catalina.deploy.MessageDestination");
+
+        digester.addCallMethod(prefix + "web-app/message-destination/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination/display-name",
+                               "setDisplayName", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination/icon/large-icon",
+                               "setLargeIcon", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination/icon/small-icon",
+                               "setSmallIcon", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination/message-destination-name",
+                               "setName", 0);
+
+        digester.addObjectCreate(prefix + "web-app/message-destination-ref",
+                                 "org.apache.catalina.deploy.MessageDestinationRef");
+        digester.addSetNext(prefix + "web-app/message-destination-ref",
+                            "addMessageDestinationRef",
+                            "org.apache.catalina.deploy.MessageDestinationRef");
+
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/message-destination-link",
+                               "setLink", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/message-destination-ref-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/message-destination-type",
+                               "setType", 0);
+        digester.addCallMethod(prefix + "web-app/message-destination-ref/message-destination-usage",
+                               "setUsage", 0);
+
+        digester.addObjectCreate(prefix + "web-app/resource-ref",
+                                 "org.apache.catalina.deploy.ContextResource");
+        digester.addSetNext(prefix + "web-app/resource-ref",
+                            "addResource",
+                            "org.apache.catalina.deploy.ContextResource");
+
+        digester.addCallMethod(prefix + "web-app/resource-ref/description",
+                               "setDescription", 0);
+        digester.addCallMethod(prefix + "web-app/resource-ref/res-auth",
+                               "setAuth", 0);
+        digester.addCallMethod(prefix + "web-app/resource-ref/res-ref-name",
+                               "setName", 0);
+        digester.addCallMethod(prefix + "web-app/resource-ref/res-sharing-scope",
+                               "setScope", 0);
+        digester.addCallMethod(prefix + "web-app/resource-ref/res-type",
+                               "setType", 0);
+
+        digester.addObjectCreate(prefix + "web-app/security-constraint",
+                                 "org.apache.catalina.deploy.SecurityConstraint");
+        digester.addSetNext(prefix + "web-app/security-constraint",
+                            "addConstraint",
+                            "org.apache.catalina.deploy.SecurityConstraint");
+
+        digester.addRule(prefix + "web-app/security-constraint/auth-constraint",
+                         new SetAuthConstraintRule(digester));
+        digester.addCallMethod(prefix + "web-app/security-constraint/auth-constraint/role-name",
+                               "addAuthRole", 0);
+        digester.addCallMethod(prefix + "web-app/security-constraint/display-name",
+                               "setDisplayName", 0);
+        digester.addCallMethod(prefix + "web-app/security-constraint/user-data-constraint/transport-guarantee",
+                               "setUserConstraint", 0);
+
+        digester.addObjectCreate(prefix + "web-app/security-constraint/web-resource-collection",
+                                 "org.apache.catalina.deploy.SecurityCollection");
+        digester.addSetNext(prefix + "web-app/security-constraint/web-resource-collection",
+                            "addCollection",
+                            "org.apache.catalina.deploy.SecurityCollection");
+        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/http-method",
+                               "addMethod", 0);
+        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/url-pattern",
+                               "addPattern", 0);
+        digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/web-resource-name",
+                               "setName", 0);
+
+        digester.addCallMethod(prefix + "web-app/security-role/role-name",
+                               "addSecurityRole", 0);
+
+        digester.addRule(prefix + "web-app/servlet",
+                         new WrapperCreateRule(digester));
+        digester.addSetNext(prefix + "web-app/servlet",
+                            "addChild",
+                            "org.apache.catalina.Container");
+
+        digester.addCallMethod(prefix + "web-app/servlet/init-param",
+                               "addInitParameter", 2);
+        digester.addCallParam(prefix + "web-app/servlet/init-param/param-name",
+                              0);
+        digester.addCallParam(prefix + "web-app/servlet/init-param/param-value",
+                              1);
+
+        digester.addCallMethod(prefix + "web-app/servlet/jsp-file",
+                               "setJspFile", 0);
+        digester.addCallMethod(prefix + "web-app/servlet/load-on-startup",
+                               "setLoadOnStartupString", 0);
+        digester.addCallMethod(prefix + "web-app/servlet/run-as/role-name",
+                               "setRunAs", 0);
+
+        digester.addCallMethod(prefix + "web-app/servlet/security-role-ref",
+                               "addSecurityReference", 2);
+        digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-link", 1);
+        digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-name", 0);
+
+        digester.addCallMethod(prefix + "web-app/servlet/servlet-class",
+                              "setServletClass", 0);
+        digester.addCallMethod(prefix + "web-app/servlet/servlet-name",
+                              "setName", 0);
+
+        digester.addObjectCreate(prefix + "web-app/servlet-mapping",
+                                 "org.apache.catalina.deploy.ServletMap");
+        digester.addSetNext(prefix + "web-app/servlet-mapping",
+                            "addServletMapping",
+                            "org.apache.catalina.deploy.ServletMap");
+        digester.addCallMethod(prefix + "web-app/servlet-mapping/servlet-name",
+                               "setServletName", 0);
+        digester.addCallMethod(prefix + "web-app/servlet-mapping/url-pattern",
+                               "addURLPattern", 0);
+
+        digester.addRule(prefix + "web-app/session-config",
+                         sessionConfig);
+        
+        digester.addCallMethod(prefix + "web-app/session-config/session-timeout",
+                               "setSessionTimeout", 1,
+                               new Class[] { Integer.TYPE });
+        digester.addCallParam(prefix + "web-app/session-config/session-timeout", 0);
+
+        digester.addCallMethod(prefix + "web-app/taglib",
+                               "addTaglib", 2);
+        digester.addCallParam(prefix + "web-app/taglib/taglib-location", 1);
+        digester.addCallParam(prefix + "web-app/taglib/taglib-uri", 0);
+
+        digester.addCallMethod(prefix + "web-app/welcome-file-list/welcome-file",
+                               "addWelcomeFile", 0);
+
+        digester.addCallMethod(prefix + "web-app/locale-encoding-mapping-list/locale-encoding-mapping",
+                              "addLocaleEncodingMappingParameter", 2);
+        digester.addCallParam(prefix + "web-app/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0);
+        digester.addCallParam(prefix + "web-app/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1);
+
+        // START GlassFish 747
+        digester.addCallMethod(prefix + "web-app/jsp-config/taglib",
+                               "addTaglib", 2);
+        digester.addCallParam(prefix + "web-app/jsp-config/taglib/taglib-uri", 0);
+        digester.addCallParam(prefix + "web-app/jsp-config/taglib/taglib-location", 1);
+        // END GlassFish 747
+    }
+
+    /**
+     * Reset counter used for validating the web.xml file.
+     */
+    public void recycle() {
+        if (jspConfig!=null) {
+            jspConfig.isJspConfigSet = false;
+        }
+        if (sessionConfig!=null) {
+            sessionConfig.isSessionConfigSet = false;
+        }
+        if (loginConfig!=null) {
+            loginConfig.isLoginConfigSet = false;
+        }
+    }
+}
+
+
+// ----------------------------------------------------------- Private Classes
+
+
+/**
+ * Rule to check that the <code>login-config</code> is occuring 
+ * only 1 time within the web.xml
+ */
+final class SetLoginConfig extends Rule {
+    boolean isLoginConfigSet = false;
+    public SetLoginConfig(Digester digester) {
+        super(digester);
+    }
+
+    public void begin(Attributes attributes) throws Exception {
+        if (isLoginConfigSet){
+            throw new IllegalArgumentException(
+            "<login-config> element is limited to 1 occurance");
+        }
+        isLoginConfigSet = true;
+    }
+
+}
+
+
+/**
+ * Rule to check that the <code>jsp-config</code> is occuring 
+ * only 1 time within the web.xml
+ */
+final class SetJspConfig extends Rule {
+    boolean isJspConfigSet = false;
+    public SetJspConfig(Digester digester) {
+        super(digester);
+    }
+
+    public void begin(Attributes attributes) throws Exception {
+        if (isJspConfigSet){
+            throw new IllegalArgumentException(
+            "<jsp-config> element is limited to 1 occurance");
+        }
+        isJspConfigSet = true;
+    }
+
+}
+
+
+/**
+ * Rule to check that the <code>jsp-config</code> is occuring 
+ * only 1 time within the web.xml
+ */
+final class SetSessionConfig extends Rule {
+    boolean isSessionConfigSet = false;
+    public SetSessionConfig(Digester digester) {
+        super(digester);
+    }
+
+    public void begin(Attributes attributes) throws Exception {
+        if (isSessionConfigSet){
+            throw new IllegalArgumentException(
+            "<session-config> element is limited to 1 occurance");
+        }
+        isSessionConfigSet = true;
+    }
+
+}
+
+/**
+ * A Rule that calls the <code>setAuthConstraint(true)</code> method of
+ * the top item on the stack, which must be of type
+ * <code>org.apache.catalina.deploy.SecurityConstraint</code>.
+ */
+
+final class SetAuthConstraintRule extends Rule {
+
+    public SetAuthConstraintRule(Digester digester) {
+        super(digester);
+    }
+
+    public void begin(Attributes attributes) throws Exception {
+        SecurityConstraint securityConstraint =
+            (SecurityConstraint) digester.peek();
+        securityConstraint.setAuthConstraint(true);
+        if (digester.getDebug() > 0)
+            digester.log("Calling SecurityConstraint.setAuthConstraint(true)");
+    }
+
+}
+
+
+/**
+ * Class that calls <code>setDistributable(true)</code> for the top object
+ * on the stack, which must be a <code>org.apache.catalina.Context</code>.
+ */
+
+final class SetDistributableRule extends Rule {
+
+    public SetDistributableRule(Digester digester) {
+        super(digester);
+    }
+
+    public void begin(Attributes attributes) throws Exception {
+        Context context = (Context) digester.peek();
+        context.setDistributable(true);
+        if (digester.getDebug() > 0)
+            digester.log(context.getClass().getName() +
+                         ".setDistributable( true)");
+    }
+
+}
+
+
+/**
+ * Class that calls a property setter for the top object on the stack,
+ * passing the public ID of the entity we are currently processing.
+ */
+
+final class SetPublicIdRule extends Rule {
+
+    public SetPublicIdRule(Digester digester, String method) {
+        super(digester);
+        this.method = method;
+    }
+
+    private String method = null;
+
+    public void begin(Attributes attributes) throws Exception {
+
+        Object top = digester.peek();
+        Class paramClasses[] = new Class[1];
+        paramClasses[0] = "String".getClass();
+        String paramValues[] = new String[1];
+        paramValues[0] = digester.getPublicId();
+
+        Method m = null;
+        try {
+            m = top.getClass().getMethod(method, paramClasses);
+        } catch (NoSuchMethodException e) {
+            digester.log("Can't find method " + method + " in " + top +
+                         " CLASS " + top.getClass());
+            return;
+        }
+
+        m.invoke(top, (Object[])paramValues);
+        if (digester.getDebug() >= 1)
+            digester.log("" + top.getClass().getName() + "." + method +
+                        "(" + paramValues[0] + ")");
+
+    }
+
+}
+
+
+/**
+ * A Rule that calls the factory method on the specified Context to
+ * create the object that is to be added to the stack.
+ */
+
+final class WrapperCreateRule extends Rule {
+
+    public WrapperCreateRule(Digester digester) {
+        super(digester);
+    }
+
+    public void begin(Attributes attributes) throws Exception {
+        Context context =
+            (Context) digester.peek(digester.getCount() - 1);
+        Wrapper wrapper = context.createWrapper();
+        digester.push(wrapper);
+        if (digester.getDebug() > 0)
+            digester.log("new " + wrapper.getClass().getName());
+    }
+
+    public void end() throws Exception {
+        Wrapper wrapper = (Wrapper) digester.pop();
+        if (digester.getDebug() > 0)
+            digester.log("pop " + wrapper.getClass().getName());
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/Base64.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/Base64.java
new file mode 100644
index 0000000..0859662
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/Base64.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.nio.charset.Charset;
+
+/**
+ * This class provides encode/decode for RFC 2045 Base64 as defined by
+ * RFC 2045, N. Freed and N. Borenstein.  <a
+ * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>:
+ * Multipurpose Internet Mail Extensions (MIME) Part One: Format of
+ * Internet Message Bodies. Reference 1996
+ *
+ * @author Jeffrey Rodriguez
+ * @version $Id: Base64.java,v 1.2 2005/12/08 01:28:14 kchung Exp $
+ */
+public final class  Base64
+{
+    static private final int  BASELENGTH         = 255;
+    static private final int  LOOKUPLENGTH       = 64;
+    static private final int  TWENTYFOURBITGROUP = 24;
+    static private final int  EIGHTBIT           = 8;
+    static private final int  SIXTEENBIT         = 16;
+    static private final int  SIXBIT             = 6;
+    static private final int  FOURBYTE           = 4;
+    static private final int  SIGN               = -128;
+    static private final byte PAD                = (byte) '=';
+    static private byte [] base64Alphabet       = new byte[BASELENGTH];
+    static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+    //static private final Log log = LogSource.getInstance("org.apache.commons.util.Base64");
+
+    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);
+
+        lookUpBase64Alphabet[62] = (byte) '+';
+        lookUpBase64Alphabet[63] = (byte) '/';
+    }
+
+    public static boolean isBase64( String isValidString )
+    {
+        return isArrayByteBase64(isValidString.getBytes(Charset.defaultCharset()));
+    }
+
+    public static boolean isBase64( byte octect )
+    {
+        //shall we ignore white space? JEFF??
+        return (octect == PAD || base64Alphabet[octect] != -1);
+    }
+
+    public static boolean isArrayByteBase64( byte[] arrayOctect )
+    {
+        int length = arrayOctect.length;
+        if (length == 0)
+        {
+            // shouldn't a 0 length array be valid base64 data?
+            // return false;
+            return true;
+        }
+        for (int i=0; i < length; i++)
+        {
+            if ( !Base64.isBase64(arrayOctect[i]) )
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Encodes hex octets into Base64.
+     *
+     * @param binaryData Array containing binary data to encode.
+     * @return Base64-encoded data.
+     */
+    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;
+        //log.debug("number of triplets = " + numberTriplets);
+        for ( i = 0; i<numberTriplets; i++ )
+        {
+            dataIndex = i*3;
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            b3 = binaryData[dataIndex + 2];
+
+            //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
+
+            l  = (byte)(b2 & 0x0f);
+            k  = (byte)(b1 & 0x03);
+
+            encodedIndex = i * 4;
+            byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+            byte val2 = ((b2 & SIGN)==0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
+            byte val3 = ((b3 & SIGN)==0)?(byte)(b3>>6):(byte)((b3)>>6^0xfc);
+
+            encodedData[encodedIndex]   = lookUpBase64Alphabet[ val1 ];
+            //log.debug( "val2 = " + val2 );
+            //log.debug( "k4   = " + (k<<4) );
+            //log.debug(  "vak  = " + (val2 | (k<<4)) );
+            encodedData[encodedIndex+1] =
+                lookUpBase64Alphabet[ val2 | ( k<<4 )];
+            encodedData[encodedIndex+2] =
+                lookUpBase64Alphabet[ (l <<2 ) | val3 ];
+            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 );
+            //log.debug("b1=" + b1);
+            //log.debug("b1<<2 = " + (b1>>2) );
+            byte val1 = ((b1 & SIGN)==0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ val1 ];
+            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);
+
+            byte val1 = ((b1 & SIGN) == 0)?(byte)(b1>>2):(byte)((b1)>>2^0xc0);
+            byte val2 = ((b2 & SIGN) == 0)?(byte)(b2>>4):(byte)((b2)>>4^0xf0);
+
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ val1 ];
+            encodedData[encodedIndex + 1] =
+                lookUpBase64Alphabet[ val2 | ( k<<4 )];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+
+        return encodedData;
+    }
+
+    /**
+     * Decodes Base64 data into octets
+     *
+     * @param binaryData Byte array containing Base64 data
+     * @return Array containing decoded data.
+     */
+    public static byte[] decode( byte[] base64Data )
+    {
+        // handle the edge case, so we don't have to worry about it later
+        if(base64Data.length == 0) { return new byte[0]; }
+
+        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
+
+        int encodedIndex = 0;
+        int dataIndex    = 0;
+        {
+            // this sizes the output array properly - rlw
+            int lastData = base64Data.length;
+            // ignore the '=' padding
+            while (base64Data[lastData-1] == PAD)
+            {
+                if (--lastData == 0)
+                {
+                    return new byte[0];
+                }
+            }
+            decodedData = new byte[ lastData - numberQuadruple ];
+        }
+
+        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 ) ;
+            }
+            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) );
+            }
+            encodedIndex += 3;
+        }
+        return decodedData;
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/CharsetMapper.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/CharsetMapper.java
new file mode 100644
index 0000000..69d8ec3
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/CharsetMapper.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This class is based on a class originally written by Jason Hunter
+ * <jhunter@acm.org> as part of the book "Java Servlet Programming"
+ * (O'Reilly).  See http://www.servlets.com/book for more information.
+ * Used by Sun Microsystems with permission.
+ */
+
+package org.apache.catalina.util;
+
+
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.Properties;
+
+
+
+/**
+ * Utility class that attempts to map from a Locale to the corresponding
+ * character set to be used for interpreting input text (or generating
+ * output text) when the Content-Type header does not include one.  You
+ * can customize the behavior of this class by modifying the mapping data
+ * it loads, or by subclassing it (to change the algorithm) and then using
+ * your own version for a particular web application.
+ *
+ * @author Craig R. McClanahan
+ * @revision $Date: 2005/12/08 01:28:14 $ $Version$
+ */
+
+/* SJSAS 6292972
+public class CharsetMapper {
+*/
+// START SJSAS 6292972
+public class CharsetMapper implements Cloneable {
+// END SJSAS 6292972
+
+
+    // ---------------------------------------------------- Manifest Constants
+    private static final char HYPHEN = '-';
+
+    /**
+     * Default properties resource name.
+     */
+    public static final String DEFAULT_RESOURCE =
+      "/org/apache/catalina/util/CharsetMapperDefault.properties";
+
+
+    // ---------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new CharsetMapper using the default properties resource.
+     */
+    public CharsetMapper() {
+
+        this(DEFAULT_RESOURCE);
+
+    }
+
+
+    /**
+     * Construct a new CharsetMapper using the specified properties resource.
+     *
+     * @param name Name of a properties resource to be loaded
+     *
+     * @exception IllegalArgumentException if the specified properties
+     *  resource could not be loaded for any reason.
+     */
+    public CharsetMapper(String name) {
+
+        InputStream stream = null;
+        try {
+            stream =
+                this.getClass().getResourceAsStream(name);
+            map.load(stream);
+        } catch (Throwable t) {
+            throw new IllegalArgumentException(t.toString());
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch(Throwable t2) {
+                }
+            }
+        }
+
+
+    }
+
+
+    // ---------------------------------------------------- Instance Variables
+
+
+    /**
+     * The mapping properties that have been initialized from the specified or
+     * default properties resource.
+     */
+    // START RIMOD 4870531
+    /*
+    private Properties map = new Properties();
+    */
+    protected Properties map = new Properties();
+    // END RIMOD 4870531
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Calculate the name of a character set to be assumed, given the specified
+     * Locale and the absence of a character set specified as part of the
+     * content type header.
+     *
+     * @param locale The locale for which to calculate a character set
+     */
+    public String getCharset(Locale locale) {
+
+        String charset = null;
+
+        // First, try a full name match (language and country)
+        charset = map.getProperty(locale.toString());
+        if (charset != null)
+            return (charset);
+
+        // Second, try to match just the language
+        charset = map.getProperty(locale.getLanguage());
+        return (charset);
+
+    }
+
+    /**
+     * The deployment descriptor can have a
+     * locale-encoding-mapping-list element which describes the
+     * webapp's desired mapping from locale to charset.  This method
+     * gets called when processing the web.xml file for a context
+     *
+     * @param locale The locale for a character set
+     * @param charset The charset to be associated with the locale
+     */
+    public void addCharsetMappingFromDeploymentDescriptor(String locale,String charset) {
+        if (((locale.indexOf(HYPHEN)) > -1)) {
+            map.put(Locale.forLanguageTag(locale).toString(),charset);
+        }
+        else{
+            map.put( locale, charset );
+        }
+    }
+
+
+    // START SJSAS 6292972
+    public final Object clone() {
+        
+        try {
+            CharsetMapper clone = (CharsetMapper)super.clone();
+            clone.map = (Properties)map.clone();
+            return clone;
+        } catch (CloneNotSupportedException e) {
+            throw new InternalError(e.toString());
+        }
+    }
+    // END SJSAS 6292972
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/CustomObjectInputStream.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/CustomObjectInputStream.java
new file mode 100644
index 0000000..e805221
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/CustomObjectInputStream.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+
+/**
+ * Custom subclass of <code>ObjectInputStream</code> that loads from the
+ * class loader for this web application.  This allows classes defined only
+ * with the web application to be found correctly.
+ *
+ * @author Craig R. McClanahan
+ * @author Bip Thelin
+ * @version $Revision: 1.2 $, $Date: 2005/12/08 01:28:15 $
+ */
+
+public final class CustomObjectInputStream
+    extends ObjectInputStream {
+
+
+    /**
+     * The class loader we will use to resolve classes.
+     */
+    private ClassLoader classLoader = null;
+
+
+    /**
+     * Construct a new instance of CustomObjectInputStream
+     *
+     * @param stream The input stream we will read from
+     * @param classLoader The class loader used to instantiate objects
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public CustomObjectInputStream(InputStream stream,
+                                   ClassLoader classLoader)
+        throws IOException {
+
+        super(stream);
+        this.classLoader = classLoader;
+    }
+
+
+    /**
+     * Load the local class equivalent of the specified stream class
+     * description, by using the class loader assigned to this Context.
+     *
+     * @param classDesc Class description from the input stream
+     *
+     * @exception ClassNotFoundException if this class cannot be found
+     * @exception IOException if an input/output error occurs
+     */
+    public Class resolveClass(ObjectStreamClass classDesc)
+             throws ClassNotFoundException, IOException {
+        try {
+            return Class.forName(classDesc.getName(), false, classLoader);
+        } catch (ClassNotFoundException e) {
+            try {
+                // Try also the superclass because of primitive types
+                return super.resolveClass(classDesc);
+            }  catch (ClassNotFoundException e2) {
+                // Rethrow original exception, as it can have more information
+                // about why the class was not found. BZ 48007
+                throw e;
+            }
+        }
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/DOMWriter.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/DOMWriter.java
new file mode 100644
index 0000000..097f19a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/DOMWriter.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import org.w3c.dom.*;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+/**
+ * A sample DOM writer. This sample program illustrates how to
+ * traverse a DOM tree in order to print a document that is parsed.
+ */
+public class DOMWriter {
+
+   //
+   // Data
+   //
+
+   /** Default Encoding */
+   private static  String
+   PRINTWRITER_ENCODING = "UTF8";
+
+   private static String MIME2JAVA_ENCODINGS[] =
+    { "Default", "UTF-8", "US-ASCII", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4",
+      "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "ISO-2022-JP",
+      "SHIFT_JIS", "EUC-JP","GB2312", "BIG5", "EUC-KR", "ISO-2022-KR", "KOI8-R", "EBCDIC-CP-US",
+      "EBCDIC-CP-CA", "EBCDIC-CP-NL", "EBCDIC-CP-DK", "EBCDIC-CP-NO", "EBCDIC-CP-FI", "EBCDIC-CP-SE",
+      "EBCDIC-CP-IT", "EBCDIC-CP-ES", "EBCDIC-CP-GB", "EBCDIC-CP-FR", "EBCDIC-CP-AR1",
+      "EBCDIC-CP-HE", "EBCDIC-CP-CH", "EBCDIC-CP-ROECE","EBCDIC-CP-YU",
+      "EBCDIC-CP-IS", "EBCDIC-CP-AR2", "UTF-16"
+    };
+
+   /** Output qualified names */
+   private boolean qualifiedNames = true;
+
+   /** Print writer. */
+   protected PrintWriter out;
+
+   /** Canonical output. */
+   protected boolean canonical;
+
+
+   public DOMWriter(String encoding, boolean canonical)
+   throws UnsupportedEncodingException {
+      out = new PrintWriter(new OutputStreamWriter(System.out, encoding));
+      this.canonical = canonical;
+   } // <init>(String,boolean)
+
+   //
+   // Constructors
+   //
+
+   /** Default constructor. */
+   public DOMWriter(boolean canonical) throws UnsupportedEncodingException {
+      this( getWriterEncoding(), canonical);
+   }
+
+    public DOMWriter(Writer writer, boolean canonical) {
+        out = new PrintWriter(writer);
+        this.canonical = canonical;
+    }
+
+   public boolean getQualifiedNames() {
+      return this.qualifiedNames;
+   }
+
+   public void setQualifiedNames(boolean qualifiedNames) {
+      this.qualifiedNames = qualifiedNames;
+   }
+
+   public static String getWriterEncoding( ) {
+      return (PRINTWRITER_ENCODING);
+   }// getWriterEncoding
+
+   public static void  setWriterEncoding( String encoding ) {
+      if( encoding.equalsIgnoreCase( "DEFAULT" ) )
+         PRINTWRITER_ENCODING  = "UTF8";
+      else if( encoding.equalsIgnoreCase( "UTF-16" ) )
+         PRINTWRITER_ENCODING  = "Unicode";
+      else
+         PRINTWRITER_ENCODING = MIME2Java.convert( encoding );
+   }// setWriterEncoding
+
+
+   public static boolean isValidJavaEncoding( String encoding ) {
+      for ( int i = 0; i < MIME2JAVA_ENCODINGS.length; i++ )
+         if ( encoding.equals( MIME2JAVA_ENCODINGS[i] ) )
+            return (true);
+
+      return (false);
+   }// isValidJavaEncoding
+
+
+   /** Prints the specified node, recursively. */
+   public void print(Node node) {
+
+      // is there anything to do?
+      if ( node == null ) {
+         return;
+      }
+
+      int type = node.getNodeType();
+      switch ( type ) {
+         // print document
+         case Node.DOCUMENT_NODE: {
+               if ( !canonical ) {
+                  String  Encoding = getWriterEncoding();
+                  if( Encoding.equalsIgnoreCase( "DEFAULT" ) )
+                     Encoding = "UTF-8";
+                  else if( Encoding.equalsIgnoreCase( "Unicode" ) )
+                     Encoding = "UTF-16";
+                  else
+                     Encoding = MIME2Java.reverse( Encoding );
+
+                  out.println("<?xml version=\"1.0\" encoding=\""+
+                           Encoding + "\"?>");
+               }
+               print(((Document)node).getDocumentElement());
+               out.flush();
+               break;
+            }
+
+            // print element with attributes
+         case Node.ELEMENT_NODE: {
+               out.print('<');
+               if (this.qualifiedNames) { 
+                  out.print(node.getNodeName());
+               } else {
+                  out.print(node.getLocalName());
+               }
+               Attr attrs[] = sortAttributes(node.getAttributes());
+               for ( int i = 0; i < attrs.length; i++ ) {
+                  Attr attr = attrs[i];
+                  out.print(' ');
+                  if (this.qualifiedNames) {
+                     out.print(attr.getNodeName());
+                  } else {
+                     out.print(attr.getLocalName());
+                  }
+                  
+                  out.print("=\"");
+                  out.print(normalize(attr.getNodeValue()));
+                  out.print('"');
+               }
+               out.print('>');
+               NodeList children = node.getChildNodes();
+               if ( children != null ) {
+                  int len = children.getLength();
+                  for ( int i = 0; i < len; i++ ) {
+                     print(children.item(i));
+                  }
+               }
+               break;
+            }
+
+            // handle entity reference nodes
+         case Node.ENTITY_REFERENCE_NODE: {
+               if ( canonical ) {
+                  NodeList children = node.getChildNodes();
+                  if ( children != null ) {
+                     int len = children.getLength();
+                     for ( int i = 0; i < len; i++ ) {
+                        print(children.item(i));
+                     }
+                  }
+               } else {
+                  out.print('&');
+                  if (this.qualifiedNames) {
+                     out.print(node.getNodeName());
+                  } else {
+                     out.print(node.getLocalName());
+                  }
+                  out.print(';');
+               }
+               break;
+            }
+
+            // print cdata sections
+         case Node.CDATA_SECTION_NODE: {
+               if ( canonical ) {
+                  out.print(normalize(node.getNodeValue()));
+               } else {
+                  out.print("<![CDATA[");
+                  out.print(node.getNodeValue());
+                  out.print("]]>");
+               }
+               break;
+            }
+
+            // print text
+         case Node.TEXT_NODE: {
+               out.print(normalize(node.getNodeValue()));
+               break;
+            }
+
+            // print processing instruction
+         case Node.PROCESSING_INSTRUCTION_NODE: {
+               out.print("<?");
+               if (this.qualifiedNames) {
+                  out.print(node.getNodeName());
+               } else {
+                  out.print(node.getLocalName());
+               }
+               
+               String data = node.getNodeValue();
+               if ( data != null && data.length() > 0 ) {
+                  out.print(' ');
+                  out.print(data);
+               }
+               out.print("?>");
+               break;
+            }
+      }
+
+      if ( type == Node.ELEMENT_NODE ) {
+         out.print("</");
+         if (this.qualifiedNames) {
+            out.print(node.getNodeName());
+         } else {
+            out.print(node.getLocalName());
+         }
+         out.print('>');
+      }
+
+      out.flush();
+
+   } // print(Node)
+
+   /** Returns a sorted list of attributes. */
+   protected Attr[] sortAttributes(NamedNodeMap attrs) {
+
+      int len = (attrs != null) ? attrs.getLength() : 0;
+      Attr array[] = new Attr[len];
+      for ( int i = 0; i < len; i++ ) {
+         array[i] = (Attr)attrs.item(i);
+      }
+      for ( int i = 0; i < len - 1; i++ ) {
+         String name = null;
+         if (this.qualifiedNames) {
+            name  = array[i].getNodeName();
+         } else {
+            name  = array[i].getLocalName();
+         }
+         int    index = i;
+         for ( int j = i + 1; j < len; j++ ) {
+            String curName = null;
+            if (this.qualifiedNames) {
+               curName = array[j].getNodeName();
+            } else {
+               curName = array[j].getLocalName();
+            }
+            if ( curName.compareTo(name) < 0 ) {
+               name  = curName;
+               index = j;
+            }
+         }
+         if ( index != i ) {
+            Attr temp    = array[i];
+            array[i]     = array[index];
+            array[index] = temp;
+         }
+      }
+
+      return (array);
+
+   } // sortAttributes(NamedNodeMap):Attr[]
+
+
+   /** Normalizes the given string. */
+   protected String normalize(String s) {
+      StringBuilder str = new StringBuilder();
+
+      int len = (s != null) ? s.length() : 0;
+      for ( int i = 0; i < len; i++ ) {
+         char ch = s.charAt(i);
+         switch ( ch ) {
+            case '<': {
+                  str.append("&lt;");
+                  break;
+               }
+            case '>': {
+                  str.append("&gt;");
+                  break;
+               }
+            case '&': {
+                  str.append("&amp;");
+                  break;
+               }
+            case '"': {
+                  str.append("&quot;");
+                  break;
+               }
+            case '\r':
+            case '\n': {
+                  if ( canonical ) {
+                     str.append("&#");
+                     str.append(Integer.toString(ch));
+                     str.append(';');
+                     break;
+                  }
+                  // else, default append char
+               }
+            default: {
+                  str.append(ch);
+               }
+         }
+      }
+
+      return (str.toString());
+
+   } // normalize(String):String
+
+   /*
+   private static void printValidJavaEncoding() {
+      System.err.println( "    ENCODINGS:" );
+      System.err.print( "   " );
+      for( int i = 0;
+                     i < MIME2JAVA_ENCODINGS.length; i++) {
+         System.err.print( MIME2JAVA_ENCODINGS[i] + " " );
+      if( (i % 7 ) == 0 ){
+         System.err.println();
+         System.err.print( "   " );
+         }
+      }
+
+   } // printJavaEncoding()
+   */
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/DigestEncoder.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/DigestEncoder.java
new file mode 100644
index 0000000..ebb69e5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/DigestEncoder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+/**
+ * Encode a digest into a String.
+ * <p>
+ * The digest hash is converted into a character long String.
+ * Each character of the String is the hexadecimal representation of 4 bits
+ * of the digest.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:18 $
+ */
+
+public final class DigestEncoder extends DigestEncoderBase {
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/DigestEncoderBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/DigestEncoderBase.java
new file mode 100644
index 0000000..faa7e7e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/DigestEncoderBase.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+/**
+ * Encode a digest into a String.
+ * <p>
+ * The digest hash is converted into a character long String.
+ * Each character of the String is the hexadecimal representation of 4 bits
+ * of the digest.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:18 $
+ */
+
+class DigestEncoderBase {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    private static final char[] hexadecimal =
+    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+     'a', 'b', 'c', 'd', 'e', 'f'};
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Encodes the 8m bit (m bytes) MD5 into a 2m character String.
+     *
+     * @param binaryData Array containing the digest
+     * @return Encoded digest, or null if encoding failed
+     */
+    public char[] encode( byte[] binaryData ) {
+
+        char[] buffer = new char[2 * binaryData.length];
+
+        for (int i=0; i < binaryData.length; i++) {
+            int low = (int) (binaryData[i] & 0x0f);
+            int high = (int) ((binaryData[i] & 0xf0) >> 4);
+            buffer[i*2] = hexadecimal[high];
+            buffer[i*2 + 1] = hexadecimal[low];
+        }
+
+        return buffer;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/Enumerator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/Enumerator.java
new file mode 100644
index 0000000..528e215
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/Enumerator.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+
+/**
+ * Adapter class that wraps an <code>Enumeration</code> around a Java2
+ * collection classes object <code>Iterator</code> so that existing APIs
+ * returning Enumerations can easily run on top of the new collections.
+ * Constructors are provided to easliy create such wrappers.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:15 $
+ */
+
+public final class Enumerator<T> implements Enumeration<T> {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Return an Enumeration over the values of the specified Collection.
+     *
+     * @param collection Collection whose values should be enumerated
+     */
+    public Enumerator(Collection<T> collection) {
+
+        this(collection.iterator());
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Collection.
+     *
+     * @param collection Collection whose values should be enumerated
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Collection<T> collection, boolean clone) {
+
+        this(collection.iterator(), clone);
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values returned by the
+     * specified Iterator.
+     *
+     * @param iterator Iterator to be wrapped
+     */
+    public Enumerator(Iterator<T> iterator) {
+        this.iterator = iterator;
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values returned by the
+     * specified Iterator.
+     *
+     * @param iterator Iterator to be wrapped
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Iterator<T> iterator, boolean clone) {
+        if (!clone) {
+            this.iterator = iterator;
+        } else {
+            List<T> list = new ArrayList<T>();
+            while (iterator.hasNext()) {
+                list.add(iterator.next());
+            }
+            this.iterator = list.iterator();   
+        }
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Map.
+     *
+     * @param map Map whose values should be enumerated
+     */
+    public Enumerator(Map<?, T> map) {
+
+        this(map.values().iterator());
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Map.
+     *
+     * @param map Map whose values should be enumerated
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Map<?, T> map, boolean clone) {
+
+        this(map.values().iterator(), clone);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>Iterator</code> over which the <code>Enumeration</code>
+     * represented by this class actually operates.
+     */
+    private Iterator<T> iterator;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Tests if this enumeration contains more elements.
+     *
+     * @return <code>true</code> if and only if this enumeration object
+     *  contains at least one more element to provide, <code>false</code>
+     *  otherwise
+     */
+    @Override
+    public boolean hasMoreElements() {
+
+        return iterator.hasNext();
+
+    }
+
+
+    /**
+     * Returns the next element of this enumeration if this enumeration
+     * has at least one more element to provide.
+     *
+     * @return the next element of this enumeration
+     *
+     * @exception NoSuchElementException if no more elements exist
+     */
+    @Override
+    public T nextElement() throws NoSuchElementException {
+
+        return iterator.next();
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/Extension.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/Extension.java
new file mode 100644
index 0000000..37d6ca7
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/Extension.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+import java.util.StringTokenizer;
+
+
+/**
+ * Utility class that represents either an available "Optional Package"
+ * (formerly known as "Standard Extension") as described in the manifest
+ * of a JAR file, or the requirement for such an optional package.  It is
+ * used to support the requirements of the Servlet Specification, version
+ * 2.3, related to providing shared extensions to all webapps.
+ * <p>
+ * In addition, static utility methods are available to scan a manifest
+ * and return an array of either available or required optional modules
+ * documented in that manifest.
+ * <p>
+ * For more information about optional packages, see the document
+ * <em>Optional Package Versioning</em> in the documentation bundle for your
+ * Java2 Standard Edition package, in file
+ * <code>guide/extensions/versioning.html</code>.
+ *
+ * @author Craig McClanahan
+ * @author Justyna Horwat
+ * @author Greg Murray
+ * @version $Revision: 1.3 $ $Date: 2006/11/06 20:14:21 $
+ */
+
+public final class Extension {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The name of the optional package being made available, or required.
+     */
+    private String extensionName = null;
+    
+
+    public String getExtensionName() {
+        return (this.extensionName);
+    }
+
+    public void setExtensionName(String extensionName) {
+        if (extensionName != null) {
+            this.extensionName = extensionName.trim();
+        }
+    }
+
+    /**
+     * UniqueId created by combining the extension name and implementation
+     * version. 
+     */
+    public String getUniqueId() {
+        return this.extensionName + this.implementationVersion;
+    }
+
+    /**
+     * The URL from which the most recent version of this optional package
+     * can be obtained if it is not already installed.
+     */
+    private String implementationURL = null;
+
+    public String getImplementationURL() {
+        return (this.implementationURL);
+    }
+
+    public void setImplementationURL(String implementationURL) {
+        this.implementationURL = implementationURL;
+    }
+
+
+    /**
+     * The name of the company or organization that produced this
+     * implementation of this optional package.
+     */
+    private String implementationVendor = null;
+
+    public String getImplementationVendor() {
+        return (this.implementationVendor);
+    }
+
+    public void setImplementationVendor(String implementationVendor) {
+        this.implementationVendor = implementationVendor;
+    }
+
+
+    /**
+     * The unique identifier of the company that produced the optional
+     * package contained in this JAR file.
+     */
+    private String implementationVendorId = null;
+
+    public String getImplementationVendorId() {
+        return (this.implementationVendorId);
+    }
+
+    public void setImplementationVendorId(String implementationVendorId) {
+        this.implementationVendorId = implementationVendorId;
+    }
+
+
+    /**
+     * The version number (dotted decimal notation) for this implementation
+     * of the optional package.
+     */
+    private String implementationVersion = null;
+
+    public String getImplementationVersion() {
+        return (this.implementationVersion);
+    }
+
+    public void setImplementationVersion(String implementationVersion) {
+        if (implementationVersion != null) {
+            this.implementationVersion = implementationVersion.trim();
+        }
+    }
+
+
+    /**
+     * The name of the company or organization that originated the
+     * specification to which this optional package conforms.
+     */
+    private String specificationVendor = null;
+
+    public String getSpecificationVendor() {
+        return (this.specificationVendor);
+    }
+
+    public void setSpecificationVendor(String specificationVendor) {
+        this.specificationVendor = specificationVendor;
+    }
+
+
+    /**
+     * The version number (dotted decimal notation) of the specification
+     * to which this optional package conforms.
+     */
+    private String specificationVersion = null;
+
+    public String getSpecificationVersion() {
+        return (this.specificationVersion);
+    }
+
+    public void setSpecificationVersion(String specificationVersion) {
+        this.specificationVersion = specificationVersion;
+    }
+
+
+    /**
+     * fulfilled is true if all the required extension dependencies have been
+     * satisfied
+     */
+    private boolean fulfilled = false;
+
+    public void setFulfilled(boolean fulfilled) {
+        this.fulfilled = fulfilled;
+    }
+    
+    public boolean isFulfilled() {
+        return fulfilled;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Return <code>true</code> if the specified <code>Extension</code>
+     * (which represents an optional package required by this application)
+     * is satisfied by this <code>Extension</code> (which represents an
+     * optional package that is already installed.  Otherwise, return
+     * <code>false</code>.
+     *
+     * @param required Extension of the required optional package
+     */
+    public boolean isCompatibleWith(Extension required) {
+
+        // Extension Name must match
+        if (extensionName == null)
+            return (false);
+        if (!extensionName.equals(required.getExtensionName()))
+            return (false);
+
+        // Available specification version must be >= required
+        if (!isNewer(specificationVersion, required.getSpecificationVersion()))
+            return (false);
+
+        // Implementation Vendor ID must match
+        if (implementationVendorId == null)
+            return (false);
+        if (!implementationVendorId.equals(required.getImplementationVendorId()))
+            return (false);
+
+        // Implementation version must be >= required
+        if (!isNewer(implementationVersion, required.getImplementationVersion()))
+            return (false);
+
+        // This available optional package satisfies the requirements
+        return (true);
+
+    }
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("Extension[");
+        sb.append(extensionName);
+        if (implementationURL != null) {
+            sb.append(", implementationURL=");
+            sb.append(implementationURL);
+        }
+        if (implementationVendor != null) {
+            sb.append(", implementationVendor=");
+            sb.append(implementationVendor);
+        }
+        if (implementationVendorId != null) {
+            sb.append(", implementationVendorId=");
+            sb.append(implementationVendorId);
+        }
+        if (implementationVersion != null) {
+            sb.append(", implementationVersion=");
+            sb.append(implementationVersion);
+        }
+        if (specificationVendor != null) {
+            sb.append(", specificationVendor=");
+            sb.append(specificationVendor);
+        }
+        if (specificationVersion != null) {
+            sb.append(", specificationVersion=");
+            sb.append(specificationVersion);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+
+    /**
+     * Return <code>true</code> if the first version number is greater than
+     * or equal to the second; otherwise return <code>false</code>.
+     *
+     * @param first First version number (dotted decimal)
+     * @param second Second version number (dotted decimal)
+     *
+     * @exception NumberFormatException on a malformed version number
+     */
+    private boolean isNewer(String first, String second)
+        throws NumberFormatException {
+
+        if ((first == null) || (second == null))
+            return (false);
+        if (first.equals(second))
+            return (true);
+
+        StringTokenizer fTok = new StringTokenizer(first, ".", true);
+        StringTokenizer sTok = new StringTokenizer(second, ".", true);
+        int fVersion = 0;
+        int sVersion = 0;
+        while (fTok.hasMoreTokens() || sTok.hasMoreTokens()) {
+            if (fTok.hasMoreTokens())
+                fVersion = Integer.parseInt(fTok.nextToken());
+            else
+                fVersion = 0;
+            if (sTok.hasMoreTokens())
+                sVersion = Integer.parseInt(sTok.nextToken());
+            else
+                sVersion = 0;
+            if (fVersion < sVersion)
+                return (false);
+            else if (fVersion > sVersion)
+                return (true);
+            if (fTok.hasMoreTokens())   // Swallow the periods
+                fTok.nextToken();
+            if (sTok.hasMoreTokens())
+                sTok.nextToken();
+        }
+
+        return (true);  // Exact match
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/ExtensionValidator.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ExtensionValidator.java
new file mode 100644
index 0000000..6bda627
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ExtensionValidator.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.core.StandardContext;
+import org.apache.naming.resources.Resource;
+
+import javax.naming.Binding;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Ensures that all extension dependies are resolved for a WEB application
+ * are met. This class builds a master list of extensions available to an
+ * applicaiton and then validates those extensions.
+ *
+ * See http://java.sun.com/j2se/1.4/docs/guide/extensions/spec.html for
+ * a detailed explanation of the extension mechanism in Java.
+ *
+ * @author Greg Murray
+ * @author Justyna Horwat
+ * @version $Revision: 1.3 $ $Date: 2006/03/12 01:27:08 $
+ *
+ */
+public final class ExtensionValidator {
+
+    private static final Logger log = LogFacade.getLogger();
+    private static final ResourceBundle rb = log.getResourceBundle();
+
+    private static volatile HashMap<String, Extension> containerAvailableExtensions = null;
+    private static ArrayList<ManifestResource> containerManifestResources =
+        new ArrayList<ManifestResource>();
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    /**
+     *  This static initializer loads the container level extensions that are
+     *  available to all web applications. This method scans all extension 
+     *  directories available via the "java.ext.dirs" System property. 
+     *
+     *  The System Class-Path is also scanned for jar files that may contain 
+     *  available extensions.
+     */
+    static {
+
+        // check for container level optional packages
+        String systemClasspath = System.getProperty("java.class.path");
+
+        StringTokenizer strTok = new StringTokenizer(systemClasspath, 
+                                                     File.pathSeparator);
+
+        // build a list of jar files in the classpath
+        while (strTok.hasMoreTokens()) {
+            String classpathItem = strTok.nextToken();
+            if (classpathItem.toLowerCase(Locale.ENGLISH).endsWith(".jar")) {
+                File item = new File(classpathItem);
+                if (item.exists()) {
+                    try {
+                        addSystemResource(item);
+                    } catch (IOException e) {
+                        String msg = MessageFormat.format(rb.getString(LogFacade.FAILED_LOAD_MANIFEST_RESOURCES_EXCEPTION),
+                                                          item);
+                        log.log(Level.SEVERE, msg, e);
+                    }
+                }
+            }
+        }
+
+        // get the files in the extensions directory
+        String extensionsDir = System.getProperty("java.ext.dirs");
+        if (extensionsDir != null) {
+            StringTokenizer extensionsTok
+                = new StringTokenizer(extensionsDir, File.pathSeparator);
+            while (extensionsTok.hasMoreTokens()) {
+                File targetDir = new File(extensionsTok.nextToken());
+                if (!targetDir.exists() || !targetDir.isDirectory()) {
+                    continue;
+                }
+                File[] files = targetDir.listFiles();
+                for (int i = 0; i < files.length; i++) {
+                    if (files[i].getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) {
+                        try {
+                            addSystemResource(files[i]);
+                        } catch (IOException e) {
+                            String msg = MessageFormat.format(rb.getString(LogFacade.FAILED_LOAD_MANIFEST_RESOURCES_EXCEPTION),
+                                                              files[i]);
+                            log.log(Level.SEVERE, msg, e);
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Runtime validation of a Web Applicaiton.
+     *
+     * This method uses JNDI to look up the resources located under a 
+     * <code>DirContext</code>. It locates Web Application MANIFEST.MF 
+     * file in the /META-INF/ directory of the application and all 
+     * MANIFEST.MF files in each JAR file located in the WEB-INF/lib 
+     * directory and creates an <code>ArrayList</code> of 
+     * <code>ManifestResorce<code> objects. These objects are then passed 
+     * to the validateManifestResources method for validation.
+     *
+     * @param dirContext The JNDI root of the Web Application
+     * @param context The context from which the Logger and path to the
+     *                application
+     *
+     * @return true if all required extensions satisfied
+     */
+    public static synchronized boolean validateApplication(
+                                           DirContext dirContext, 
+                                           StandardContext context)
+                    throws IOException {
+
+        String appName = context.getPath();
+        ArrayList<ManifestResource> appManifestResources =
+            new ArrayList<ManifestResource>();
+        ManifestResource appManifestResource = null;
+        // If the application context is null it does not exist and 
+        // therefore is not valid
+        if (dirContext == null) return false;
+        // Find the Manifest for the Web Applicaiton
+        InputStream inputStream = null;
+        try {
+            NamingEnumeration wne = dirContext.listBindings("/META-INF/");
+            Binding binding = (Binding) wne.nextElement();
+            if (binding.getName().toUpperCase(Locale.ENGLISH).equals("MANIFEST.MF")) {
+                Resource resource = (Resource)dirContext.lookup
+                                    ("/META-INF/" + binding.getName());
+                inputStream = resource.streamContent();
+                Manifest manifest = new Manifest(inputStream);
+                inputStream.close();
+                inputStream = null;
+
+                String resourceName = "Web Application Manifest";       // Can we do it like this?
+
+                ManifestResource mre = new ManifestResource
+                    (resourceName,
+                    manifest, ManifestResource.WAR);
+                appManifestResources.add(mre);
+            } 
+        } catch (NamingException nex) {
+            // Application does not contain a MANIFEST.MF file
+        } catch (NoSuchElementException nse) {
+            // Application does not contain a MANIFEST.MF file
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+        }
+
+        // Locate the Manifests for all bundled JARs
+        NamingEnumeration ne = null;
+        try {
+            ne = dirContext.listBindings("WEB-INF/lib/");
+            while ((ne != null) && ne.hasMoreElements()) {
+                Binding binding = (Binding)ne.nextElement();
+                if (!binding.getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) {
+                    continue;
+                }
+                Object obj =
+                    dirContext.lookup("/WEB-INF/lib/" + binding.getName());
+                if (!(obj instanceof Resource)) {
+                    // Probably a directory named xxx.jar - ignore it
+                    continue;
+                }
+                Resource resource = (Resource) obj;
+                Manifest jmanifest = getManifest(resource.streamContent());
+                if (jmanifest != null) {
+                    ManifestResource mre = new ManifestResource(
+                                                binding.getName(),
+                                                jmanifest, 
+                                                ManifestResource.APPLICATION);
+                    appManifestResources.add(mre);
+                }
+            }
+        } catch (NamingException nex) {
+            // Jump out of the check for this application because it 
+            // has no resources
+        }
+
+        return validateManifestResources(appName, appManifestResources);
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Validates a <code>ArrayList</code> of <code>ManifestResource</code> 
+     * objects. This method requires an application name (which is the 
+     * context root of the application at runtime).  
+     *
+     * <code>false</false> is returned if the extension dependencies
+     * represented by any given <code>ManifestResource</code> objects 
+     * is not met.
+     *
+     * This method should also provide static validation of a Web Applicaiton 
+     * if provided with the necessary parameters.
+     *
+     * @param appName The name of the Application that will appear in the 
+     *                error messages
+     * @param resources A list of <code>ManifestResource</code> objects 
+     *                  to be validated.
+     *
+     * @return true if manifest resource file requirements are met
+     */
+    private static boolean validateManifestResources(String appName, 
+                                                     ArrayList<ManifestResource> resources) {
+        boolean passes = true;
+        int failureCount = 0;        
+        HashMap availableExtensions = null;
+
+        Iterator<ManifestResource> it = resources.iterator();
+        while (it.hasNext()) {
+            ManifestResource mre = it.next();
+            ArrayList requiredList = mre.getRequiredExtensions();
+            if (requiredList == null) {
+                continue;
+            }
+
+            // build the list of available extensions if necessary
+            if (availableExtensions == null) {
+                availableExtensions = buildAvailableExtensionsMap(resources);
+            }
+
+            // load the container level resource map if it has not been built
+            // yet
+            if (containerAvailableExtensions == null) {
+                containerAvailableExtensions
+                    = buildAvailableExtensionsMap(containerManifestResources);
+            }
+
+            // iterate through the list of required extensions
+            Iterator rit = requiredList.iterator();
+            while (rit.hasNext()) {
+                Extension requiredExt = (Extension)rit.next();
+                String extId = requiredExt.getUniqueId();
+                // check the applicaion itself for the extension
+                if (availableExtensions != null
+                                && availableExtensions.containsKey(extId)) {
+                   Extension targetExt = (Extension)
+                       availableExtensions.get(extId);
+                   if (targetExt.isCompatibleWith(requiredExt)) {
+                       requiredExt.setFulfilled(true);
+                   }
+                // check the container level list for the extension
+                } else if (containerAvailableExtensions != null
+                        && containerAvailableExtensions.containsKey(extId)) {
+                   Extension targetExt = 
+                       containerAvailableExtensions.get(extId);
+                   if (targetExt.isCompatibleWith(requiredExt)) {
+                       requiredExt.setFulfilled(true);
+                   }
+                } else {
+                    // Failure
+                    if (log.isLoggable(Level.INFO)) {
+                        log.log(Level.INFO, LogFacade.EXTENSION_NOT_FOUND_INFO,
+                                new Object[] {appName, mre.getResourceName(),
+                                        requiredExt.getExtensionName()});
+                    }
+                    passes = false;
+                    failureCount++;
+                }
+            }
+        }
+
+        if (!passes) {
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, LogFacade.FAILED_FIND_EXTENSION_INFO,
+                        new Object[] {appName, failureCount});
+            }
+        }
+
+        return passes;
+    }
+    
+   /* 
+    * Build this list of available extensions so that we do not have to 
+    * re-build this list every time we iterate through the list of required 
+    * extensions. All available extensions in all of the 
+    * <code>MainfestResource</code> objects will be added to a 
+    * <code>HashMap</code> which is returned on the first dependency list
+    * processing pass. 
+    *
+    * The key is the name + implementation version.
+    *
+    * NOTE: A list is built only if there is a dependency that needs 
+    * to be checked (performance optimization).
+    *
+    * @param resources A list of <code>ManifestResource</code> objects
+    *
+    * @return HashMap Map of available extensions
+    */
+    private static HashMap<String, Extension> buildAvailableExtensionsMap(
+            ArrayList<ManifestResource> resources) {
+
+        HashMap<String, Extension> availableMap = null;
+
+        Iterator<ManifestResource> it = resources.iterator();
+        while (it.hasNext()) {
+            ManifestResource mre = it.next();
+            HashMap map = mre.getAvailableExtensions();
+            if (map != null) {
+                Iterator values = map.values().iterator();
+                while (values.hasNext()) {
+                    Extension ext = (Extension) values.next();
+                    if (availableMap == null) {
+                        availableMap = new HashMap<String, Extension>();
+                        availableMap.put(ext.getUniqueId(), ext);
+                    } else if (!availableMap.containsKey(ext.getUniqueId())) {
+                        availableMap.put(ext.getUniqueId(), ext);
+                    }
+                }
+            }
+        }
+
+        return availableMap;
+    }
+    
+    /**
+     * Return the Manifest from a jar file or war file
+     *
+     * @param inStream Input stream to a WAR or JAR file
+     * @return The WAR's or JAR's manifest
+     */
+    private static Manifest getManifest(InputStream inStream)
+            throws IOException {
+
+        Manifest manifest = null;
+        JarInputStream jin = null;
+
+        try {
+            jin = new JarInputStream(inStream);
+            manifest = jin.getManifest();
+            jin.close();
+            jin = null;
+        } finally {
+            if (jin != null) {
+                try {
+                    jin.close();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+        }
+
+        return manifest;
+    }
+    
+    /*
+     * Checks to see if the given system JAR file contains a MANIFEST, and adds
+     * it to the container's manifest resources.
+     *
+     * @param jarFile The system JAR whose manifest to add
+     */
+    private static void addSystemResource(File jarFile) throws IOException {
+
+        try (FileInputStream fio = new FileInputStream(jarFile)) {
+            Manifest manifest = getManifest(fio);
+            if (manifest != null) {
+                ManifestResource mre
+                  = new ManifestResource(jarFile.getAbsolutePath(),
+                  manifest,
+                  ManifestResource.SYSTEM);
+                containerManifestResources.add(mre);
+            }
+        }
+    }
+
+}
+
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/HexUtils.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/HexUtils.java
new file mode 100644
index 0000000..5409c2e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/HexUtils.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import org.apache.catalina.LogFacade;
+
+import java.util.ResourceBundle;
+
+/**
+ * Library of utility methods useful in dealing with converting byte arrays
+ * to and from strings of hexadecimal digits.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public final class HexUtils {
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    // Code from Ajp11, from Apache's JServ
+
+    // Table for HEX to DEC byte translation
+    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,
+    };
+
+
+
+    /**
+     * 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) {
+
+        int length = digits.length();
+        if (length % 2 != 0) {
+            throw new IllegalArgumentException
+                    (rb.getString(LogFacade.ODD_NUMBER_HEX_DIGITS_EXCEPTION));
+        }
+
+        int bLength = length / 2;
+        byte[] bytes = new byte[bLength];
+        for (int i = 0; i < bLength; i++) {
+            char c1 = digits.charAt(2*i);
+            char c2 = digits.charAt(2*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
+                    (rb.getString(LogFacade.BAD_HEX_DIGIT_EXCEPTION));
+            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
+                    (rb.getString(LogFacade.BAD_HEX_DIGIT_EXCEPTION));
+            bytes[i] = b;
+        }
+        return bytes;
+
+    }
+
+
+    /**
+     * Convert a byte array into a printable format containing a
+     * char[] of hexadecimal digit characters (two per byte).
+     *
+     * @param bytes Byte array representation
+     */
+    public static char[] convert(byte bytes[]) {
+
+        char[] arr = new char[bytes.length * 2];
+        for (int i = 0; i < bytes.length; i++) {
+            arr[2*i] = convertDigit((int) (bytes[i] >> 4));
+            arr[2*i + 1] = convertDigit((int) (bytes[i] & 0x0f));
+        }
+        return arr;
+
+    }
+
+    /**
+     * 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(rb.getString(LogFacade.BAD_HEX_DIGIT_EXCEPTION));
+        len = DEC[hex[0]];
+        len = len << 4;
+        if( DEC[hex[1]]<0 )
+            throw new IllegalArgumentException(rb.getString(LogFacade.BAD_HEX_DIGIT_EXCEPTION));
+        len += DEC[hex[1]];
+        len = len << 4;
+        if( DEC[hex[2]]<0 )
+            throw new IllegalArgumentException(rb.getString(LogFacade.BAD_HEX_DIGIT_EXCEPTION));
+        len += DEC[hex[2]];
+        len = len << 4;
+        if( DEC[hex[3]]<0 )
+            throw new IllegalArgumentException(rb.getString(LogFacade.BAD_HEX_DIGIT_EXCEPTION));
+        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/appserver/web/web-core/src/main/java/org/apache/catalina/util/IOTools.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/IOTools.java
new file mode 100644
index 0000000..4832b9a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/IOTools.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.*;
+
+
+/**
+ * Contains commonly needed I/O-related methods 
+ *
+ * @author Dan Sandberg
+ */
+public class IOTools {
+    protected final static int DEFAULT_BUFFER_SIZE=4*1024; //4k
+
+    //Ensure non-instantiability
+    private IOTools() {
+    }
+
+     /**
+     * Read input from reader and write it to writer until there is no more
+     * input from reader.
+     *
+     * @param reader the reader to read from.
+     * @param writer the writer to write to.
+     * @param buf the char array to use as a buffer
+     */
+    public static void flow( Reader reader, Writer writer, char[] buf ) 
+        throws IOException {
+        int numRead;
+        while ( (numRead = reader.read(buf) ) >= 0) {
+            writer.write(buf, 0, numRead);
+        }
+    }
+
+    /**
+     * @see flow( Reader, Writer, char[] )
+     */
+    public static void flow( Reader reader, Writer writer ) 
+        throws IOException {
+        char[] buf = new char[DEFAULT_BUFFER_SIZE];
+        flow( reader, writer, buf );
+    }
+
+    /**
+     * Read input from input stream and write it to output stream 
+     * until there is no more input from input stream.
+     *
+     * @param input stream the input stream to read from.
+     * @param output stream the output stream to write to.
+     * @param buf the byte array to use as a buffer
+     */
+    public static void flow( InputStream is, OutputStream os, byte[] buf ) 
+        throws IOException {
+        int numRead;
+        while ( (numRead = is.read(buf) ) >= 0) {
+            os.write(buf, 0, numRead);
+        }
+    }  
+
+    /**
+     * @see flow( Reader, Writer, byte[] )
+     */ 
+    public static void flow( InputStream is, OutputStream os ) 
+        throws IOException {
+        byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
+        flow( is, os, buf );
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/InstanceSupport.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/InstanceSupport.java
new file mode 100644
index 0000000..7a1bd45
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/InstanceSupport.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+import org.apache.catalina.InstanceEvent;
+import org.apache.catalina.InstanceListener;
+import org.apache.catalina.Wrapper;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+// END SJSAS 6374619
+
+/**
+ * Support class to assist in firing InstanceEvent notifications to
+ * registered InstanceListeners.
+ *
+ * @author Craig R. McClanahan
+ * @version $Id: InstanceSupport.java,v 1.3 2006/03/09 20:38:05 jfarcand Exp $
+ */
+
+public final class InstanceSupport {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new InstanceSupport object associated with the specified
+     * Instance component.
+     *
+     * @param lifecycle The Instance component that will be the source
+     *  of events that we fire
+     */
+    public InstanceSupport(Wrapper wrapper) {
+
+        super();
+        this.wrapper = wrapper;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+    // START SJSAS 6374619
+    private ReadWriteLock listenersLock = new ReentrantReadWriteLock();
+    private Lock listenersReadLock = listenersLock.readLock();
+    private Lock listenersWriteLock = listenersLock.writeLock();
+    // END SJSAS 6374619
+
+    /**
+     * The set of registered InstanceListeners for event notifications.
+     */
+    private InstanceListener listeners[] = new InstanceListener[0];
+
+
+    /**
+     * The source component for instance events that we will fire.
+     */
+    private Wrapper wrapper = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Wrapper with which we are associated.
+     */
+    public Wrapper getWrapper() {
+
+        return (this.wrapper);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addInstanceListener(InstanceListener listener) {
+        /* SJSAS 6374619
+        synchronized (listeners) {
+          InstanceListener results[] =
+            new InstanceListener[listeners.length + 1];
+          for (int i = 0; i < listeners.length; i++)
+              results[i] = listeners[i];
+          results[listeners.length] = listener;
+          listeners = results;
+        }
+        */
+        // START SJSAS 6374619
+        listenersWriteLock.lock();
+        try {
+            InstanceListener results[] =
+                new InstanceListener[listeners.length + 1];
+            for (int i = 0; i < listeners.length; i++)
+              results[i] = listeners[i];
+            results[listeners.length] = listener;
+            listeners = results;
+        } finally {
+            listenersWriteLock.unlock();
+        }
+        // END SJSAS 6374619
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param filter The relevant Filter for this event
+     */
+    public void fireInstanceEvent(InstanceEvent.EventType type, Filter filter) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, filter, type);
+        InstanceListener interested[] = null;
+        /* SJSAS XXX
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+        */
+        // START SJSAS XXX
+        listenersReadLock.lock();
+        try {
+            for (int i = 0; i < listeners.length; i++)
+                listeners[i].instanceEvent(event);
+        } finally {
+            listenersReadLock.unlock();
+        }
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param filter The relevant Filter for this event
+     * @param exception Exception that occurred
+     */
+    public void fireInstanceEvent(InstanceEvent.EventType type, Filter filter,
+                                  Throwable exception) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, filter, type,
+                                                exception);
+        /* SJSAS 6374619
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+        */
+        // START SJSAS 6374619
+        listenersReadLock.lock();
+        try {
+            for (int i = 0; i < listeners.length; i++)
+                listeners[i].instanceEvent(event);
+        } finally {
+            listenersReadLock.unlock();
+        }
+        // END SJSAS 6374619
+
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param filter The relevant Filter for this event
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     */
+    public void fireInstanceEvent(InstanceEvent.EventType type, Filter filter,
+                                  ServletRequest request,
+                                  ServletResponse response) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, filter, type,
+                                                request, response);
+        /* SJSAS 6374619
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+        */
+        // START SJSAS 6374619
+        listenersReadLock.lock();
+        try {
+            for (int i = 0; i < listeners.length; i++)
+                listeners[i].instanceEvent(event);
+        } finally {
+            listenersReadLock.unlock();
+        }
+        // END SJSAS 6374619
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param filter The relevant Filter for this event
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     * @param exception Exception that occurred
+     */
+    public void fireInstanceEvent(InstanceEvent.EventType type, Filter filter,
+                                  ServletRequest request,
+                                  ServletResponse response,
+                                  Throwable exception) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, filter, type,
+                                                request, response, exception);
+        /* SJSAS 6374619        
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+        */
+        // START SJSAS 6374619
+        listenersReadLock.lock();
+        try {
+            for (int i = 0; i < listeners.length; i++)
+                listeners[i].instanceEvent(event);
+        } finally {
+            listenersReadLock.unlock();
+        }
+        // END SJSAS 6374619
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param servlet The relevant Servlet for this event
+     */
+    public void fireInstanceEvent(InstanceEvent.EventType type, Servlet servlet) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, servlet, type);
+        /* SJSAS 6374619
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+        */
+        // START SJSAS 6374619
+        listenersReadLock.lock();
+        try {
+            for (int i = 0; i < listeners.length; i++)
+                listeners[i].instanceEvent(event);
+        } finally {
+            listenersReadLock.unlock();
+        }
+        // END SJSAS 6374619
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param servlet The relevant Servlet for this event
+     * @param exception Exception that occurred
+     */
+    public void fireInstanceEvent(InstanceEvent.EventType type, Servlet servlet,
+                                  Throwable exception) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
+                                                exception);
+        /* SJSAS 6374619
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+        */
+        // START SJSAS 6374619
+        listenersReadLock.lock();
+        try {
+            for (int i = 0; i < listeners.length; i++)
+                listeners[i].instanceEvent(event);
+        } finally {
+            listenersReadLock.unlock();
+        }
+        // END SJSAS 6374619
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param servlet The relevant Servlet for this event
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     */
+    public void fireInstanceEvent(InstanceEvent.EventType type, Servlet servlet,
+                                  ServletRequest request,
+                                  ServletResponse response) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
+                                                request, response);
+        /* SJSAS 6374619
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+        */
+        // START SJSAS 6374619
+        listenersReadLock.lock();
+        try {
+            for (int i = 0; i < listeners.length; i++)
+                listeners[i].instanceEvent(event);
+        } finally {
+            listenersReadLock.unlock();
+        }
+        // END SJSAS 6374619
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param servlet The relevant Servlet for this event
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are processing
+     * @param exception Exception that occurred
+     */
+    public void fireInstanceEvent(InstanceEvent.EventType type, Servlet servlet,
+                                  ServletRequest request,
+                                  ServletResponse response,
+                                  Throwable exception) {
+
+        if (listeners.length == 0)
+            return;
+
+        InstanceEvent event = new InstanceEvent(wrapper, servlet, type,
+                                                request, response, exception);
+        /* SJSAS 6374619
+        InstanceListener interested[] = null;
+        synchronized (listeners) {
+            interested = (InstanceListener[]) listeners.clone();
+        }
+        for (int i = 0; i < interested.length; i++)
+            interested[i].instanceEvent(event);
+        */
+        // START SJSAS 6374619
+        listenersReadLock.lock();
+        try {
+            for (int i = 0; i < listeners.length; i++)
+                listeners[i].instanceEvent(event);
+        } finally {
+            listenersReadLock.unlock();
+        }
+        // END SJSAS 6374619
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeInstanceListener(InstanceListener listener) {
+
+        /* SJSAS 6374619
+        synchronized (listeners) {
+            int n = -1;
+            for (int i = 0; i < listeners.length; i++) {
+                if (listeners[i] == listener) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+            InstanceListener results[] =
+              new InstanceListener[listeners.length - 1];
+            int j = 0;
+            for (int i = 0; i < listeners.length; i++) {
+                if (i != n)
+                    results[j++] = listeners[i];
+            }
+            listeners = results;
+        }
+        */
+        // START SJSAS 6374619
+        listenersWriteLock.lock();
+        try {
+            int n = -1;
+            for (int i = 0; i < listeners.length; i++) {
+                if (listeners[i] == listener) {
+                    n = i;
+                    break;
+                }
+            }
+            if (n < 0)
+                return;
+            InstanceListener results[] =
+              new InstanceListener[listeners.length - 1];
+            int j = 0;
+            for (int i = 0; i < listeners.length; i++) {
+                if (i != n)
+                    results[j++] = listeners[i];
+            }
+            listeners = results;
+        } finally {
+            listenersWriteLock.unlock();
+        }
+        // END SJSAS 6374619
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/IterableAdapter.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/IterableAdapter.java
new file mode 100644
index 0000000..7046a41
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/IterableAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.util;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+
+/**
+ * Adapter class which wraps an <tt>Iterable</tt> over an
+ * <tt>Enumeration</tt>, to support foreach-style iteration over the
+ * <tt>Enumeration</tt>.
+ */
+public final class IterableAdapter<T> implements Iterable<T> {
+
+    // The Enumeration over which to iterate
+    private Enumeration<T> en;
+
+    /**
+     * Constructor
+     *
+     * @param en the Enumeration over which to iterate
+     */
+    public IterableAdapter(Enumeration<T> en) {
+        this.en = en;
+    }
+
+    public Iterator<T> iterator() {
+
+        return new Iterator<T>() {
+
+            public boolean hasNext() {
+                return en.hasMoreElements();
+            }
+
+            public T next() {
+                return en.nextElement();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException(
+                    "remove not supported");
+            }
+        };
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/LifecycleSupport.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/LifecycleSupport.java
new file mode 100644
index 0000000..64b18de
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/LifecycleSupport.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Support class to assist in firing LifecycleEvent notifications to
+ * registered LifecycleListeners.
+ *
+ * @author Craig R. McClanahan
+ * @version $Id: LifecycleSupport.java,v 1.2 2005/12/08 01:28:17 kchung Exp $
+ */
+
+public final class LifecycleSupport {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new LifecycleSupport object associated with the specified
+     * Lifecycle component.
+     *
+     * @param lifecycle The Lifecycle component that will be the source
+     *  of events that we fire
+     */
+    public LifecycleSupport(Lifecycle lifecycle) {
+        super();
+        this.lifecycle = lifecycle;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The source component for lifecycle events that we will fire.
+     */
+    private Lifecycle lifecycle = null;
+
+
+    /**
+     * The list of registered LifecycleListeners for event notifications.
+     */
+    private List<LifecycleListener> listeners =
+        new ArrayList<LifecycleListener>();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        synchronized (listeners) {
+            listeners.add(listener);
+        }
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this LifecycleSupport instance.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return listeners;
+    }
+
+
+    /**
+     * Notify all lifecycle event listeners that a particular event has
+     * occurred for this Container.  The default implementation performs
+     * this notification synchronously using the calling thread.
+     *
+     * @param type Event type
+     * @param data Event data
+     */
+    public void fireLifecycleEvent(String type, Object data)
+            throws LifecycleException {
+
+
+        LifecycleListener[] listenersArray = null;
+
+        synchronized (listeners) {
+            if (listeners.isEmpty()) {
+                return;
+            }
+            listenersArray = listeners.toArray(
+                    new LifecycleListener[listeners.size()]);
+        }
+
+        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
+
+        for (int i = 0; i < listenersArray.length; i++) {
+            listenersArray[i].lifecycleEvent(event);
+        }
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+    }
+
+
+    /**
+     * Removes any lifecycle event listeners from this LifecycleSupport
+     * instance.
+     */
+    public void removeLifecycleListeners() {
+        listeners.clear();
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/MD5Encoder.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/MD5Encoder.java
new file mode 100644
index 0000000..320d24b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/MD5Encoder.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+/**
+ * Encode an MD5 digest into a String.
+ * <p>
+ * The 128 bit MD5 hash is converted into a 32 character long String.
+ * Each character of the String is the hexadecimal representation of 4 bits
+ * of the digest.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:18 $
+ */
+
+public final class MD5Encoder extends DigestEncoderBase {
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Encodes the 128 bit (16 bytes) MD5 into a 32 character String.
+     *
+     * @param binaryData Array containing the digest
+     * @return Encoded MD5, or null if encoding failed
+     */
+    public char[] encode( byte[] binaryData ) {
+
+        if (binaryData.length != 16)
+            return null;
+
+        return super.encode(binaryData);
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/MIME2Java.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/MIME2Java.java
new file mode 100644
index 0000000..2a09411
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/MIME2Java.java
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.util.Hashtable;
+import java.util.Locale;
+
+/**
+ * MIME2Java is a convenience class which handles conversions between MIME charset names
+ * and Java encoding names.
+ * <p>The supported XML encodings are the intersection of XML-supported code sets and those
+ * supported in JDK 1.1.
+ * <p>MIME charset names are used on <var>xmlEncoding</var> parameters to methods such
+ * as <code>TXDocument#setEncoding</code> and <code>DTD#setEncoding</code>.
+ * <p>Java encoding names are used on <var>encoding</var> parameters to
+ * methods such as <code>TXDocument#printWithFormat</code> and <code>DTD#printExternal</code>.
+ * <P>
+ * <TABLE BORDER="0" WIDTH="100%">
+ *  <TR>
+ *      <TD WIDTH="33%">
+ *          <P ALIGN="CENTER"><B>Common Name</B>
+ *      </TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER"><B>Use this name in XML files</B>
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER"><B>Name Type</B>
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER"><B>Xerces converts to this Java Encoder Name</B>
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">8 bit Unicode</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">UTF-8
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">UTF8
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 1</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-1
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-1
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 2</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-2
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-2
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 3</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-3
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-3
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 4</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-4
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-4
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Cyrillic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-5
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-5
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Arabic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-6
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-6
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Greek</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-7
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-7
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin Hebrew</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-8
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-8
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">ISO Latin 5</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ISO-8859-9
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">ISO-8859-9
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: US</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-us
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Canada</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ca
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Netherlands</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-nl
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp037
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Denmark</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-dk
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp277
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Norway</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-no
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp277
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Finland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-fi
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp278
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Sweden</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-se
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp278
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Italy</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-it
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp280
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Spain, Latin America</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-es
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp284
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Great Britain</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-gb
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp285
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: France</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-fr
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp297
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Arabic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ar1
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp420
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Hebrew</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-he
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp424
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Switzerland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ch
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp500
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Roece</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-roece
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp870
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Yogoslavia</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-yu
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp870
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Iceland</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-is
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp871
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">EBCDIC: Urdu</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">ebcdic-cp-ar2
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">IANA
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">cp918
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Chinese for PRC, mixed 1/2 byte</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">gb2312
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">GB2312
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Extended Unix Code, packed for Japanese</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">euc-jp
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">eucjis
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Japanese: iso-2022-jp</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">iso-2020-jp
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">JIS
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Japanese: Shift JIS</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">Shift_JIS
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">SJIS
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Chinese: Big5</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">Big5
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">Big5
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Extended Unix Code, packed for Korean</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">euc-kr
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">iso2022kr
+ *      </TD>
+ *  </TR>
+ *  <TR>
+ *      <TD WIDTH="33%">Cyrillic</TD>
+ *      <TD WIDTH="15%">
+ *          <P ALIGN="CENTER">koi8-r
+ *      </TD>
+ *      <TD WIDTH="12%">
+ *          <P ALIGN="CENTER">MIME
+ *      </TD>
+ *      <TD WIDTH="31%">
+ *          <P ALIGN="CENTER">koi8-r
+ *      </TD>
+ *  </TR>
+ * </TABLE>
+ *
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:18 $
+ * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
+ */
+public class MIME2Java {
+
+    static private Hashtable<String, String> s_enchash;
+    static private Hashtable<String, String> s_revhash;
+
+    static {
+        s_enchash = new Hashtable<String, String>();
+        //    <preferred MIME name>, <Java encoding name>
+        s_enchash.put("UTF-8", "UTF8");
+        s_enchash.put("US-ASCII",        "8859_1");    // ?
+        s_enchash.put("ISO-8859-1",      "8859_1");
+        s_enchash.put("ISO-8859-2",      "8859_2");
+        s_enchash.put("ISO-8859-3",      "8859_3");
+        s_enchash.put("ISO-8859-4",      "8859_4");
+        s_enchash.put("ISO-8859-5",      "8859_5");
+        s_enchash.put("ISO-8859-6",      "8859_6");
+        s_enchash.put("ISO-8859-7",      "8859_7");
+        s_enchash.put("ISO-8859-8",      "8859_8");
+        s_enchash.put("ISO-8859-9",      "8859_9");
+        s_enchash.put("ISO-2022-JP",     "JIS");
+        s_enchash.put("SHIFT_JIS",       "SJIS");
+        s_enchash.put("EUC-JP",          "EUCJIS");
+        s_enchash.put("GB2312",          "GB2312");
+        s_enchash.put("BIG5",            "Big5");
+        s_enchash.put("EUC-KR",          "KSC5601");
+        s_enchash.put("ISO-2022-KR",     "ISO2022KR");
+        s_enchash.put("KOI8-R",          "KOI8_R");
+
+        s_enchash.put("EBCDIC-CP-US",    "CP037");
+        s_enchash.put("EBCDIC-CP-CA",    "CP037");
+        s_enchash.put("EBCDIC-CP-NL",    "CP037");
+        s_enchash.put("EBCDIC-CP-DK",    "CP277");
+        s_enchash.put("EBCDIC-CP-NO",    "CP277");
+        s_enchash.put("EBCDIC-CP-FI",    "CP278");
+        s_enchash.put("EBCDIC-CP-SE",    "CP278");
+        s_enchash.put("EBCDIC-CP-IT",    "CP280");
+        s_enchash.put("EBCDIC-CP-ES",    "CP284");
+        s_enchash.put("EBCDIC-CP-GB",    "CP285");
+        s_enchash.put("EBCDIC-CP-FR",    "CP297");
+        s_enchash.put("EBCDIC-CP-AR1",   "CP420");
+        s_enchash.put("EBCDIC-CP-HE",    "CP424");
+        s_enchash.put("EBCDIC-CP-CH",    "CP500");
+        s_enchash.put("EBCDIC-CP-ROECE", "CP870");
+        s_enchash.put("EBCDIC-CP-YU",    "CP870");
+        s_enchash.put("EBCDIC-CP-IS",    "CP871");
+        s_enchash.put("EBCDIC-CP-AR2",   "CP918");
+
+                                                // j:CNS11643 -> EUC-TW?
+                                                // ISO-2022-CN? ISO-2022-CN-EXT?
+
+        s_revhash = new Hashtable<String, String>();
+        //    <Java encoding name>, <preferred MIME name>
+        s_revhash.put("UTF8", "UTF-8");
+        //s_revhash.put("8859_1", "US-ASCII");    // ?
+        s_revhash.put("8859_1", "ISO-8859-1");
+        s_revhash.put("8859_2", "ISO-8859-2");
+        s_revhash.put("8859_3", "ISO-8859-3");
+        s_revhash.put("8859_4", "ISO-8859-4");
+        s_revhash.put("8859_5", "ISO-8859-5");
+        s_revhash.put("8859_6", "ISO-8859-6");
+        s_revhash.put("8859_7", "ISO-8859-7");
+        s_revhash.put("8859_8", "ISO-8859-8");
+        s_revhash.put("8859_9", "ISO-8859-9");
+        s_revhash.put("JIS", "ISO-2022-JP");
+        s_revhash.put("SJIS", "Shift_JIS");
+        s_revhash.put("EUCJIS", "EUC-JP");
+        s_revhash.put("GB2312", "GB2312");
+        s_revhash.put("BIG5", "Big5");
+        s_revhash.put("KSC5601", "EUC-KR");
+        s_revhash.put("ISO2022KR", "ISO-2022-KR");
+        s_revhash.put("KOI8_R", "KOI8-R");
+
+        s_revhash.put("CP037", "EBCDIC-CP-US");
+        s_revhash.put("CP037", "EBCDIC-CP-CA");
+        s_revhash.put("CP037", "EBCDIC-CP-NL");
+        s_revhash.put("CP277", "EBCDIC-CP-DK");
+        s_revhash.put("CP277", "EBCDIC-CP-NO");
+        s_revhash.put("CP278", "EBCDIC-CP-FI");
+        s_revhash.put("CP278", "EBCDIC-CP-SE");
+        s_revhash.put("CP280", "EBCDIC-CP-IT");
+        s_revhash.put("CP284", "EBCDIC-CP-ES");
+        s_revhash.put("CP285", "EBCDIC-CP-GB");
+        s_revhash.put("CP297", "EBCDIC-CP-FR");
+        s_revhash.put("CP420", "EBCDIC-CP-AR1");
+        s_revhash.put("CP424", "EBCDIC-CP-HE");
+        s_revhash.put("CP500", "EBCDIC-CP-CH");
+        s_revhash.put("CP870", "EBCDIC-CP-ROECE");
+        s_revhash.put("CP870", "EBCDIC-CP-YU");
+        s_revhash.put("CP871", "EBCDIC-CP-IS");
+        s_revhash.put("CP918", "EBCDIC-CP-AR2");
+    }
+
+    private MIME2Java() {
+    }
+
+    /**
+     * Convert a MIME charset name, also known as an XML encoding name, to a Java encoding name.
+     * @param   mimeCharsetName Case insensitive MIME charset name: <code>UTF-8, US-ASCII, ISO-8859-1,
+     *                          ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6,
+     *                          ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-2022-JP, Shift_JIS,
+     *                          EUC-JP, GB2312, Big5, EUC-KR, ISO-2022-KR, KOI8-R,
+     *                          EBCDIC-CP-US, EBCDIC-CP-CA, EBCDIC-CP-NL, EBCDIC-CP-DK,
+     *                          EBCDIC-CP-NO, EBCDIC-CP-FI, EBCDIC-CP-SE, EBCDIC-CP-IT,
+     *                          EBCDIC-CP-ES, EBCDIC-CP-GB, EBCDIC-CP-FR, EBCDIC-CP-AR1,
+     *                          EBCDIC-CP-HE, EBCDIC-CP-CH, EBCDIC-CP-ROECE, EBCDIC-CP-YU,
+     *                          EBCDIC-CP-IS and EBCDIC-CP-AR2</code>.
+     * @return                  Java encoding name, or <var>null</var> if <var>mimeCharsetName</var>
+     *                          is unknown.
+     * @see #reverse
+     */
+    public static String convert(String mimeCharsetName) {
+        return s_enchash.get(mimeCharsetName.toUpperCase(Locale.ENGLISH));
+    }
+
+    /**
+     * Convert a Java encoding name to MIME charset name.
+     * Available values of <i>encoding</i> are "UTF8", "8859_1", "8859_2", "8859_3", "8859_4",
+     * "8859_5", "8859_6", "8859_7", "8859_8", "8859_9", "JIS", "SJIS", "EUCJIS",
+     * "GB2312", "BIG5", "KSC5601", "ISO2022KR",  "KOI8_R", "CP037", "CP277", "CP278",
+     * "CP280", "CP284", "CP285", "CP297", "CP420", "CP424", "CP500", "CP870", "CP871" and "CP918".
+     * @param   encoding    Case insensitive Java encoding name: <code>UTF8, 8859_1, 8859_2, 8859_3,
+     *                      8859_4, 8859_5, 8859_6, 8859_7, 8859_8, 8859_9, JIS, SJIS, EUCJIS,
+     *                      GB2312, BIG5, KSC5601, ISO2022KR, KOI8_R, CP037, CP277, CP278,
+     *                      CP280, CP284, CP285, CP297, CP420, CP424, CP500, CP870, CP871
+     *                      and CP918</code>.
+     * @return              MIME charset name, or <var>null</var> if <var>encoding</var> is unknown.
+     * @see #convert
+     */
+    public static String reverse(String encoding) {
+        return s_revhash.get(encoding.toUpperCase(Locale.ENGLISH));
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/ManifestResource.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ManifestResource.java
new file mode 100644
index 0000000..fecfe92
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ManifestResource.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+/**
+ *  Representation of a Manifest file and its available extensions and
+ *  required extensions
+ *  
+ * @author Greg Murray
+ * @author Justyna Horwat
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:18 $
+ * 
+ */
+public class ManifestResource {
+    
+    // ------------------------------------------------------------- Properties
+
+    // These are the resource types for determining effect error messages
+    public static final int SYSTEM = 1;
+    public static final int WAR = 2;
+    public static final int APPLICATION = 3;
+    
+    private HashMap<String, Extension> availableExtensions = null;
+    private ArrayList<Extension> requiredExtensions = null;
+    
+    private String resourceName = null;
+    private int resourceType = -1;
+        
+    public ManifestResource(String resourceName, Manifest manifest, 
+                            int resourceType) {
+        this.resourceName = resourceName;
+        this.resourceType = resourceType;
+        processManifest(manifest);
+    }
+    
+    /**
+     * Gets the name of the resource
+     *
+     * @return The name of the resource
+     */
+    public String getResourceName() {
+        return resourceName;
+    }
+
+    /**
+     * Gets the map of available extensions
+     *
+     * @return Map of available extensions
+     */
+    public HashMap<String, Extension> getAvailableExtensions() {
+        return availableExtensions;
+    }
+    
+    /**
+     * Gets the list of required extensions
+     *
+     * @return List of required extensions
+     */
+    public ArrayList<Extension> getRequiredExtensions() {
+        return requiredExtensions;   
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Gets the number of available extensions
+     *
+     * @return The number of available extensions
+     */
+    public int getAvailableExtensionCount() {
+        return (availableExtensions != null) ? availableExtensions.size() : 0;
+    }
+    
+    /**
+     * Gets the number of required extensions
+     *
+     * @return The number of required extensions
+     */
+    public int getRequiredExtensionCount() {
+        return (requiredExtensions != null) ? requiredExtensions.size() : 0;
+    }
+    
+    /**
+     * Convenience method to check if this <code>ManifestResource</code>
+     * has an requires extensions.
+     *
+     * @return true if required extensions are present
+     */
+    public boolean requiresExtensions() {
+        return (requiredExtensions != null) ? true : false;
+    }
+    
+    /**
+     * Convenience method to check if this <code>ManifestResource</code>
+     * has an extension available.
+     *
+     * @param key extension identifier
+     *
+     * @return true if extension available
+     */
+    public boolean containsExtension(String key) {
+        return (availableExtensions != null) ?
+                availableExtensions.containsKey(key) : false;
+    }
+    
+    /**
+     * Returns <code>true</code> if all required extension dependencies
+     * have been meet for this <code>ManifestResource</code> object.
+     *
+     * @return boolean true if all extension dependencies have been satisfied
+     */
+    public boolean isFulfilled() {
+        if (requiredExtensions == null) {
+            return true;
+        }
+        Iterator<Extension> it = requiredExtensions.iterator();
+        while (it.hasNext()) {
+            Extension ext = it.next();
+            if (!ext.isFulfilled()) return false;            
+        }
+        return true;
+    }
+    
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ManifestResource[");
+        sb.append(resourceName);
+
+        sb.append(", isFulfilled=");
+        sb.append(isFulfilled() +"");
+        sb.append(", requiredExtensionCount =");
+        sb.append(getRequiredExtensionCount());
+        sb.append(", availableExtensionCount=");
+        sb.append(getAvailableExtensionCount());
+        switch (resourceType) {
+            case SYSTEM : sb.append(", resourceType=SYSTEM"); break;
+            case WAR : sb.append(", resourceType=WAR"); break;
+            case APPLICATION : sb.append(", resourceType=APPLICATION"); break;
+            default: sb.append(", resourceType=" + resourceType); break;
+        }
+        sb.append("]");
+        return (sb.toString());
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+    private void processManifest(Manifest manifest) {
+        availableExtensions = getAvailableExtensions(manifest);
+        requiredExtensions = getRequiredExtensions(manifest);
+    }
+    
+    /**
+     * Return the set of <code>Extension</code> objects representing optional
+     * packages that are required by the application associated with the
+     * specified <code>Manifest</code>.
+     *
+     * @param manifest Manifest to be parsed
+     *
+     * @return List of required extensions, or null if the application
+     * does not require any extensions
+     */
+    private ArrayList<Extension> getRequiredExtensions(Manifest manifest) {
+
+        Attributes attributes = manifest.getMainAttributes();
+        String names = attributes.getValue("Extension-List");
+        if (names == null)
+            return null;
+
+        ArrayList<Extension> extensionList = new ArrayList<Extension>();
+        names += " ";
+
+        while (true) {
+
+            int space = names.indexOf(' ');
+            if (space < 0)
+                break;
+            String name = names.substring(0, space).trim();
+            names = names.substring(space + 1);
+
+            String value =
+                attributes.getValue(name + "-Extension-Name");
+            if (value == null)
+                continue;
+            Extension extension = new Extension();
+            extension.setExtensionName(value);
+            extension.setImplementationURL
+                (attributes.getValue(name + "-Implementation-URL"));
+            extension.setImplementationVendorId
+                (attributes.getValue(name + "-Implementation-Vendor-Id"));
+            String version = attributes.getValue(name + "-Implementation-Version");
+            extension.setImplementationVersion(version);
+            extension.setSpecificationVersion
+                (attributes.getValue(name + "-Specification-Version"));
+            extensionList.add(extension);
+        }
+        return extensionList;
+    }
+    
+    /**
+     * Return the set of <code>Extension</code> objects representing optional
+     * packages that are bundled with the application associated with the
+     * specified <code>Manifest</code>.
+     *
+     * @param manifest Manifest to be parsed
+     *
+     * @return Map of available extensions, or null if the web application
+     * does not bundle any extensions
+     */
+    private HashMap<String, Extension> getAvailableExtensions(Manifest manifest) {
+
+        Attributes attributes = manifest.getMainAttributes();
+        String name = attributes.getValue("Extension-Name");
+        if (name == null)
+            return null;
+
+        HashMap<String, Extension> extensionMap = new HashMap<String, Extension>();
+
+        Extension extension = new Extension();
+        extension.setExtensionName(name);
+        extension.setImplementationURL(
+            attributes.getValue("Implementation-URL"));
+        extension.setImplementationVendor(
+            attributes.getValue("Implementation-Vendor"));
+        extension.setImplementationVendorId(
+            attributes.getValue("Implementation-Vendor-Id"));
+        extension.setImplementationVersion(
+            attributes.getValue("Implementation-Version"));
+        extension.setSpecificationVersion(
+            attributes.getValue("Specification-Version"));
+
+        if (!extensionMap.containsKey(extension.getUniqueId())) {
+            extensionMap.put(extension.getUniqueId(), extension);
+        }
+
+        return extensionMap;
+    }
+    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/ParameterMap.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ParameterMap.java
new file mode 100644
index 0000000..f624751
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ParameterMap.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+/* START PWC 6057385
+import java.util.HashMap;
+*/
+// START PWC 6057385
+
+import org.apache.catalina.LogFacade;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+
+/**
+ * Extended implementation of <strong>HashMap</strong> that includes a
+ * <code>locked</code> property.  This class can be used to safely expose
+ * Catalina internal parameter map objects to user classes without having
+ * to clone them in order to avoid modifications.  When first created, a
+ * <code>ParmaeterMap</code> instance is not locked.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:18 $
+ */
+
+/* START PWC 6057385
+public final class ParameterMap extends HashMap {
+*/
+// START PWC 6057385
+public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
+// END PWC 6057385
+
+    private static final ResourceBundle rb = LogFacade.getLogger().getResourceBundle();
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new, empty map with the default initial capacity and
+     * load factor.
+     */
+    public ParameterMap() {
+
+        super();
+
+    }
+
+
+    /**
+     * Construct a new, empty map with the specified initial capacity and
+     * default load factor.
+     *
+     * @param initialCapacity The initial capacity of this map
+     */
+    public ParameterMap(int initialCapacity) {
+
+        super(initialCapacity);
+
+    }
+
+
+    /**
+     * Construct a new, empty map with the specified initial capacity and
+     * load factor.
+     *
+     * @param initialCapacity The initial capacity of this map
+     * @param loadFactor The load factor of this map
+     */
+    public ParameterMap(int initialCapacity, float loadFactor) {
+
+        super(initialCapacity, loadFactor);
+
+    }
+
+
+    /**
+     * Construct a new map with the same mappings as the given map.
+     *
+     * @param map Map whose contents are duplicated in the new map
+     */
+    public ParameterMap(Map<K,V> map) {
+
+        super(map);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The current lock state of this parameter map.
+     */
+    private boolean locked = false;
+
+
+    /**
+     * Return the locked state of this parameter map.
+     */
+    public boolean isLocked() {
+
+        return (this.locked);
+
+    }
+
+
+    /**
+     * Set the locked state of this parameter map.
+     *
+     * @param locked The new locked state
+     */
+    public void setLocked(boolean locked) {
+
+        this.locked = locked;
+
+    }
+
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+
+    /**
+     * Remove all mappings from this map.
+     *
+     * @exception IllegalStateException if this map is currently locked
+     */
+    @Override
+    public void clear() {
+
+        if (locked)
+            throw new IllegalStateException
+                (rb.getString(LogFacade.MAP_IS_LOCKED_EXCEPTION));
+        super.clear();
+
+    }
+
+
+    /**
+     * Associate the specified value with the specified key in this map.  If
+     * the map previously contained a mapping for this key, the old value is
+     * replaced.
+     *
+     * @param key Key with which the specified value is to be associated
+     * @param value Value to be associated with the specified key
+     *
+     * @return The previous value associated with the specified key, or
+     *  <code>null</code> if there was no mapping for key
+     *
+     * @exception IllegalStateException if this map is currently locked
+     */
+    @Override
+    public V put(K key, V value) {
+
+        if (locked)
+            throw new IllegalStateException
+                (rb.getString(LogFacade.MAP_IS_LOCKED_EXCEPTION));
+        return (super.put(key, value));
+
+    }
+
+
+    /**
+     * Copy all of the mappings from the specified map to this one.  These
+     * mappings replace any mappings that this map had for any of the keys
+     * currently in the specified Map.
+     *
+     * @param map Mappings to be stored into this map
+     *
+     * @exception IllegalStateException if this map is currently locked
+     */
+    @Override
+    public void putAll(Map<? extends K,? extends V> map) {
+
+        if (locked)
+            throw new IllegalStateException
+                (rb.getString(LogFacade.MAP_IS_LOCKED_EXCEPTION));
+        super.putAll(map);
+
+    }
+
+
+    /**
+     * Remove the mapping for this key from the map if present.
+     *
+     * @param key Key whose mapping is to be removed from the map
+     *
+     * @return The previous value associated with the specified key, or
+     *  <code>null</code> if there was no mapping for that key
+     *
+     * @exception IllegalStateException if this map is currently locked
+     */
+    @Override
+    public V remove(Object key) {
+
+        if (locked)
+            throw new IllegalStateException
+                (rb.getString(LogFacade.MAP_IS_LOCKED_EXCEPTION));
+        return (super.remove(key));
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/ProcessEnvironment.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ProcessEnvironment.java
new file mode 100644
index 0000000..147f32c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ProcessEnvironment.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import org.apache.catalina.LogFacade;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+// import org.apache.catalina.util.StringManager;
+
+
+
+/**
+ * Encapsulates the Process environment and rules to derive
+ * that environment from the servlet container and request information.
+ * @author   Martin Dengler [root@martindengler.com]
+ * @version  $Revision: 1.3 $, $Date: 2006/03/12 01:27:08 $
+ * @since    Tomcat 4.0
+ */
+public class ProcessEnvironment {
+
+    private static final Logger log = LogFacade.getLogger();
+
+    /** context of the enclosing servlet */
+    private ServletContext context = null;
+
+    /** real file system directory of the enclosing servlet's web app */
+    private String webAppRootDir = null;
+
+    /** context path of enclosing servlet */
+    private String contextPath = null;
+
+    /** pathInfo for the current request */
+    protected String pathInfo = null;
+
+    /** servlet URI of the enclosing servlet */
+    private String servletPath = null;
+
+    /** derived process environment */
+    protected Hashtable<String, String> env = null;
+
+    /** command to be invoked */
+    protected String command = null;
+
+    /** whether or not this object is valid or not */
+    protected boolean valid = false;
+
+    /** the debugging detail level for this instance. */
+    protected int debug = 0;
+
+    /** process' desired working directory */
+    protected File workingDirectory = null;
+
+
+    /**
+     * Creates a ProcessEnvironment and derives the necessary environment,
+     * working directory, command, etc.
+     * @param  req       HttpServletRequest for information provided by
+     *                   the Servlet API
+     * @param  context   ServletContext for information provided by
+     *                   the Servlet API
+     */
+    public ProcessEnvironment(HttpServletRequest req,
+        ServletContext context) {
+        this(req, context, 0);
+    }
+
+
+    /**
+     * Creates a ProcessEnvironment and derives the necessary environment,
+     * working directory, command, etc.
+     * @param  req       HttpServletRequest for information provided by
+     *                   the Servlet API
+     * @param  context   ServletContext for information provided by
+     *                   the Servlet API
+     * @param  debug     int debug level (0 == none, 4 == medium, 6 == lots)
+     */
+    public ProcessEnvironment(HttpServletRequest req,
+        ServletContext context, int debug) {
+            this.debug = debug;
+            setupFromContext(context);
+            setupFromRequest(req);
+            this.valid = deriveProcessEnvironment(req);
+            log(this.getClass().getName() + "() ctor, debug level " + debug);
+    }
+
+
+    /**
+     * Uses the ServletContext to set some process variables
+     * @param  context   ServletContext for information provided by
+     *                   the Servlet API
+     */
+    protected void setupFromContext(ServletContext context) {
+        this.context = context;
+        this.webAppRootDir = context.getRealPath("/");
+    }
+
+
+    /**
+     * Uses the HttpServletRequest to set most process variables
+     * @param  req   HttpServletRequest for information provided by
+     *               the Servlet API
+     */
+    protected void setupFromRequest(HttpServletRequest req) {
+        this.contextPath = req.getContextPath();
+        this.pathInfo = req.getPathInfo();
+        this.servletPath = req.getServletPath();
+    }
+
+
+    /**
+     * Print important process environment information in an
+     * easy-to-read HTML table
+     * @return  HTML string containing process environment info
+     */
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<TABLE border=2>");
+        sb.append("<tr><th colspan=2 bgcolor=grey>");
+        sb.append("ProcessEnvironment Info</th></tr>");
+        sb.append("<tr><td>Debug Level</td><td>");
+        sb.append(debug);
+        sb.append("</td></tr>");
+        sb.append("<tr><td>Validity:</td><td>");
+        sb.append(isValid());
+        sb.append("</td></tr>");
+        if (isValid()) {
+            Enumeration<String> envk = env.keys();
+            while (envk.hasMoreElements()) {
+                String s = envk.nextElement();
+                sb.append("<tr><td>");
+                sb.append(s);
+                sb.append("</td><td>");
+                sb.append(blanksToString(env.get(s),
+                    "[will be set to blank]"));
+                    sb.append("</td></tr>");
+            }
+        }
+        sb.append("<tr><td colspan=2><HR></td></tr>");
+        sb.append("<tr><td>Derived Command</td><td>");
+        sb.append(nullsToBlanks(command));
+        sb.append("</td></tr>");
+        sb.append("<tr><td>Working Directory</td><td>");
+        if (workingDirectory != null) {
+            sb.append(workingDirectory.toString());
+        }
+        sb.append("</td></tr>");
+        sb.append("</TABLE><p>end.");
+        return sb.toString();
+    }
+
+
+    /**
+     * Gets derived command string
+     * @return  command string
+     */
+    public String getCommand() {
+        return command;
+    }
+
+
+    /**
+     * Sets the desired command string
+     * @param   command string as desired
+     * @return  command string
+     */
+    protected String setCommand(String command) {
+        return command;
+    }
+
+
+    /**
+     * Gets this process' derived working directory
+     * @return  working directory
+     */
+    public File getWorkingDirectory() {
+        return workingDirectory;
+    }
+
+
+    /**
+     * Gets process' environment
+     * @return   process' environment
+     */
+    public Hashtable<String, String> getEnvironment() {
+        return env;
+    }
+
+
+    /**
+     * Sets process' environment
+     * @param   env  process' environment
+     * @return  Hashtable to which the process' environment was set
+     */
+    public Hashtable<String, String> setEnvironment(Hashtable<String, String> env) {
+        this.env = env;
+        return this.env;
+    }
+
+
+    /**
+     * Gets validity status
+     * @return   true if this environment is valid, false otherwise
+     */
+    public boolean isValid() {
+        return valid;
+    }
+
+
+    /**
+     * Converts null strings to blank strings ("")
+     * @param    s string to be converted if necessary
+     * @return   a non-null string, either the original or the empty string
+     *           ("") if the original was <code>null</code>
+     */
+    protected String nullsToBlanks(String s) {
+        return nullsToString(s, "");
+    }
+
+
+    /**
+     * Converts null strings to another string
+     * @param    couldBeNull string to be converted if necessary
+     * @param    subForNulls string to return instead of a null string
+     * @return   a non-null string, either the original or the substitute
+     *           string if the original was <code>null</code>
+     */
+    protected String nullsToString(String couldBeNull, String subForNulls) {
+        return (couldBeNull == null ? subForNulls : couldBeNull);
+    }
+
+
+    /**
+     * Converts blank strings to another string
+     * @param    couldBeBlank string to be converted if necessary
+     * @param    subForBlanks string to return instead of a blank string
+     * @return   a non-null string, either the original or the substitute
+     *           string if the original was <code>null</code> or empty ("")
+     */
+    protected String blanksToString(String couldBeBlank,
+        String subForBlanks) {
+            return (("".equals(couldBeBlank) || couldBeBlank == null) ?
+                subForBlanks : couldBeBlank);
+    }
+
+
+    protected void log(String s) {
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, s);
+    }
+
+
+    /**
+     * Constructs the Process environment to be supplied to the invoked
+     * process.  Defines an environment no environment variables.
+     * <p>
+     * Should be overriden by subclasses to perform useful setup.
+     * </p>
+     *
+     * @param    req HttpServletRequest request associated with the
+     *           Process' invocation
+     * @return   true if environment was set OK, false if there was a problem
+     *           and no environment was set
+     */
+    protected boolean deriveProcessEnvironment(HttpServletRequest req) {
+
+        Hashtable<String, String> envp = new Hashtable<String, String>();
+        command = getCommand();
+        if (command != null) {
+            workingDirectory = new
+                File(command.substring(0,
+                command.lastIndexOf(File.separator)));
+                envp.put("X_TOMCAT_COMMAND_PATH", command); //for kicks
+        }
+        this.env = envp;
+        return true;
+    }
+
+
+    /**
+     * Gets the root directory of the web application to which this process\
+     * belongs
+     * @return  root directory
+     */
+    public String getWebAppRootDir() {
+        return webAppRootDir;
+    }
+
+
+    public String getContextPath(){
+            return contextPath;
+        }
+
+
+    public ServletContext getContext(){
+            return context;
+        }
+
+
+    public String getServletPath(){
+            return servletPath;
+        }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/RequestUtil.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/RequestUtil.java
new file mode 100644
index 0000000..9ad7f24
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/RequestUtil.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+import javax.servlet.http.Cookie;
+
+import org.apache.naming.Util;
+import org.glassfish.grizzly.http.util.ByteChunk;
+import org.glassfish.grizzly.utils.Charsets;
+
+/**
+ * General purpose request parsing and encoding utility methods.
+ *
+ * @author Craig R. McClanahan
+ * @author Tim Tye
+ * @version $Revision: 1.4 $ $Date: 2006/12/12 20:43:07 $
+ */
+
+public final class RequestUtil {
+
+    private static final String SESSION_VERSION_SEPARATOR = ":";
+
+
+    /**
+     * Encode a cookie as per RFC 2109.  The resulting string can be used
+     * as the value for a <code>Set-Cookie</code> header.
+     *
+     * @param cookie The cookie to encode.
+     * @return A string following RFC 2109.
+     */
+    public static String encodeCookie(Cookie cookie) {
+
+        StringBuilder buf = new StringBuilder( cookie.getName() );
+        buf.append("=");
+        buf.append(cookie.getValue());
+
+        if (cookie.getComment() != null) {
+            buf.append("; Comment=\"");
+            buf.append(cookie.getComment());
+            buf.append("\"");
+        }
+
+        if (cookie.getDomain() != null) {
+            buf.append("; Domain=\"");
+            buf.append(cookie.getDomain());
+            buf.append("\"");
+        }
+
+        if (cookie.getMaxAge() >= 0) {
+            buf.append("; Max-Age=\"");
+            buf.append(cookie.getMaxAge());
+            buf.append("\"");
+        }
+
+        if (cookie.getPath() != null) {
+            buf.append("; Path=\"");
+            buf.append(cookie.getPath());
+            buf.append("\"");
+        }
+
+        if (cookie.getSecure()) {
+            buf.append("; Secure");
+        }
+
+        if (cookie.getVersion() > 0) {
+            buf.append("; Version=\"");
+            buf.append(cookie.getVersion());
+            buf.append("\"");
+        }
+
+        return (buf.toString());
+    }
+
+
+    /**
+     * Normalize a relative URI path that may have relative values ("/./",
+     * "/../", and so on ) it it.  <strong>WARNING</strong> - This method is
+     * useful only for normalizing application-generated paths.  It does not
+     * try to perform security checks for malicious input.
+     *
+     * @param path Relative path to be normalized
+     */
+    public static String normalize(String path) {
+        return normalize(path, true);
+    }
+
+    /**
+     * Normalize a relative URI path that may have relative values ("/./",
+     * "/../", and so on ) it it.  <strong>WARNING</strong> - This method is
+     * useful only for normalizing application-generated paths.  It does not
+     * try to perform security checks for malicious input.
+     *
+     * @param path Relative path to be normalized
+     * @param replaceBackSlash Should '\\' be replaced with '/'
+     */
+    public static String normalize(String path, boolean replaceBackSlash) {
+        // Implementation has been moved to org.apache.naming.Util
+        // so that it may be accessed by code in web-naming
+        return Util.normalize(path, replaceBackSlash);
+    }
+
+
+    /**
+     * 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 parseCharacterEncoding(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());
+
+    }
+
+
+    /**
+     * Parse a cookie header into an array of cookies according to RFC 2109.
+     *
+     * @param header Value of an HTTP "Cookie" header
+     */
+    public static Cookie[] parseCookieHeader(String header) {
+
+        if ((header == null) || (header.length() < 1))
+            return (new Cookie[0]);
+
+        ArrayList<Cookie> cookies = new ArrayList<Cookie>();
+        while (header.length() > 0) {
+            int semicolon = header.indexOf(';');
+            if (semicolon < 0)
+                semicolon = header.length();
+            if (semicolon == 0)
+                break;
+            String token = header.substring(0, semicolon);
+            if (semicolon < header.length())
+                header = header.substring(semicolon + 1);
+            else
+                header = "";
+            try {
+                int equals = token.indexOf('=');
+                if (equals > 0) {
+                    String name = token.substring(0, equals).trim();
+                    String value = token.substring(equals+1).trim();
+                    cookies.add(new Cookie(name, value));
+                }
+            } catch (Throwable e) {
+                // Ignore
+            }
+        }
+
+        return cookies.toArray(new Cookie[cookies.size()]);
+
+    }
+
+
+    /**
+     * Append request parameters from the specified String to the specified
+     * Map.  It is presumed that the specified Map is not accessed from any
+     * other thread, so no synchronization is performed.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>:  URL decoding is performed
+     * individually on the parsed name and value elements, rather than on
+     * the entire query string ahead of time, to properly deal with the case
+     * where the name or value includes an encoded "=" or "&" character
+     * that would otherwise be interpreted as a delimiter.
+     *
+     * @param map Map that accumulates the resulting parameters
+     * @param data Input string containing request parameters
+     * @param encoding The name of a supported charset used to encode
+     *
+     * @exception IllegalArgumentException if the data is malformed
+     */
+    public static void parseParameters(Map<String, String[]> map, String data,
+            String encoding) throws UnsupportedEncodingException {
+
+        if ((data != null) && (data.length() > 0)) {
+
+            // use the specified encoding to extract bytes out of the
+            // given string so that the encoding is not lost. If an
+            // encoding is not specified, let it use platform default
+            byte[] bytes = null;
+            try {
+                if (encoding == null) {
+                    bytes = data.getBytes(Charset.defaultCharset());
+                } else {
+                    bytes = data.getBytes(Charsets.lookupCharset(encoding));
+                }
+            } catch (UnsupportedCharsetException uee) {
+            }
+
+            parseParameters(map, bytes, encoding);
+        }
+
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded String.
+     * When the byte array is converted to a string, the system default
+     * character encoding is used...  This may be different than some other
+     * servers.
+     *
+     * @param str The url-encoded string
+     *
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String urlDecode(String str) {
+        // Implementation has been moved to org.apache.naming.Util
+        // so that it may be accessed by code in war-util
+        return Util.urlDecode(str);
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded String.
+     *
+     * @param str The url-encoded string
+     * @param enc The encoding to use; if null, the default encoding is used
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String urlDecode(String str, String enc) {
+        // Implementation has been moved to org.apache.naming.Util
+        // so that it may be accessed by code in war-util
+        return Util.urlDecode(str, enc);
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded byte array.
+     *
+     * @param bytes The url-encoded byte array
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String urlDecode(byte[] bytes) {
+        // Implementation has been moved to org.apache.naming.Util
+        // so that it may be accessed by code in war-util
+        return Util.urlDecode(bytes);
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded byte array.
+     *
+     * @param bytes The url-encoded byte array
+     * @param enc The encoding to use; if null, the default encoding is used
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String urlDecode(byte[] bytes, String enc) {
+        // Implementation has been moved to org.apache.naming.Util
+        // so that it may be accessed by code in war-util
+        return Util.urlDecode(bytes, enc);
+    }
+
+
+    /**
+     * Decode (in place) the specified URL-encoded byte chunk, and optionally
+     * return the decoded result as a String
+     *
+     * @param bc The URL-encoded byte chunk to be decoded in place
+     * @param toString true if the decoded result is to be returned as a
+     * String, false otherwise
+     *
+     * @return The decoded result in String form, if <code>toString</code>
+     * is true, or null otherwise
+     *
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String urlDecode(ByteChunk bc, boolean toString) {
+
+        if (bc == null) {
+            return (null);
+        }
+
+        byte[] bytes = bc.getBytes();
+        if (bytes == null) {
+            return (null);
+        }
+
+        int ix = bc.getStart();
+        int end = bc.getEnd();
+        int ox = ix;
+        while (ix < end) {
+            byte b = bytes[ix++];     // Get byte to test
+            if (b == '+') {
+                b = (byte)' ';
+            } else if (b == '%') {
+                b = (byte) ((Util.convertHexDigit(bytes[ix++]) << 4)
+                            + Util.convertHexDigit(bytes[ix++]));
+            }
+            bytes[ox++] = b;
+        }
+        bc.setEnd(ox);
+        if (toString) {
+            return bc.toString();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Put name value pair in map.
+     *
+     * Put name and value pair in map.  When name already exist, add value
+     * to array of values.
+     */
+    private static void putMapEntry( Map<String, String[]> map, String name, String value) {
+        String[] newValues = null;
+        String[] oldValues = map.get(name);
+        if (oldValues == null) {
+            newValues = new String[1];
+            newValues[0] = value;
+        } else {
+            newValues = new String[oldValues.length + 1];
+            System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
+            newValues[oldValues.length] = value;
+        }
+        map.put(name, newValues);
+    }
+
+
+    /**
+     * Append request parameters from the specified String to the specified
+     * Map.  It is presumed that the specified Map is not accessed from any
+     * other thread, so no synchronization is performed.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>:  URL decoding is performed
+     * individually on the parsed name and value elements, rather than on
+     * the entire query string ahead of time, to properly deal with the case
+     * where the name or value includes an encoded "=" or "&" character
+     * that would otherwise be interpreted as a delimiter.
+     *
+     * NOTE: byte array data is modified by this method.  Caller beware.
+     *
+     * @param map Map that accumulates the resulting parameters
+     * @param data Input string containing request parameters
+     * @param encoding Encoding to use for converting hex
+     *
+     * @exception UnsupportedEncodingException if the data is malformed
+     */
+    public static void parseParameters(Map<String, String[]> map, byte[] data, String encoding)
+        throws UnsupportedEncodingException {
+
+        if (data != null && data.length > 0) {
+            int    pos = 0;
+            int    ix = 0;
+            int    ox = 0;
+            String key = null;
+            String value = null;
+            while (ix < data.length) {
+                byte c = data[ix++];
+                switch ((char) c) {
+                case '&':
+                    value = new String(data, 0, ox, Charsets.lookupCharset(encoding));
+                    if (key != null) {
+                        putMapEntry(map, key, value);
+                        key = null;
+                    }
+                    ox = 0;
+                    break;
+                case '=':
+                    if (key == null) {
+                        key = new String(data, 0, ox, Charsets.lookupCharset(encoding));
+                        ox = 0;
+                    } else {
+                        data[ox++] = c;
+                    }                   
+                    break;  
+                case '+':
+                    data[ox++] = (byte)' ';
+                    break;
+                case '%':
+                    data[ox++] = (byte)((Util.convertHexDigit(data[ix++]) << 4)
+                                    + Util.convertHexDigit(data[ix++]));
+                    break;
+                default:
+                    data[ox++] = c;
+                }
+            }
+            //The last value does not end in '&'.  So save it now.
+            if (key != null) {
+                value = new String(data, 0, ox, Charsets.lookupCharset(encoding));
+                putMapEntry(map, key, value);
+            }
+        }
+
+    }
+
+
+    /**
+     * Parses the given session version string into its components.
+     *
+     * @param sessionVersion The session version string to parse
+     *
+     * @return The mappings from context paths to session version numbers
+     * that were parsed from the given session version string
+     */
+    public static final HashMap<String, String> parseSessionVersionString(
+                                                String sessionVersion) {
+        if (sessionVersion == null) {
+            return null;
+        }
+
+        StringTokenizer st = new StringTokenizer(sessionVersion,
+                                                 SESSION_VERSION_SEPARATOR);
+        HashMap<String, String> result =
+            new HashMap<String, String>(st.countTokens());
+        while (st.hasMoreTokens()) {
+            String hexPath = st.nextToken();
+            if (st.hasMoreTokens()) {
+                try {
+                    String contextPath = new String(
+                            HexUtils.convert(hexPath), Charsets.UTF8_CHARSET);
+                    result.put(contextPath, st.nextToken());
+                } catch(UnsupportedCharsetException ex) {
+                    //should not be here
+                    throw new IllegalArgumentException(ex);
+                }
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Creates the string representation for the given context path to
+     * session version mappings.
+     *
+     * <p>The returned string will be used as the value of a
+     * JSESSIONIDVERSION cookie or jsessionidversion URI parameter, depending
+     * on the configured session tracking mode.
+     *
+     * @param sessionVersions Context path to session version mappings
+     *
+     * @return The resulting string representation, to be used as the value
+     * of a JSESSIONIDVERSION cookie or jsessionidversion URI parameter
+     */
+    public static String createSessionVersionString(Map<String, String> sessionVersions) {
+        if (sessionVersions == null) {
+            return null;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (Map.Entry<String, String> e : sessionVersions.entrySet()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(':');
+            }
+            String contextPath = e.getKey();
+            // encode so that there is no / or %2F
+            try {
+                sb.append(new String(HexUtils.convert(contextPath.getBytes(Charsets.UTF8_CHARSET))));
+            } catch(UnsupportedCharsetException ex) {
+                //should not be here
+                throw new IllegalArgumentException(ex);
+            }
+            sb.append(SESSION_VERSION_SEPARATOR);
+            sb.append(e.getValue());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * This is a convenient API which wraps around the one in Grizzly and throws
+     * checked java.io.UnsupportedEncodingException instead of
+     * unchecked java.nio.charset.UnsupportedCharsetException.
+     * cf. String.getBytes(String charset) throws UnsupportedEncodingException
+     *
+     * @exception UnsupportedEncodingException
+     */
+    public static Charset lookupCharset(String enc) throws UnsupportedEncodingException {
+        Charset charset = null;
+        Throwable throwable = null;
+        try {
+            charset = Charsets.lookupCharset(enc);
+        } catch(Throwable t) {
+            throwable = t;
+        }
+
+        if (charset == null) {
+            UnsupportedEncodingException uee = new UnsupportedEncodingException();
+            if (throwable != null) {
+                uee.initCause(throwable);
+            }
+            throw uee;
+        }
+
+        return charset;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/ResponseUtil.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ResponseUtil.java
new file mode 100644
index 0000000..38bf652
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ResponseUtil.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import javax.servlet.ServletOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+
+public final class ResponseUtil {
+
+    /**
+     * Copies the contents of the specified input stream to the specified
+     * output stream.
+     *
+     * @param istream The input stream to read from
+     * @param ostream The output stream to write to
+     *
+     * @return Exception that occurred during processing, or null
+     */
+    public static IOException copy(InputStream istream,
+                                   ServletOutputStream ostream) {
+
+        IOException exception = null;
+        byte buffer[] = new byte[2048];
+        int len;
+        while (true) {
+            try {
+                len = istream.read(buffer);
+                if (len == -1)
+                    break;
+                ostream.write(buffer, 0, len);
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+                break;
+            }
+        }
+        return exception;
+
+    }
+
+
+    /**
+     * Copies the contents of the specified input stream to the specified
+     * output stream.
+     *
+     * @param reader The reader to read from
+     * @param writer The writer to write to
+     *
+     * @return Exception that occurred during processing, or null
+     */
+    public static IOException copy(Reader reader, PrintWriter writer) {
+
+        IOException exception = null;
+        char buffer[] = new char[2048];
+        int len;
+        while (true) {
+            try {
+                len = reader.read(buffer);
+                if (len == -1)
+                    break;
+                writer.write(buffer, 0, len);
+            } catch (IOException e) {
+                exception = e;
+                len = -1;
+                break;
+            }
+        }
+        return exception;
+
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/SchemaResolver.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/SchemaResolver.java
new file mode 100644
index 0000000..9cc24ab
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/SchemaResolver.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+// END PWC 6457880
+
+/**
+ * This class implements a local SAX's <code>EntityResolver</code>. All
+ * DTDs and schemas used to validate the web.xml file will re-directed
+ * to a local file stored in the servlet-api.jar and jsp-api.jar.
+ *
+ * @author Jean-Francois Arcand
+ */
+public class SchemaResolver implements EntityResolver {
+
+    /**
+     * The digester instance for which this class is the entity resolver.
+     */
+    protected Digester digester;
+
+
+    /**
+     * The URLs of dtds and schemas that have been registered, keyed by the
+     * public identifier that corresponds.
+     */
+    protected HashMap<String, String> entityValidator = new HashMap<String, String>();
+
+
+    /**
+     * Extension to make the difference between DTD and Schema.
+     */
+    protected String schemaExtension = "xsd";
+
+    // START PWC 6457880
+    /**
+     * Attribute value used to turn on/off network access of dtd/schema
+     */
+    private static boolean forceLocalSchema = false;
+    // END PWC 6457880
+
+    /**
+     * Create a new <code>EntityResolver</code> that will redirect
+     * all remote dtds and schema to a local destination.
+     * @param digester schemaLocation the XML Schema used to validate xml instance.
+     */
+    public SchemaResolver(Digester digester) {
+        this.digester = digester;
+    }
+
+
+    /**
+     * Register the specified DTD/Schema URL for the specified public
+     * identifier. This must be called before the first call to
+     * <code>parse()</code>.
+     *
+     * When adding a schema file (*.xsd), only the name of the file
+     * will get added. If two schemas with the same name are added,
+     * only the last one will be stored.
+     *
+     * @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) {
+         String key = publicId;
+         if (publicId.indexOf(schemaExtension) != -1)
+             key = publicId.substring(publicId.lastIndexOf('/')+1);
+         entityValidator.put(key, entityURL);
+     }
+
+
+    /**
+     * 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 (publicId != null) {
+            digester.setPublicId(publicId);
+        }
+
+        // Has this system identifier been registered?
+        String entityURL = null;
+        if (publicId != null) {
+            entityURL = entityValidator.get(publicId);
+        }
+
+        // Redirect the schema location to a local destination
+        String key = null;
+        if (entityURL == null && systemId != null) {
+            key = systemId.substring(systemId.lastIndexOf('/')+1);
+            entityURL = entityValidator.get(key);
+        }
+
+/* PWC 6457880
+        if (entityURL == null) {
+           return (null);
+        }
+
+*/
+        // START PWC 6457880
+        if (entityURL == null) {
+            if (forceLocalSchema) {
+                URI u;
+                try {
+                    u  = new URI(systemId);
+                } catch (URISyntaxException e) {
+                    throw new SAXException(e);
+                }
+                String scheme = u.getScheme();
+                // if the scheme is local, let the digester look it up
+                // otherwise, throw an exception
+                if (scheme != null && (scheme.equals("file") ||
+                    scheme.equals("jar"))) {
+                    return (null);
+                }
+                else {
+                    throw new SAXException("Unable to find local schema for "+key);
+                }
+            }
+            else {
+                return (null);
+            }
+        }
+        // END PWC 6457880
+        try {
+            return (new InputSource(entityURL));
+        } catch (Exception e) {
+            throw new SAXException(e);
+        }
+
+    }
+
+    // START PWC 6457880
+    public static void setForceLocalSchema(boolean flag) {
+        forceLocalSchema = flag;
+    }
+    // END PWC 6457880
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/ServerInfo.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ServerInfo.java
new file mode 100644
index 0000000..3345a0a
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/ServerInfo.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+/**
+ * Simple utility module to make it easy to plug in the server identifier
+ * when integrating Tomcat.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:28:20 $
+ */
+
+public class ServerInfo {
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The server information string used for logging
+     */
+    private static String serverInfo = null;
+
+    /**
+     * The public server information string that is exposed in
+     * container-generated error pages and as the value of the "Server"
+     * HTTP response header
+     */
+    private static String publicServerInfo = null;
+
+    static {
+
+        // BEGIN S1AS 5022949
+        /*
+        try {
+            InputStream is = ServerInfo.class.getResourceAsStream
+                ("/org/apache/catalina/util/ServerInfo.properties");
+            Properties props = new Properties();
+            props.load(is);
+            is.close();
+            serverInfo = props.getProperty("server.info");
+        } catch (Throwable t) {
+            ;
+        }
+        if (serverInfo == null)
+            serverInfo = "Apache Tomcat";
+        */
+        // END S1AS 5022949
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // START PWC 5022949
+    public static void setServerInfo(String info) {
+        serverInfo = info;
+    }
+    // END PWC 5022949
+
+    /**
+     * Return the server identification for this version of Tomcat.
+     */
+    public static String getServerInfo() {
+        return (serverInfo);
+    }
+
+    public static void setPublicServerInfo(String info) {
+        publicServerInfo = info;
+    }
+
+    public static String getPublicServerInfo() {
+        return publicServerInfo;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/Strftime.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/Strftime.java
new file mode 100644
index 0000000..446902f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/Strftime.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.TimeZone;
+
+/**
+ * Converts dates to strings using the same format specifiers as strftime
+ *
+ * Note: This does not mimic strftime perfectly.  Certain strftime commands, 
+ *       are not supported, and will convert as if they were literals.
+ *
+ *       Certain complicated commands, like those dealing with the week of the year
+ *       probably don't have exactly the same behavior as strftime.
+ *
+ *       These limitations are due to use SimpleDateTime.  If the conversion was done
+ *       manually, all these limitations could be eliminated.
+ *
+ *       The interface looks like a subset of DateFormat.  Maybe someday someone will make this class
+ *       extend DateFormat.
+ *
+ * @author Bip Thelin
+ * @author Dan Sandberg
+ * @version $Revision: 1.3 $, $Date: 2005/12/08 01:28:20 $
+ */
+public class Strftime {
+    protected static final Properties translate;
+    protected SimpleDateFormat simpleDateFormat;
+
+    /**
+     * Initialize our pattern translation
+     */
+    static {
+        translate = new Properties();
+        translate.put("a","EEE");
+        translate.put("A","EEEE");
+        translate.put("b","MMM");
+        translate.put("B","MMMM");
+        translate.put("c","EEE MMM d HH:mm:ss yyyy");
+
+        //There's no way to specify the century in SimpleDateFormat.  We don't want to hard-code
+        //20 since this could be wrong for the pre-2000 files.
+        //translate.put("C", "20");
+        translate.put("d","dd");
+        translate.put("D","MM/dd/yy");
+        translate.put("e","dd"); //will show as '03' instead of ' 3'
+        translate.put("F","yyyy-MM-dd");
+        translate.put("g","yy");
+        translate.put("G","yyyy");
+        translate.put("H","HH");
+        translate.put("h","MMM");
+        translate.put("I","hh");
+        translate.put("j","DDD");
+        translate.put("k","HH"); //will show as '07' instead of ' 7'
+        translate.put("l","hh"); //will show as '07' instead of ' 7'
+        translate.put("m","MM");
+        translate.put("M","mm");
+        translate.put("n","\n");
+        translate.put("p","a");
+        translate.put("P","a");  //will show as pm instead of PM
+        translate.put("r","hh:mm:ss a");
+        translate.put("R","HH:mm");
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("s","seconds since epoch");
+        translate.put("S","ss");
+        translate.put("t","\t");
+        translate.put("T","HH:mm:ss");
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("u","day of week ( 1-7 )");
+
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("U","week in year with first Sunday as first day...");
+
+        translate.put("V","ww"); //I'm not sure this is always exactly the same
+
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("W","week in year with first Monday as first day...");
+
+        //There's no way to specify this with SimpleDateFormat
+        //translate.put("w","E");
+        translate.put("X","HH:mm:ss");
+        translate.put("x","MM/dd/yy");
+        translate.put("y","yy");
+        translate.put("Y","yyyy");
+        translate.put("Z","z");
+        translate.put("z","Z");
+        translate.put("%","%");
+    }
+
+
+    /**
+     * Create an instance of this date formatting class
+     *
+     * @see #Strftime( String, Locale )
+     */
+    public Strftime( String origFormat ) {
+        String convertedFormat = convertDateFormat( origFormat );
+        simpleDateFormat = new SimpleDateFormat( convertedFormat );
+    }
+
+    /**
+     * Create an instance of this date formatting class
+     * 
+     * @param origFormat the strftime-style formatting string
+     * @param locale to use for locale-specific conversions
+     */
+    public Strftime( String origFormat, Locale locale ) {
+        String convertedFormat = convertDateFormat( origFormat );
+        simpleDateFormat = new SimpleDateFormat( convertedFormat, locale );
+    }
+
+    /**
+     * Format the date according to the strftime-style string given in the constructor.
+     *
+     * @param date the date to format
+     * @return the formatted date
+     */
+    public String format( Date date ) {
+        return simpleDateFormat.format( date );
+    }
+
+    /**
+     * Get the timezone used for formatting conversions
+     *
+     * @return the timezone
+     */
+    public TimeZone getTimeZone() {
+        return simpleDateFormat.getTimeZone();
+    }
+
+    /**
+     * Change the timezone used to format dates
+     *
+     * @see java.text.SimpleDateFormat#setTimeZone
+     */
+    public void setTimeZone( TimeZone timeZone ) {
+        simpleDateFormat.setTimeZone( timeZone );
+    }
+
+    /**
+     * Search the provided pattern and get the C standard
+     * Date/Time formatting rules and convert them to the
+     * Java equivalent.
+     *
+     * @param pattern The pattern to search
+     * @return The modified pattern
+     */
+    protected String convertDateFormat( String pattern ) {
+        boolean inside = false;
+        boolean mark = false;
+        boolean modifiedCommand = false;
+
+        StringBuilder buf = new StringBuilder();
+
+        for(int i = 0; i < pattern.length(); i++) {
+            char c = pattern.charAt(i);
+
+            if ( c=='%' && !mark ) {
+                mark=true;
+            } else {
+                if ( mark ) {
+                    if ( modifiedCommand ) {
+                        //don't do anything--we just wanted to skip a char
+                        modifiedCommand = false;
+                        mark = false;
+                    } else {
+                        inside = translateCommand( buf, pattern, i, inside );
+                        //It's a modifier code
+                        if ( c=='O' || c=='E' ) {
+                            modifiedCommand = true;
+                        } else {
+                            mark=false;
+                        }
+                    }
+                } else {
+                    if ( !inside && c != ' ' ) {
+                        //We start a literal, which we need to quote
+                        buf.append("'");
+                        inside = true;
+                    }
+                    
+                    buf.append(c);
+                }
+            }
+        }
+
+        if ( buf.length() > 0 ) {
+            char lastChar = buf.charAt( buf.length() - 1 );
+
+            if( lastChar!='\'' && inside ) {
+                buf.append('\'');
+            }
+        }
+        return buf.toString();
+    }
+
+    protected String quote( String str, boolean insideQuotes ) {
+        String retVal = str;
+        if ( !insideQuotes ) {
+            retVal = '\'' + retVal + '\'';
+        }
+        return retVal;
+    }
+
+    /**
+     * try to get the Java Date/Time formating associated with
+     * the C standard provided
+     *
+     * @param buf translated StringBuilder 
+     * @param pattern command to translate
+     * @param index  first character index
+     * @param oldInside whether to close the quotes if inside quote
+     * @return The Java formatting rule to use
+     */
+    protected boolean translateCommand( StringBuilder buf, String pattern, int index, boolean oldInside ) {
+        char firstChar = pattern.charAt( index );
+        boolean newInside = oldInside;
+
+        //O and E are modifiers, they mean to present an alternative representation of the next char
+        //we just handle the next char as if the O or E wasn't there
+        if ( firstChar == 'O' || firstChar == 'E' ) {
+            if ( index + 1 < pattern.length() ) {               
+                newInside = translateCommand( buf, pattern, index + 1, oldInside );
+            } else {
+                buf.append( quote("%" + firstChar, oldInside ) );
+            }
+        } else {
+            String command = translate.getProperty( String.valueOf( firstChar ) );
+            
+            //If we don't find a format, treat it as a literal--That's what apache does
+            if ( command == null ) {
+                buf.append( quote( "%" + firstChar, oldInside ) );
+            } else {
+                //If we were inside quotes, close the quotes
+                if ( oldInside ) {
+                    buf.append( '\'' );
+                }
+                buf.append( command );
+                newInside = false;
+            }
+        }
+        return newInside;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/StringManager.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/StringManager.java
new file mode 100644
index 0000000..3f43ba4
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/StringManager.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.text.MessageFormat;
+import java.util.*;
+
+/**
+ * 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.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ */
+
+public class StringManager {
+    // START SJSAS 6412710
+    private final HashMap<Locale, ResourceBundle> bundles =
+        new HashMap<Locale, ResourceBundle>(5);
+    private String bundleName = null;
+    // END SJSAS 6412710
+
+
+    /**
+     * 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) {
+        /* 6412710
+        String bundleName = packageName + ".LocalStrings";
+        */
+        // START SJSAS 6412710
+        this.bundleName = packageName + ".LocalStrings";
+        // END SJSAS 6412710
+        /* SJSAS 6412710
+        try {
+        */
+        /* SJSAS 6412710
+        } catch( MissingResourceException ex ) {
+            // Try from the current loader ( that's the case for trusted apps )
+            ClassLoader cl=Thread.currentThread().getContextClassLoader();
+            if( cl != null ) {
+                try {
+                    bundle=ResourceBundle.getBundle(bundleName, Locale.getDefault(), cl);
+                    return;
+                } catch(MissingResourceException ex2) {
+                }
+            }
+            if( cl==null )
+                cl=this.getClass().getClassLoader();
+
+            if (log.isDebugEnabled())
+                log.debug("Can't find resource " + bundleName +
+                    " " + cl);
+            if( cl instanceof URLClassLoader ) {
+                if (log.isDebugEnabled())
+                    log.debug( ((URLClassLoader)cl).getURLs());
+            }
+        }
+        */
+    }
+
+
+    /**
+     * Get a string from the underlying resource bundle.
+     *
+     * @param key
+     */
+    public String getString(String key) {
+        /* SJSAS 6412710
+        return MessageFormat.format(getStringInternal(key),(Object[]) null);
+        */
+        // START SJSAS 6412710
+        return getString(key, Locale.getDefault());
+        // END SJSAS 6412710
+    }
+
+    // START SJSAS 6412710
+    public String getString(String key, Locale locale) {
+        return MessageFormat.format(getStringInternal(key, locale),
+                                    (Object[]) null);
+    }
+    // END SJSAS 6412710
+
+
+    protected String getStringInternal(String key) {
+        // START SJSAS 6412710
+        return getStringInternal(key, Locale.getDefault());
+    }
+
+    protected String getStringInternal(String key, Locale locale) {
+        // END SJSAS 6412710
+        if (key == null) {
+            String msg = "key is null";
+
+            throw new NullPointerException(msg);
+        }
+
+        // START SJSAS 6412710
+        ResourceBundle bundle = bundles.get(locale);
+        if (bundle == null) {
+            synchronized (bundles) {
+                bundle = bundles.get(locale);
+                if (bundle == null) {
+                    bundle = ResourceBundle.getBundle(this.bundleName, locale, this.getClass().getClassLoader());
+                    bundles.put(locale, bundle);
+                }
+            }
+        }
+        // END SJSAS 6412710
+
+        if( bundle==null )
+            return key;
+
+        try {
+            return bundle.getString(key);
+        } catch (MissingResourceException mre) {
+            return "Cannot find message associated with key '" + key + "'";
+        }
+    }
+
+    /**
+     * 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) {
+        // START SJSAS 6412710
+        return getString(key, args, Locale.getDefault());
+    }
+
+    public String getString(String key, Object[] args, Locale locale) {
+        // END SJSAS 6412710
+        String iString = null;
+        /* 6412710
+        String value = getStringInternal(key);
+        */
+        // START SJSAS 6412710
+        String value = getStringInternal(key, locale);
+        // END SJSAS 6412710
+
+        // 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
+            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";
+                }
+            }
+
+            iString = MessageFormat.format(value, nonNullArgs);
+        } catch (IllegalArgumentException iae) {
+            StringBuilder buf = new StringBuilder();
+            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) {
+        /* SJSAS 6412710
+        Object[] args = new Object[] {arg};
+        return getString(key, args);
+        */
+        // START SJSAS 6412710
+        return getString(key, arg, Locale.getDefault());
+        // END SJSAS 6412710
+    }
+
+    // START SJSAS 6412710
+    public String getString(String key, Object arg, Locale locale) {
+        Object[] args = new Object[] {arg};
+        return getString(key, args, locale);
+    }
+    // END SJSAS 6412710
+
+    /**
+     * 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) {
+        /* SJSAS 6412710
+        Object[] args = new Object[] {arg1, arg2};
+        return getString(key, args);
+        */
+        // START SJSAS 6412710
+        return getString(key, arg1, arg2, Locale.getDefault());
+        // END SJSAS 6412710
+    }
+
+    // START SJSAS 6412710
+    public String getString(String key, Object arg1, Object arg2,
+                            Locale locale) {
+        Object[] args = new Object[] {arg1, arg2};
+        return getString(key, args, locale);
+    }
+    // END SJSAS 6412710
+
+    /**
+     * 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) {
+        /* SJSAS 6412710
+        Object[] args = new Object[] {arg1, arg2, arg3};
+        return getString(key, args);
+        */
+        // START SJSAS 6412710
+        return getString(key, arg1, arg2, arg3, Locale.getDefault());
+        // END SJSAS 6412710
+    }
+
+    // START SJSAS 6412710
+    public String getString(String key, Object arg1, Object arg2,
+                            Object arg3, Locale locale) {
+        Object[] args = new Object[] {arg1, arg2, arg3};
+        return getString(key, args, locale);
+    }
+    // END SJSAS 6412710
+
+    /**
+     * 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) {
+        /* SJSAS 6412710
+        Object[] args = new Object[] {arg1, arg2, arg3, arg4};
+        return getString(key, args);
+        */
+        // START SJSAS 6412710
+        return getString(key, arg1, arg2, arg3, arg4, Locale.getDefault());
+        // END SJSAS 6412710
+    }
+
+    // START SJSAS 6412710
+    public String getString(String key, Object arg1, Object arg2,
+                            Object arg3, Object arg4, Locale locale) {
+        Object[] args = new Object[] {arg1, arg2, arg3, arg4};
+        return getString(key, args, locale);
+    }
+    // END SJSAS 6412710
+
+    // START SJSAS 6412710
+    /**
+     * Returns the locale of the resource bundle for the given request locale.
+     */
+    public Locale getResourceBundleLocale(Locale requestLocale) {
+        ResourceBundle bundle = bundles.get(requestLocale);
+        if (bundle == null) {
+            synchronized (bundles) {
+                bundle = bundles.get(requestLocale);
+                if (bundle == null) {
+                    bundle = ResourceBundle.getBundle(this.bundleName,
+                                                      requestLocale);
+                    bundles.put(requestLocale, bundle);
+                }
+            }
+        }
+        return bundle.getLocale();
+    }
+    // END SJSAS 6412710
+
+    // --------------------------------------------------------------
+    // STATIC SUPPORT METHODS
+    // --------------------------------------------------------------
+
+    private static Hashtable<String, StringManager> managers =
+            new Hashtable<String, StringManager>();
+
+    /**
+     * 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
+     */
+
+    public synchronized static StringManager getManager(String packageName) {
+        StringManager mgr = managers.get(packageName);
+
+        if (mgr == null) {
+            mgr = new StringManager(packageName);
+            managers.put(packageName, mgr);
+        }
+        return mgr;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/StringParser.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/StringParser.java
new file mode 100644
index 0000000..4349e64
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/StringParser.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+/**
+ * Utility class for string parsing that is higher performance than
+ * StringParser for simple delimited text cases.  Parsing is performed
+ * by setting the string, and then using the <code>findXxxx()</code> and
+ * <code>skipXxxx()</code> families of methods to remember significant
+ * offsets.  To retrieve the parsed substrings, call the <code>extract()</code>
+ * method with the appropriate saved offset values.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:21 $
+ */
+
+public final class StringParser {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a string parser with no preset string to be parsed.
+     */
+    public StringParser() {
+
+        this(null);
+
+    }
+
+
+    /**
+     * Construct a string parser that is initialized to parse the specified
+     * string.
+     *
+     * @param string The string to be parsed
+     */
+    public StringParser(String string) {
+
+        super();
+        setString(string);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The characters of the current string, as a character array.  Stored
+     * when the string is first specified to speed up access to characters
+     * being compared during parsing.
+     */
+    private char chars[] = null;
+
+
+    /**
+     * The zero-relative index of the current point at which we are
+     * positioned within the string being parsed.  <strong>NOTE</strong>:
+     * the value of this index can be one larger than the index of the last
+     * character of the string (i.e. equal to the string length) if you
+     * parse off the end of the string.  This value is useful for extracting
+     * substrings that include the end of the string.
+     */
+    private int index = 0;
+
+
+    /**
+     * The length of the String we are currently parsing.  Stored when the
+     * string is first specified to avoid repeated recalculations.
+     */
+    private int length = 0;
+
+
+    /**
+     * The String we are currently parsing.
+     */
+    private String string = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the zero-relative index of our current parsing position
+     * within the string being parsed.
+     */
+    public int getIndex() {
+
+        return (this.index);
+
+    }
+
+
+    /**
+     * Return the length of the string we are parsing.
+     */
+    public int getLength() {
+
+        return (this.length);
+
+    }
+
+
+    /**
+     * Return the String we are currently parsing.
+     */
+    public String getString() {
+
+        return (this.string);
+
+    }
+
+
+    /**
+     * Set the String we are currently parsing.  The parser state is also reset
+     * to begin at the start of this string.
+     *
+     * @param string The string to be parsed.
+     */
+    public void setString(String string) {
+
+        this.string = string;
+        if (string != null) {
+            this.length = string.length();
+            chars = this.string.toCharArray();
+        } else {
+            this.length = 0;
+            chars = new char[0];
+        }
+        reset();
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Advance the current parsing position by one, if we are not already
+     * past the end of the string.
+     */
+    public void advance() {
+
+        if (index < length)
+            index++;
+
+    }
+
+
+    /**
+     * Extract and return a substring that starts at the specified position,
+     * and extends to the end of the string being parsed.  If this is not
+     * possible, a zero-length string is returned.
+     *
+     * @param start Starting index, zero relative, inclusive
+     */
+    public String extract(int start) {
+
+        if ((start < 0) || (start >= length))
+            return ("");
+        else
+            return (string.substring(start));
+
+    }
+
+
+    /**
+     * Extract and return a substring that starts at the specified position,
+     * and ends at the character before the specified position.  If this is
+     * not possible, a zero-length string is returned.
+     *
+     * @param start Starting index, zero relative, inclusive
+     * @param end Ending index, zero relative, exclusive
+     */
+    public String extract(int start, int end) {
+
+        if ((start < 0) || (start >= end) || (end > length))
+            return ("");
+        else
+            return (string.substring(start, end));
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of the specified character,
+     * or the index of the character after the last position of the string
+     * if no more occurrences of this character are found.  The current
+     * parsing position is updated to the returned value.
+     *
+     * @param ch Character to be found
+     */
+    public int findChar(char ch) {
+
+        while ((index < length) && (ch != chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of a non-whitespace character,
+     * or the index of the character after the last position of the string
+     * if no more non-whitespace characters are found.  The current
+     * parsing position is updated to the returned value.
+     */
+    public int findText() {
+
+        while ((index < length) && isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of a whitespace character,
+     * or the index of the character after the last position of the string
+     * if no more whitespace characters are found.  The current parsing
+     * position is updated to the returned value.
+     */
+    public int findWhite() {
+
+        while ((index < length) && !isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Reset the current state of the parser to the beginning of the
+     * current string being parsed.
+     */
+    public void reset() {
+
+        index = 0;
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at the
+     * specified character, or until it moves past the end of the string.
+     * Return the final value.
+     *
+     * @param ch Character to be skipped
+     */
+    public int skipChar(char ch) {
+
+        while ((index < length) && (ch == chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at a
+     * non-whitespace character, or until it moves past the end of the string.
+     * Return the final value.
+     */
+    public int skipText() {
+
+        while ((index < length) && !isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at a
+     * whitespace character, or until it moves past the end of the string.
+     * Return the final value.
+     */
+    public int skipWhite() {
+
+        while ((index < length) && isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Is the specified character considered to be whitespace?
+     *
+     * @param ch Character to be checked
+     */
+    protected boolean isWhite(char ch) {
+
+        if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
+            return (true);
+        else
+            return (false);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/TomcatCSS.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/TomcatCSS.java
new file mode 100644
index 0000000..83dbb79
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/TomcatCSS.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+
+public class TomcatCSS {
+
+    public static final String TOMCAT_CSS =
+        "H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} " +
+        "H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} " +
+        "H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} " +
+        "BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} " +
+        "B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} " +
+        "P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}" +
+        "A {color : black;}" +
+        "HR {color : #525D76;}";
+
+
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/URLEncoder.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/URLEncoder.java
new file mode 100644
index 0000000..bd49bd8
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/URLEncoder.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import org.apache.catalina.LogFacade;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * This class is very similar to the java.net.URLEncoder class.
+ *
+ * Unfortunately, with java.net.URLEncoder there is no way to specify to the 
+ * java.net.URLEncoder which characters should NOT be encoded.
+ *
+ * This code was moved from DefaultServlet.java
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ */
+public class URLEncoder {
+
+    private static final Logger log = LogFacade.getLogger();
+
+    static final char[] hexadecimal =
+    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+     'A', 'B', 'C', 'D', 'E', 'F'};
+
+    //Array containing the safe characters set.
+    protected BitSet safeCharacters = new BitSet(256);
+
+    public URLEncoder() {
+        for (char i = 'a'; i <= 'z'; i++) {
+            addSafeCharacter(i);
+        }
+        for (char i = 'A'; i <= 'Z'; i++) {
+            addSafeCharacter(i);
+        }
+        for (char i = '0'; i <= '9'; i++) {
+            addSafeCharacter(i);
+        }
+    }
+
+    public void addSafeCharacter( char c ) {
+	safeCharacters.set( c );
+    }
+
+    public String encode( String path ) {
+        int maxBytesPerChar = 10;
+        int caseDiff = ('a' - 'A');
+        StringBuilder rewrittenPath = new StringBuilder(path.length());
+        ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
+        OutputStreamWriter writer = null;
+        try {
+            writer = new OutputStreamWriter(buf, "UTF8");
+        } catch (UnsupportedEncodingException e) {
+            log.log(Level.WARNING, LogFacade.UTF8_NOT_SUPPORTED_EXCEPTION, e);
+            writer = new OutputStreamWriter(buf);
+        }
+
+        for (int i = 0; i < path.length(); i++) {
+            int c = (int) path.charAt(i);
+            if (safeCharacters.get(c)) {
+                rewrittenPath.append((char)c);
+            } else {
+                // convert to external encoding before hex conversion
+                try {
+                    writer.write(c);
+                    writer.flush();
+                } catch(IOException e) {
+                    buf.reset();
+                    continue;
+                }
+                byte[] ba = buf.toByteArray();
+                for (int j = 0; j < ba.length; j++) {
+                    // Converting each byte in the buffer
+                    byte toEncode = ba[j];
+                    rewrittenPath.append('%');
+                    int low = (int) (toEncode & 0x0f);
+                    int high = (int) ((toEncode & 0xf0) >> 4);
+                    rewrittenPath.append(hexadecimal[high]);
+                    rewrittenPath.append(hexadecimal[low]);
+                }
+                buf.reset();
+            }
+        }
+        return rewrittenPath.toString();
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/util/XMLWriter.java b/appserver/web/web-core/src/main/java/org/apache/catalina/util/XMLWriter.java
new file mode 100644
index 0000000..5e1ac53
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/util/XMLWriter.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.util;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * XMLWriter helper class.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class XMLWriter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Opening tag.
+     */
+    public static final int OPENING = 0;
+
+
+    /**
+     * Closing tag.
+     */
+    public static final int CLOSING = 1;
+
+
+    /**
+     * Element with no content.
+     */
+    public static final int NO_CONTENT = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Buffer.
+     */
+    protected StringBuilder buffer = new StringBuilder();
+
+
+    /**
+     * Writer.
+     */
+    protected Writer writer = null;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructor.
+     */
+    public XMLWriter() {
+    }
+
+
+    /**
+     * Constructor.
+     */
+    public XMLWriter(Writer writer) {
+        this.writer = writer;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieve generated XML.
+     *
+     * @return String containing the generated XML
+     */
+    public String toString() {
+        return buffer.toString();
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param namespaceInfo Namespace info
+     * @param name Property name
+     * @param value Property value
+     */
+    public void writeProperty(String namespace, String namespaceInfo,
+                              String name, String value) {
+        writeElement(namespace, namespaceInfo, name, OPENING);
+        buffer.append(value);
+        writeElement(namespace, namespaceInfo, name, CLOSING);
+
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param name Property name
+     * @param value Property value
+     */
+    public void writeProperty(String namespace, String name, String value) {
+        writeElement(namespace, name, OPENING);
+        buffer.append(value);
+        writeElement(namespace, name, CLOSING);
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param name Property name
+     */
+    public void writeProperty(String namespace, String name) {
+        writeElement(namespace, name, NO_CONTENT);
+    }
+
+
+    /**
+     * Write an element.
+     *
+     * @param name Element name
+     * @param namespace Namespace abbreviation
+     * @param type Element type
+     */
+    public void writeElement(String namespace, String name, int type) {
+        writeElement(namespace, null, name, type);
+    }
+
+
+    /**
+     * Write an element.
+     *
+     * @param namespace Namespace abbreviation
+     * @param namespaceInfo Namespace info
+     * @param name Element name
+     * @param type Element type
+     */
+    public void writeElement(String namespace, String namespaceInfo,
+                             String name, int type) {
+        if ((namespace != null) && (namespace.length() > 0)) {
+            switch (type) {
+            case OPENING:
+                if (namespaceInfo != null) {
+                    buffer.append("<" + namespace + ":" + name + " xmlns:"
+                                  + namespace + "=\""
+                                  + namespaceInfo + "\">");
+                } else {
+                    buffer.append("<" + namespace + ":" + name + ">");
+                }
+                break;
+            case CLOSING:
+                buffer.append("</" + namespace + ":" + name + ">\n");
+                break;
+            case NO_CONTENT:
+            default:
+                if (namespaceInfo != null) {
+                    buffer.append("<" + namespace + ":" + name + " xmlns:"
+                                  + namespace + "=\""
+                                  + namespaceInfo + "\"/>");
+                } else {
+                    buffer.append("<" + namespace + ":" + name + "/>");
+                }
+                break;
+            }
+        } else {
+            switch (type) {
+            case OPENING:
+                buffer.append("<" + name + ">");
+                break;
+            case CLOSING:
+                buffer.append("</" + name + ">\n");
+                break;
+            case NO_CONTENT:
+            default:
+                buffer.append("<" + name + "/>");
+                break;
+            }
+        }
+    }
+
+
+    /**
+     * Write text.
+     *
+     * @param text Text to append
+     */
+    public void writeText(String text) {
+        buffer.append(text);
+    }
+
+
+    /**
+     * Write data.
+     *
+     * @param data Data to append
+     */
+    public void writeData(String data) {
+        buffer.append("<![CDATA[" + data + "]]>");
+    }
+
+
+    /**
+     * Write XML Header.
+     */
+    public void writeXMLHeader() {
+        buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    }
+
+
+    /**
+     * Send data and reinitializes buffer.
+     */
+    public void sendData()
+        throws IOException {
+        if (writer != null) {
+            writer.write(buffer.toString());
+            buffer = new StringBuilder();
+        }
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/AccessLogValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/AccessLogValve.java
new file mode 100644
index 0000000..4873219
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/AccessLogValve.java
@@ -0,0 +1,1178 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+import org.apache.catalina.*;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+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 java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+import java.util.logging.Level;
+
+/**
+ * <p>Implementation of the <b>Valve</b> interface that generates a web server
+ * access log with the detailed line contents matching a configurable pattern.
+ * The syntax of the available patterns is similar to that supported by the
+ * Apache <code>mod_log_config</code> module.  As an additional feature,
+ * automatic rollover of log files when the date changes is also supported.</p>
+ *
+ * <p>Patterns for the logged message may include constant text or any of the
+ * following replacement strings, for which the corresponding information
+ * from the specified Response is substituted:</p>
+ * <ul>
+ * <li><b>%a</b> - Remote IP address
+ * <li><b>%A</b> - Local IP address
+ * <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if no bytes
+ *     were sent
+ * <li><b>%B</b> - Bytes sent, excluding HTTP headers
+ * <li><b>%h</b> - Remote host name
+ * <li><b>%H</b> - Request protocol
+ * <li><b>%l</b> - Remote logical username from identd (always returns '-')
+ * <li><b>%m</b> - Request method
+ * <li><b>%p</b> - Local port
+ * <li><b>%q</b> - Query string (prepended with a '?' if it exists, otherwise
+ *     an empty string
+ * <li><b>%r</b> - First line of the request
+ * <li><b>%s</b> - HTTP status code of the response
+ * <li><b>%S</b> - User session ID
+ * <li><b>%t</b> - Date and time, in Common Log Format format
+ * <li><b>%u</b> - Remote user that was authenticated
+ * <li><b>%U</b> - Requested URL path
+ * <li><b>%v</b> - Local server name
+ * <li><b>%D</b> - Time taken to process the request, in millis
+ * <li><b>%T</b> - Time taken to process the request, in seconds
+ * </ul>
+ * <p>In addition, the caller can specify one of the following aliases for
+ * commonly utilized patterns:</p>
+ * <ul>
+ * <li><b>common</b> - <code>%h %l %u %t "%r" %s %b</code>
+ * <li><b>combined</b> -
+ *   <code>%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"</code>
+ * </ul>
+ *
+ * <p>
+ * There is also support to write information from the cookie, incoming
+ * header, the Session or something else in the ServletRequest.<br>
+ * It is modeled after the apache syntax:
+ * <ul>
+ * <li><code>%{xxx}i</code> for incoming headers
+ * <li><code>%{xxx}c</code> for a specific cookie
+ * <li><code>%{xxx}r</code> xxx is an attribute in the ServletRequest
+ * <li><code>%{xxx}s</code> xxx is an attribute in the HttpSession
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * Conditional logging is also supported. This can be done with the
+ * <code>condition</code> property.
+ * If the value returned from ServletRequest.getAttribute(condition)
+ * yields a non-null value. The logging will be skipped.
+ * </p>
+ *
+ * @author Craig R. McClanahan
+ * @author Jason Brittain
+ * @version $Revision: 1.4 $ $Date: 2006/10/23 23:18:02 $
+ */
+
+public final class AccessLogValve
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    extends ValveBase
+    implements Lifecycle {
+    */
+    // START CR 6411114
+    extends ValveBase {
+    // END CR 6411114
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new instance of this class with default property values.
+     */
+    public AccessLogValve() {
+
+        super();
+        setPattern("common");
+
+
+    }
+
+
+    // ----------------------------------------------------- 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 dateStamp = "";
+
+
+    /**
+     * The directory in which log files are created.
+     */
+    private String directory = "logs";
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.AccessLogValve/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+    */
+
+    private static final String REQUEST_START_TIME_NOTE =
+        "org.apache.catalina.valves.AccessLogValve.requestStartTime";
+
+    /**
+     * The set of month abbreviations for log messages.
+     */
+    private static final String months[] =
+    { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+
+    /**
+     * If the current log pattern is the same as the common access log
+     * format pattern, then we'll set this variable to true and log in
+     * a more optimal and hard-coded way.
+     */
+    private boolean common = false;
+
+
+    /**
+     * For the combined format (common, plus useragent and referer), we do
+     * the same
+     */
+    private boolean combined = false;
+
+
+    /**
+     * The pattern used to format our access log lines.
+     */
+    private String pattern = null;
+
+
+    /**
+     * The prefix that is added to log file filenames.
+     */
+    private String prefix = "access_log.";
+
+
+    /**
+     * Should we rotate our log file? Default is true (like old behavior)
+     */
+    private boolean rotatable = true;
+
+
+    /**
+     * Has this component been started yet?
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    private boolean started = false;
+    */
+
+
+    /**
+     * The suffix that is added to log file filenames.
+     */
+    private String suffix = "";
+
+
+    /**
+     * The PrintWriter to which we are currently logging, if any.
+     */
+    private PrintWriter writer = null;
+
+
+    /**
+     * ThreadLocal for a date formatter to format a Date into a date in the format
+     * "yyyy-MM-dd".
+     */
+    private volatile ThreadLocal<SimpleDateFormat> dateFormatter = null;
+
+    private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault();
+    
+    /**
+     * ThreadLocal for a date formatter to format Dates into a day string in the format
+     * "dd".
+     */
+    private static final ThreadLocal<SimpleDateFormat> dayFormatter =
+        new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                SimpleDateFormat f = new SimpleDateFormat("dd");
+                f.setTimeZone(DEFAULT_TIME_ZONE);
+                return f;
+            }
+        };
+
+
+    /**
+     * ThreadLocal for a date formatter to format a Date into a month string in the format
+     * "MM".
+     */
+    private static final ThreadLocal<SimpleDateFormat> monthFormatter =
+        new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                SimpleDateFormat f = new SimpleDateFormat("MM");
+                f.setTimeZone(DEFAULT_TIME_ZONE);
+                return f;
+            }
+        };
+
+
+    /**
+     * ThreadLocal for a time taken formatter for 3 decimal places.
+     */
+     private static final ThreadLocal<DecimalFormat> timeTakenFormatter =
+         new ThreadLocal<DecimalFormat>() {
+            @Override
+            protected DecimalFormat initialValue() {
+                 return  new DecimalFormat("0.000");
+            }
+        };
+
+
+    /**
+     * ThreadLocal for a date formatter to format a Date into a year string in the format
+     * "yyyy".
+     */
+    private ThreadLocal<SimpleDateFormat> yearFormatter =
+        new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                SimpleDateFormat f = new SimpleDateFormat("yyyy");
+                f.setTimeZone(DEFAULT_TIME_ZONE);
+                return f;
+            }
+        };
+
+
+    /**
+     * ThreadLocal for a date formatter to format a Date into a time in the format
+     * "kk:mm:ss" (kk is a 24-hour representation of the hour).
+     */
+    private ThreadLocal<SimpleDateFormat> timeFormatter =
+         new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                SimpleDateFormat f = new SimpleDateFormat("HH:mm:ss");
+                f.setTimeZone(DEFAULT_TIME_ZONE);
+                return f;
+            }
+        };
+
+
+    /**
+     * The time zone relative to GMT.
+     */
+    private String timeZone = null;
+
+
+    /**
+     * The system time when we last updated the Date that this valve
+     * uses for log lines.
+     */
+    private Date currentDate = null;
+
+
+    /**
+     * When formatting log lines, we often use strings like this one (" ").
+     */
+    private String space = " ";
+
+
+    /**
+     * Resolve hosts.
+     */
+    private boolean resolveHosts = false;
+
+
+    /**
+     * Instant when the log daily rotation was last checked.
+     */
+    private volatile long rotationLastChecked = 0L;
+
+
+    /**
+     * Are we doing conditional logging. default false.
+     */
+    private String condition = null;
+
+
+    /**
+     * Date format to place in log file name. Use at your own risk!
+     */
+    private String fileDateFormat = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the directory in which we create log files.
+     */
+    public String getDirectory() {
+
+        return (directory);
+
+    }
+
+
+    /**
+     * Set the directory in which we create log files.
+     *
+     * @param directory The new log file directory
+     */
+    public void setDirectory(String directory) {
+
+        this.directory = directory;
+
+    }
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    /**
+     * Return the format pattern.
+     */
+    public String getPattern() {
+
+        return (this.pattern);
+
+    }
+
+
+    /**
+     * Set the format pattern, first translating any recognized alias.
+     *
+     * @param pattern The new pattern
+     */
+    public void setPattern(String pattern) {
+
+        if (pattern == null)
+            pattern = "";
+        if (pattern.equals(Constants.AccessLog.COMMON_ALIAS))
+            pattern = Constants.AccessLog.COMMON_PATTERN;
+        if (pattern.equals(Constants.AccessLog.COMBINED_ALIAS))
+            pattern = Constants.AccessLog.COMBINED_PATTERN;
+        this.pattern = pattern;
+
+        if (this.pattern.equals(Constants.AccessLog.COMMON_PATTERN))
+            common = true;
+        else
+            common = false;
+
+        if (this.pattern.equals(Constants.AccessLog.COMBINED_PATTERN))
+            combined = true;
+        else
+            combined = false;
+
+    }
+
+
+    /**
+     * Return the log file prefix.
+     */
+    public String getPrefix() {
+
+        return (prefix);
+
+    }
+
+
+    /**
+     * Set the log file prefix.
+     *
+     * @param prefix The new log file prefix
+     */
+    public void setPrefix(String prefix) {
+
+        this.prefix = prefix;
+
+    }
+
+
+    /**
+     * Should we rotate the logs
+     */
+    public boolean isRotatable() {
+
+        return rotatable;
+
+    }
+
+
+    /**
+     * Set the value is we should we rotate the logs
+     *
+     * @param rotatable true is we should rotate.
+     */
+    public void setRotatable(boolean rotatable) {
+
+        this.rotatable = rotatable;
+
+    }
+
+
+    /**
+     * Return the log file suffix.
+     */
+    public String getSuffix() {
+
+        return (suffix);
+
+    }
+
+
+    /**
+     * Set the log file suffix.
+     *
+     * @param suffix The new log file suffix
+     */
+    public void setSuffix(String suffix) {
+
+        this.suffix = suffix;
+
+    }
+
+
+    /**
+     * Set the resolve hosts flag.
+     *
+     * @param resolveHosts The new resolve hosts value
+     */
+    public void setResolveHosts(boolean resolveHosts) {
+
+        this.resolveHosts = resolveHosts;
+
+    }
+
+
+    /**
+     * Get the value of the resolve hosts flag.
+     */
+    public boolean isResolveHosts() {
+
+        return resolveHosts;
+
+    }
+
+
+    /**
+     * Return whether the attribute name to look for when
+     * performing conditional logging. If null, every
+     * request is logged.
+     */
+    public String getCondition() {
+
+        return condition;
+
+    }
+
+
+    /**
+     * Set the ServletRequest.attribute to look for to perform
+     * conditional logging. Set to null to log everything.
+     *
+     * @param condition Set to null to log everything
+     */
+    public void setCondition(String condition) {
+
+        this.condition = condition;
+
+    }
+
+    /**
+     *  Return the date format date based log rotation.
+     */
+    public String getFileDateFormat() {
+        return fileDateFormat;
+    }
+
+
+    /**
+     *  Set the date format date based log rotation.
+     */
+    public void setFileDateFormat(String fileDateFormat) {
+        this.fileDateFormat =  fileDateFormat;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Log a message summarizing the specified request and response, according
+     * to the format specified by the <code>pattern</code> property.
+     *
+     * @param request Request being processed
+     * @param response Response being processed
+     *
+     * @exception IOException if an input/output error has occurred
+     * @exception ServletException if a servlet error has occurred
+     */
+    public int invoke(Request request, Response response)
+         throws IOException, ServletException {
+
+        // Pass this request on to the next valve in our pipeline
+        request.setNote(REQUEST_START_TIME_NOTE, Long.valueOf(System.currentTimeMillis()));
+
+        return INVOKE_NEXT;
+    }
+
+
+    public void postInvoke(Request request, Response response){
+
+        long t2 = System.currentTimeMillis();
+        Object startTimeObj = request.getNote(REQUEST_START_TIME_NOTE);
+        if (!(startTimeObj instanceof Long)) {
+            // should not happen
+            return;
+        }
+
+        long time = t2 - ((Long)startTimeObj).longValue();
+
+        if (condition!=null &&
+                null!=request.getRequest().getAttribute(condition)) {
+             return;
+
+        }
+
+
+        Date date = getDate();
+        StringBuilder result = new StringBuilder();
+
+        // Check to see if we should log using the "common" access log pattern
+        if (common || combined) {
+            String value = null;
+
+            ServletRequest req = request.getRequest();
+            HttpServletRequest hreq = (HttpServletRequest) req;
+
+            if (isResolveHosts())
+                result.append(req.getRemoteHost());
+            else
+                result.append(req.getRemoteAddr());
+
+            result.append(" - ");
+
+            value = hreq.getRemoteUser();
+            if (value == null)
+                result.append("- ");
+            else {
+                result.append(value);
+                result.append(space);
+            }
+
+            result.append("[");
+            result.append(dayFormatter.get().format(date));            // Day
+            result.append('/');
+            result.append(lookup(monthFormatter.get().format(date))); // Month
+            result.append('/');
+            result.append(yearFormatter.get().format(date));            // Year
+            result.append(':');
+            result.append(timeFormatter.get().format(date));        // Time
+            result.append(space);
+            result.append(timeZone);                            // Time Zone
+            result.append("] \"");
+
+            result.append(hreq.getMethod());
+            result.append(space);
+            result.append(hreq.getRequestURI());
+            if (hreq.getQueryString() != null) {
+                result.append('?');
+                result.append(hreq.getQueryString());
+            }
+            result.append(space);
+            result.append(hreq.getProtocol());
+            result.append("\" ");
+
+            result.append(((HttpResponse) response).getStatus());
+
+            result.append(space);
+
+            int length = response.getContentCount();
+
+            if (length <= 0)
+                value = "-";
+            else
+                value = "" + length;
+            result.append(value);
+
+            if (combined) {
+                result.append(space);
+                result.append("\"");
+                String referer = hreq.getHeader("referer");
+                if(referer != null)
+                    result.append(referer);
+                else
+                    result.append("-");
+                result.append("\"");
+
+                result.append(space);
+                result.append("\"");
+                String ua = hreq.getHeader("user-agent");
+                if(ua != null)
+                    result.append(ua);
+                else
+                    result.append("-");
+                result.append("\"");
+            }
+
+        } else {
+            // Generate a message based on the defined pattern
+            boolean replace = false;
+            for (int i = 0; i < pattern.length(); i++) {
+                char ch = pattern.charAt(i);
+                if (replace) {
+                    /* For code that processes {, the behavior will be ... if I
+                     * do not encounter a closing } - then I ignore the {
+                     */
+                    if ('{' == ch){
+                        StringBuilder name = new StringBuilder();
+                        int j = i + 1;
+                        for(;j < pattern.length() && '}' != pattern.charAt(j); j++) {
+                            name.append(pattern.charAt(j));
+                        }
+                        if (j+1 < pattern.length()) {
+                            /* the +1 was to account for } which we increment now */
+                            j++;
+                            result.append(replace(name.toString(),
+                                                pattern.charAt(j),
+                                                request,
+                                                response));
+                            i=j; /*Since we walked more than one character*/
+                        } else {
+                            //D'oh - end of string - pretend we never did this
+                            //and do processing the "old way"
+                            result.append(replace(ch, date, request, response, time));
+                        }
+                    } else {
+                        result.append(replace(ch, date, request, response,time ));
+                    }
+                    replace = false;
+                } else if (ch == '%') {
+                    replace = true;
+                } else {
+                    result.append(ch);
+                }
+            }
+        }
+        log(result.toString(), date);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Close the currently open log file (if any)
+     */
+    private synchronized void close() {
+
+        if (writer == null)
+            return;
+        writer.flush();
+        writer.close();
+        writer = null;
+        dateStamp = "";
+
+    }
+
+
+    /**
+     * Log the specified message to the log file, switching files if the date
+     * has changed since the previous log call.
+     *
+     * @param message Message to be logged
+     * @param date the current Date object (so this method doesn't need to
+     *        create a new one)
+     */
+    public void log(String message, Date date) {
+
+        if (rotatable){
+            // Only do a logfile switch check once a second, max.
+            long systime = System.currentTimeMillis();
+            if ((systime - rotationLastChecked) > 1000) {
+
+                // We need a new currentDate
+                currentDate = new Date(systime);
+                rotationLastChecked = systime;
+
+                // Check for a change of date
+                String tsDate = dateFormatter.get().format(currentDate);
+
+                // If the date has changed, switch log files
+                if (!dateStamp.equals(tsDate)) {
+                    synchronized (this) {
+                        if (!dateStamp.equals(tsDate)) {
+                            close();
+                            dateStamp = tsDate;
+                            open();
+                        }
+                    }
+                }
+
+            }
+        }
+
+        // Log this message
+        if (writer != null) {
+            writer.println(message);
+        }
+
+    }
+
+
+    /**
+     * Return the month abbreviation for the specified month, which must
+     * be a two-digit String.
+     *
+     * @param month Month number ("01" .. "12").
+     */
+    private String lookup(String month) {
+
+        int index;
+        try {
+            index = Integer.parseInt(month) - 1;
+        } catch (Throwable t) {
+            index = 0;  // Can not happen, in theory
+        }
+        return (months[index]);
+
+    }
+
+
+    /**
+     * Open the new log file for the date specified by <code>dateStamp</code>.
+     */
+    private synchronized void open() {
+
+        // Create the directory if necessary
+        File dir = new File(directory);
+        if (!dir.isAbsolute())
+            dir = new File(System.getProperty("catalina.base"), directory);
+        if (!dir.mkdirs() && !dir.isDirectory()) {
+            log.log(Level.SEVERE, LogFacade.CREATING_DIR_EXCEPTION, dir);
+        }
+
+        // Open the current log file
+        try {
+            String pathname;
+            // If no rotate - no need for dateStamp in fileName
+            if (rotatable){
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + dateStamp + suffix;
+            } else {
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + suffix;
+            }
+            writer = new PrintWriter(new FileWriter(pathname, true), true);
+        } catch (IOException e) {
+            writer = null;
+        }
+
+    }
+
+
+    /**
+     * Return the replacement text for the specified pattern character.
+     *
+     * @param pattern Pattern character identifying the desired text
+     * @param date the current Date so that this method doesn't need to
+     *        create one
+     * @param request Request being processed
+     * @param response Response being processed
+     */
+    private String replace(char pattern, Date date, Request request,
+                           Response response, long time) {
+
+        String value = null;
+
+        ServletRequest req = request.getRequest();
+        HttpServletRequest hreq = (HttpServletRequest) req;
+        ServletResponse res = response.getResponse();
+        HttpServletResponse hres = (HttpServletResponse) res;
+
+        if (pattern == 'a') {
+            value = req.getRemoteAddr();
+        } else if (pattern == 'A') {
+            try {
+                value = InetAddress.getLocalHost().getHostAddress();
+            } catch(Throwable e){
+                value = "127.0.0.1";
+            }
+        } else if (pattern == 'b') {
+            int length = response.getContentCount();
+            if (length <= 0)
+                value = "-";
+            else
+                value = "" + length;
+        } else if (pattern == 'B') {
+            value = "" + response.getContentLength();
+        } else if (pattern == 'h') {
+            value = req.getRemoteHost();
+        } else if (pattern == 'H') {
+            value = req.getProtocol();
+        } else if (pattern == 'l') {
+            value = "-";
+        } else if (pattern == 'm') {
+            if (hreq != null)
+                value = hreq.getMethod();
+            else
+                value = "";
+        } else if (pattern == 'p') {
+            value = "" + req.getServerPort();
+        } else if (pattern == 'D') {
+                    value = "" + time;
+        } else if (pattern == 'q') {
+            String query = null;
+            if (hreq != null)
+                query = hreq.getQueryString();
+            if (query != null)
+                value = "?" + query;
+            else
+                value = "";
+        } else if (pattern == 'r') {
+            if (hreq != null) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(hreq.getMethod());
+                sb.append(space);
+                sb.append(hreq.getRequestURI());
+                if (hreq.getQueryString() != null) {
+                    sb.append('?');
+                    sb.append(hreq.getQueryString());
+                }
+                sb.append(space);
+                sb.append(hreq.getProtocol());
+                value = sb.toString();
+            } else {
+                value = "- - -";
+            }
+        } else if (pattern == 'S') {
+            if (request instanceof org.apache.catalina.connector.Request) {
+                Session sess = ((org.apache.catalina.connector.Request) request).getSessionInternal(false);
+                if (sess != null) {
+                    value = sess.getIdInternal();
+                } else {
+                    value = "-";
+                }
+            } else {
+                if (hreq != null)
+                    if (hreq.getSession(false) != null)
+                        value = hreq.getSession(false).getId();
+                    else value = "-";
+                else
+                    value = "-";
+            }
+        } else if (pattern == 's') {
+            if (hres != null)
+                value = "" + ((HttpResponse) response).getStatus();
+            else
+                value = "-";
+        } else if (pattern == 't') {
+            StringBuilder temp = new StringBuilder("[");
+            temp.append(dayFormatter.get().format(date));             // Day
+            temp.append('/');
+            temp.append(lookup(monthFormatter.get().format(date)));   // Month
+            temp.append('/');
+            temp.append(yearFormatter.get().format(date));            // Year
+            temp.append(':');
+            temp.append(timeFormatter.get().format(date));            // Time
+            temp.append(' ');
+            temp.append(timeZone);                                    // Timezone
+            temp.append(']');
+            value = temp.toString();
+        } else if (pattern == 'T') {
+            value = timeTakenFormatter.get().format(time/1000d);
+        } else if (pattern == 'u') {
+            if (hreq != null)
+                value = hreq.getRemoteUser();
+            if (value == null)
+                value = "-";
+        } else if (pattern == 'U') {
+            if (hreq != null)
+                value = hreq.getRequestURI();
+            else
+                value = "-";
+        } else if (pattern == 'v') {
+            value = req.getServerName();
+        } else {
+            value = "???" + pattern + "???";
+        }
+
+        if (value == null)
+            return ("");
+        else
+            return (value);
+
+    }
+
+
+    /**
+     * Return the replacement text for the specified "header/parameter".
+     *
+     * @param header The header/parameter to get
+     * @param type Where to get it from i=input,c=cookie,r=ServletRequest,s=Session
+     * @param request Request being processed
+     * @param response Response being processed
+     */
+    private String replace(String header, char type, Request request,
+                           Response response) {
+
+        Object value = null;
+
+        ServletRequest req = request.getRequest();
+        HttpServletRequest hreq = (HttpServletRequest) req;
+
+        switch (type) {
+            case 'i':
+                if (null != hreq)
+                    value = hreq.getHeader(header);
+                else
+                    value= "??";
+                break;
+/*
+            // Someone please make me work
+            case 'o':
+                break;
+*/
+            case 'c':
+                 Cookie[] c = hreq.getCookies();
+                 for (int i=0; c != null && i < c.length; i++){
+                     if (header.equals(c[i].getName())){
+                         value = c[i].getValue();
+                         break;
+                     }
+                 }
+                break;
+            case 'r':
+                if (null != hreq)
+                    value = hreq.getAttribute(header);
+                else
+                    value= "??";
+                break;
+            case 's':
+                if (null != hreq) {
+                    HttpSession sess = hreq.getSession(false);
+                    if (null != sess)
+                        value = sess.getAttribute(header);
+                }
+               break;
+            default:
+                value = "???";
+        }
+
+        /* try catch in case toString() barfs */
+        try {
+            if (value!=null)
+                if (value instanceof String)
+                    return (String)value;
+                else
+                    return value.toString();
+            else
+               return "-";
+        } catch(Throwable e) {
+            return "-";
+        }
+    }
+
+
+    /**
+     * This method returns a Date object that is accurate to within one
+     * second.  If a thread calls this method to get a Date and it's been
+     * less than 1 second since a new Date was created, this method
+     * simply gives out the same Date again so that the system doesn't
+     * spend time creating Date objects unnecessarily.
+     */
+    private Date getDate() {
+
+        // Only create a new Date once per second, max.
+        long systime = System.currentTimeMillis();
+        if ((systime - currentDate.getTime()) > 1000) {
+            currentDate = new Date(systime);
+        }
+
+        return currentDate;
+
+    }
+
+
+    private String calculateTimeZoneOffset(long offset) {
+        StringBuilder tz = new StringBuilder();
+        if ((offset<0))  {
+            tz.append("-");
+            offset = -offset;
+        } else {
+            tz.append("+");
+        }
+
+        long hourOffset = offset/(1000*60*60);
+        long minuteOffset = (offset/(1000*60)) % 60;
+
+        if (hourOffset<10)
+            tz.append("0");
+        tz.append(hourOffset);
+
+        if (minuteOffset<10)
+            tz.append("0");
+        tz.append(minuteOffset);
+
+        return tz.toString();
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    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.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+    */
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+    */
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // START CR 6411114
+        if (started)            // Ignore multiple starts
+            return;
+
+        super.start();
+        // END CR 6411114
+
+        // Initialize the timeZone, Date formatters, and currentDate
+        timeZone = calculateTimeZoneOffset(DEFAULT_TIME_ZONE.getRawOffset());
+
+        if (fileDateFormat==null || fileDateFormat.length()==0)
+            fileDateFormat = "yyyy-MM-dd";
+
+        dateFormatter = new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                SimpleDateFormat f = new SimpleDateFormat(fileDateFormat);
+                f.setTimeZone(DEFAULT_TIME_ZONE);
+                return f;
+            }
+        };
+        currentDate = new Date();
+        dateStamp = dateFormatter.get().format(currentDate);
+
+        open();
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // START CR 6411114
+        if (!started)       // Ignore stop if not started
+            return;
+        // END CR 6411114
+
+        close();
+        // START CR 6411114
+        super.stop();
+        // END CR 6411114
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/Constants.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/Constants.java
new file mode 100644
index 0000000..49b65ec
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/Constants.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.valves</code>
+ * package.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public final class Constants {
+
+    public static final String Package = "org.apache.catalina.valves";
+
+    // Constants for the AccessLogValve class
+    public static final class AccessLog {
+        public static final String COMMON_ALIAS = "common";
+        public static final String COMMON_PATTERN = "%h %l %u %t \"%r\" %s %b";
+        public static final String COMBINED_ALIAS = "combined";
+        public static final String COMBINED_PATTERN = "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\"";
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ErrorReportValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ErrorReportValve.java
new file mode 100644
index 0000000..01f19d6
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ErrorReportValve.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Logger;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.glassfish.web.util.HtmlEntityEncoder;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.Writer;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.logging.Level;
+
+/**
+ * <p>Implementation of a Valve that outputs HTML error pages.</p>
+ *
+ * <p>This Valve should be attached at the Host level, although it will work
+ * if attached to a Context.</p>
+ *
+ * <p>HTML code from the Cocoon 2 project.</p>
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @author <a href="mailto:nicolaken@supereva.it">Nicola Ken Barozzi</a> Aisa
+ * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
+ * @version $Revision: 1.19 $ $Date: 2007/05/05 05:32:41 $
+ */
+
+public class ErrorReportValve
+    extends ValveBase {
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.ErrorReportValve/1.0";
+
+    /**
+     * The StringManager for this package.
+     */
+    protected static final StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Invoke the next Valve in the sequence. When the invoke returns, check
+     * the response state, and output an error report is necessary.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+     public int invoke(Request request, Response response)
+         throws IOException, ServletException {
+
+        // Perform the request
+        return INVOKE_NEXT;
+
+     }
+
+     public void postInvoke(Request request, Response response)
+         throws IOException, ServletException {
+
+        ServletRequest sreq = (ServletRequest) request;
+        Throwable throwable =
+            (Throwable) sreq.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+
+        ServletResponse sresp = (ServletResponse) response;
+        if (sresp.isCommitted()) {
+            return;
+        }
+
+        if (throwable != null) {
+
+            // The response is an error
+            response.setError();
+
+            // START PWC 6254469
+            // Save (and later restore) the response encoding, because the
+            // following call to reset() will reset it to the default
+            // encoding (ISO-8859-1).
+            String responseCharEnc = sresp.getCharacterEncoding();
+            // END PWC 6254469
+
+            HttpServletResponse sresponse = (HttpServletResponse) response;
+            // START IT 13858
+            Collection<String> cookieHeaders = sresponse.getHeaders("Set-Cookie");
+            // END IT 13858
+
+            // Reset the response (if possible)
+            try {
+                sresp.reset();
+            } catch (IllegalStateException e) {
+                ;
+            }
+
+            // START PWC 6254469
+            /*
+             * Restore the previously saved response encoding only if it is
+             * different from the default (ISO-8859-1). This is important so
+             * that a subsequent call to ServletResponse.setLocale() has an
+             * opportunity to set it so it corresponds to the resource bundle
+             * locale (see 6412710)
+             */
+            if (responseCharEnc != null && !responseCharEnc.equals(
+                    org.glassfish.grizzly.http.util.Constants.DEFAULT_HTTP_CHARACTER_ENCODING)) {
+                sresp.setCharacterEncoding(responseCharEnc);
+            }
+            // END PWC 6254469
+
+            // START IT 13858
+            for (String cookieHeader : cookieHeaders) {
+                sresponse.addHeader("Set-Cookie", cookieHeader);
+            }
+            // END IT 13858
+
+            /* GlassFish 6386229
+            if (sresponse instanceof HttpServletResponse)
+                ((HttpServletResponse) sresponse).sendError
+                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            */
+            // START GlassFish 6386229
+            sresponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            // END GlassFish 6386229
+        }
+
+        response.setSuspended(false);
+        sresp.setContentType("text/html");
+
+        try {
+            report(request, response, throwable);
+        } catch (Throwable tt) {
+            ;
+        }
+
+    }
+
+
+    /**
+     * Return a String rendering of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ErrorReportValve[");
+        sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Prints out an error report.
+     *
+     * @param request The request being processed
+     * @param response The response being generated
+     * @param throwable The exception that occurred (which possibly wraps
+     *  a root cause exception
+     */
+    protected void report(Request request, Response response,
+                          Throwable throwable)
+        throws IOException {
+
+        /* GlassFish 6386229
+        // Do nothing on non-HTTP responses
+        if (!(response instanceof HttpResponse))
+            return;
+        */
+        HttpResponse hresponse = (HttpResponse) response;
+        /* GlassFish 6386229
+        if (!(response instanceof HttpServletResponse))
+            return;
+        */
+        HttpServletResponse hres = (HttpServletResponse) response;
+        int statusCode = hresponse.getStatus();
+
+        // Do nothing on a 1xx, 2xx and 3xx status
+        // Do nothing if anything has been written already
+        if (statusCode < 400 || (response.getContentCount() > 0))
+            return;
+
+        Throwable rootCause = null;
+
+        if (throwable != null) {
+
+            if (throwable instanceof ServletException)
+                rootCause = ((ServletException) throwable).getRootCause();
+
+        }
+
+        String message = hresponse.getMessage();
+        /* S1AS 4878272
+        if (message == null)
+            message = "";
+        */
+        // BEGIN S1AS 4878272
+        if (message == null) {
+            message = hresponse.getDetailMessage();
+        }
+        if (message == null) {
+            message = "";
+        } else {
+            message = HtmlEntityEncoder.encodeXSS(message);
+        }
+        // END S1AS 4878272
+
+        // Do nothing if there is no report for the specified status code
+        String report = null;
+        try {
+            /* SJSAS 6412710
+            report = sm.getString("http." + statusCode, message);
+            */
+            // START SJSAS 6412710
+            report = sm.getString("http." + statusCode,
+                                  hres.getLocale());
+            // END SJSAS 6412710
+        } catch (Throwable t) {
+            ;
+        }
+        if (report == null)
+            return;
+
+        String errorPage = makeErrorPage(statusCode, message,
+                                         throwable, rootCause,
+                                         report, hres);
+
+        // START SJSAS 6412710
+        /*
+         * If throwable is not null, we've already preserved any non-default
+         * response encoding in postInvoke(), so that the throwable's exception
+         * message can be delivered to the client without any loss of
+         * information. The following call to ServletResponse.setLocale()
+         * will not override the response encoding in this case.
+         * For all other cases, the response encoding will be set according to
+         * the resource bundle locale.
+         */
+        hres.setLocale(sm.getResourceBundleLocale(hres.getLocale()));
+        // END SJSAS 6412710
+
+        /* PWC 6254469
+        // set the charset part of content type before getting the writer
+        */
+        try {
+            hres.setContentType("text/html");
+            /* PWC 6254469
+            hres.setCharacterEncoding("UTF-8");
+            */
+        } catch (Throwable t) {
+            if (debug >= 1)
+                log(rb.getString(LogFacade.SET_CONTENT_TYPE_EXCEPTION), t);
+        }
+
+        try {
+            Writer writer = response.getReporter();
+            if (writer != null) {
+                // If writer is null, it's an indication that the response has
+                // been hard committed already, which should never happen
+                writer.write(errorPage);
+            }
+        } catch (IOException e) {
+            ;
+        } catch (IllegalStateException e) {
+            ;
+        }
+
+    }
+
+
+    /**
+     * 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 = container.getLogger();
+        if (logger != null) {
+            logger.log(this.toString() + ": " + message);
+        } else {
+            log.log(Level.INFO, this.toString() + ": " + message);
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    protected void log(String message, Throwable t) {
+        Logger logger = container.getLogger();
+        if (logger != null) {
+            logger.log(this.toString() + ": " + message, t, Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, this.toString() + ": " + message, t);
+        }
+    }
+
+
+    public static String makeErrorPage(int statusCode,
+                                       String message,
+                                       Throwable throwable,
+                                       Throwable rootCause,
+                                       String report,
+                                       HttpServletResponse response) {
+
+        // START SJSAS 6412710
+        Locale responseLocale = response.getLocale();
+        // END SJSAS 6412710
+
+        String serverInfo = ServerInfo.getPublicServerInfo();
+
+        StringBuilder sb = new StringBuilder();
+
+        MessageFormat mf = new MessageFormat("");
+        mf.setLocale(responseLocale);
+
+        sb.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"");
+        sb.append(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
+        sb.append("<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>");
+        if (serverInfo != null && !serverInfo.isEmpty()) {
+            sb.append(serverInfo).append(" - ");
+        }
+        // START SJSAS 6412710
+        sb.append(sm.getString("errorReportValve.errorReport",
+                responseLocale));
+        // END SJSAS 6412710
+        sb.append("</title>");
+        sb.append("<style type=\"text/css\"><!--");
+        sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
+        sb.append("--></style> ");
+        sb.append("</head><body>");
+        sb.append("<h1>");
+        // START SJSAS 6412710
+        sb.append(sm.getString("errorReportValve.statusHeader",
+                String.valueOf(statusCode), message,
+                responseLocale)).append("</h1>");
+        // END SJSAS 6412710
+        sb.append("<hr/>");
+        sb.append("<p><b>type</b> ");
+        if (throwable != null) {
+            // START SJJAS 6412710
+            sb.append(sm.getString("errorReportValve.exceptionReport",
+                    responseLocale));
+            // END SJSAS 6412710
+        } else {
+            // START SJSAS 6412710
+            sb.append(sm.getString("errorReportValve.statusReport",
+                    responseLocale));
+            // END SJSAS 6412710
+        }
+        sb.append("</p>");
+        sb.append("<p><b>");
+
+        // START SJSAS 6412710
+        sb.append(sm.getString("errorReportValve.message",
+                responseLocale));
+        // END SJSAS 6412710
+        sb.append("</b>");
+        sb.append(message).append("</p>");
+        sb.append("<p><b>");
+        // START SJSAS 6412710
+        sb.append(sm.getString("errorReportValve.description",
+                responseLocale));
+        // END SJSAS 6412710
+        sb.append("</b>");
+        sb.append(report);
+        sb.append("</p>");
+
+        // exception class name or stacktrace can reveal the underlying product
+        // name, so do not include it if product name property has been cleared.
+        if (throwable != null && serverInfo != null && !serverInfo.isEmpty()) {
+            /* GlassFish 823
+            String stackTrace = JdkCompat.getJdkCompat()
+                .getPartialServletStackTrace(throwable);
+            */
+            sb.append("<p><b>");
+            // START SJSAS 6412710
+            sb.append(sm.getString("errorReportValve.exception",
+                    responseLocale));
+            // END SJSAS 6412710
+            sb.append("</b> <pre>");
+            /* SJSAS 6387790
+            sb.append(stackTrace);
+            */
+            /* GlassFish 823
+            // START SJSAS 6387790
+            sb.append(RequestUtil.filter(stackTrace));
+            // END SJSAS 6387790
+            */
+            // START GlassFish 823
+            sb.append(HtmlEntityEncoder.encodeXSS(String.valueOf(throwable)));
+            // END GlassFish 823
+            sb.append("</pre></p>");
+
+            while (rootCause != null) {
+                /* GlassFish 823
+                stackTrace = JdkCompat.getJdkCompat()
+                    .getPartialServletStackTrace(rootCause);
+                */
+                sb.append("<p><b>");
+                // START SJSAS 6412710
+                sb.append(sm.getString("errorReportValve.rootCause",
+                        responseLocale));
+                // END SJSAS 6412710
+                sb.append("</b> <pre>");
+                /* SJSAS 6387790
+                sb.append(stackTrace);
+                */
+                /* GlassFish 823
+                // START SJSAS 6387790
+                sb.append(RequestUtil.filter(stackTrace));
+                // END SJSAS 6387790
+                */
+                // START GlassFish 823
+                sb.append(HtmlEntityEncoder.encodeXSS(String.valueOf(rootCause)));
+                // END GlassFish 823
+                sb.append("</pre></p>");
+
+                /* GlassFish 823
+                // In case root cause is somehow heavily nested
+                try {
+                    rootCause = (Throwable)PropertyUtils.getProperty
+                                                (rootCause, "rootCause");
+                } catch (ClassCastException e) {
+                    rootCause = null;
+                } catch (IllegalAccessException e) {
+                    rootCause = null;
+                } catch (NoSuchMethodException e) {
+                    rootCause = null;
+                } catch (java.lang.reflect.InvocationTargetException e) {
+                    rootCause = null;
+                }
+                */
+                // START GlassFish 823
+                rootCause = rootCause.getCause();
+                // END GlassFish 823
+            }
+
+            sb.append("<p><b>");
+            // START SJSAS 6412710
+            sb.append(sm.getString("errorReportValve.note",
+                    responseLocale));
+            // END SJAS 6412710
+            sb.append("</b> <u>");
+            // START SJSAS 6412710
+            sb.append(sm.getString("errorReportValve.rootCauseInLogs",
+                    serverInfo, responseLocale));
+            // END SJSAS 6412710
+            sb.append("</u></p>");
+
+        }
+
+        sb.append("<hr/>");
+
+        if (serverInfo != null && !serverInfo.isEmpty()) {
+            sb.append("<h3>").append(serverInfo).append("</h3>");
+        }
+
+        sb.append("</body></html>");
+
+        return sb.toString();
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ExtendedAccessLogValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ExtendedAccessLogValve.java
new file mode 100755
index 0000000..240b32f
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ExtendedAccessLogValve.java
@@ -0,0 +1,1509 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.util.ServerInfo;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.URLEncoder;
+import java.text.DecimalFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.TimeZone;
+import java.util.logging.Level;
+
+
+/**
+ * An implementation of the W3c Extended Log File Format. See
+ * http://www.w3.org/TR/WD-logfile.html for more information about the format.
+ *
+ * The following fields are supported:
+ * <ul>
+ * <li><code>c-dns</code>:  Client hostname</li>
+ * <li><code>c-ip</code>:  Client ip address</li>
+ * <li><code>bytes</code>:  bytes served</li>
+ * <li><code>cs-method</code>:  request method</li>
+ * <li><code>cs-uri</code>:  The full uri requested</li>
+ * <li><code>cs-uri-query</code>:  The query string</li>
+ * <li><code>cs-uri-stem</code>:  The uri without query string</li>
+ * <li><code>date</code>:  The date in yyyy-mm-dd  format for GMT</li>
+ * <li><code>s-dns</code>: The server dns entry </li>
+ * <li><code>s-ip</code>:  The server ip address</li>
+ * <li><code>cs(XXX)</code>:  The value of header XXX from client to server</li>
+ * <li><code>sc(XXX)</code>: The value of header XXX from server to client </li>
+ * <li><code>sc-status</code>:  The status code</li>
+ * <li><code>time</code>:  Time the request was served</li>
+ * <li><code>time-taken</code>:  Time (in seconds) taken to serve the request</li>
+ * <li><code>x-A(XXX)</code>: Pull XXX attribute from the servlet context </li>
+ * <li><code>x-C(XXX)</code>: Pull the first cookie of the name XXX </li>
+ * <li><code>x-R(XXX)</code>: Pull XXX attribute from the servlet request </li>
+ * <li><code>x-S(XXX)</code>: Pull XXX attribute from the session </li>
+ * <li><code>x-P(...)</code>:  Call request.getParameter(...)
+ *                             and URLencode it. Helpful to capture
+ *                             certain POST parameters.
+ * </li>
+ * <li>For any of the x-H(...) the following method will be called from the
+ *                HttpServletRequestObject </li>
+ * <li><code>x-H(authType)</code>: getAuthType </li>
+ * <li><code>x-H(characterEncoding)</code>: getCharacterEncoding </li>
+ * <li><code>x-H(contentLength)</code>: getContentLength </li>
+ * <li><code>x-H(locale)</code>:  getLocale</li>
+ * <li><code>x-H(protocol)</code>: getProtocol </li>
+ * <li><code>x-H(remoteUser)</code>:  getRemoteUser</li>
+ * <li><code>x-H(requestedSessionId)</code>: getGequestedSessionId</li>
+ * <li><code>x-H(requestedSessionIdFromCookie)</code>:
+ *                  isRequestedSessionIdFromCookie </li>
+ * <li><code>x-H(requestedSessionIdValid)</code>:
+ *                  isRequestedSessionIdValid</li>
+ * <li><code>x-H(scheme)</code>:  getScheme</li>
+ * <li><code>x-H(secure)</code>:  isSecure</li>
+ * </ul>
+ *
+ *
+ *
+ * <p>
+ * Log rotation can be on or off. This is dictated by the rotatable
+ * property.
+ * </p>
+ *
+ * <p>
+ * For UNIX users, another field called <code>checkExists</code>is also
+ * available. If set to true, the log file's existence will be checked before
+ * each logging. This way an external log rotator can move the file
+ * somewhere and tomcat will start with a new file.
+ * </p>
+ *
+ * <p>
+ * For JMX junkies, a public method called </code>rotate</code> has
+ * been made available to allow you to tell this instance to move
+ * the existing log file to somewhere else start writing a new log file.
+ * </p>
+ *
+ * <p>
+ * Conditional logging is also supported. This can be done with the
+ * <code>condition</code> property.
+ * If the value returned from ServletRequest.getAttribute(condition)
+ * yields a non-null value. The logging will be skipped.
+ * </p>
+ *
+ * <p>
+ * For extended attributes coming from a getAttribute() call,
+ * it is you responsibility to ensure there are no newline or
+ * control characters.
+ * </p>
+ *
+ *
+ * @author Tim Funk
+ * @version $Revision: 1.4 $ $Date: 2006/04/17 16:44:48 $
+ */
+
+public final class ExtendedAccessLogValve
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    extends ValveBase
+    implements Lifecycle {
+    */
+    // START CR 6411114
+    extends ValveBase {
+    // END CR 6411114
+
+    // --------------------------------------------------------- Constructors
+
+    /**
+     * Construct a new instance of this class with default property values.
+     */
+    public ExtendedAccessLogValve() {
+        super();
+    }
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.ExtendedAccessLogValve/1.0";
+
+    private static final String REQUEST_START_TIME_NOTE =
+        "org.apache.catalina.valves.ExtendedAccessLogValve.requestStartTime";
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+    */
+
+    /**
+     * Has this component been started yet?
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    private boolean started = false;
+    */
+
+
+    /**
+     * The as-of date for the currently open log file, or a zero-length
+     * string if there is no open log file.
+     */
+    private String dateStamp = "";
+
+
+    /**
+     * The PrintWriter to which we are currently logging, if any.
+     */
+    private PrintWriter writer = null;
+
+
+    private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");
+
+
+    /**
+     * ThreadLocal for the formatter for the date contained in the file name.
+     */
+    private volatile ThreadLocal<SimpleDateFormat> fileDateFormatter = null;
+
+
+    /**
+     *  ThreadLocal for a date formatter to format a Date into a date in the format
+     * "yyyy-MM-dd".
+     */
+    private static final ThreadLocal<SimpleDateFormat> dateFormatter =
+        new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
+                f.setTimeZone(GMT_TIME_ZONE);
+                return f;
+            }
+        };
+
+
+    /**
+     * ThreadLocal for a date formatter to format a Date into a time in the format
+     * "kk:mm:ss" (kk is a 24-hour representation of the hour).
+     */
+    private static final ThreadLocal<SimpleDateFormat> timeFormatter =
+        new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                SimpleDateFormat f = new SimpleDateFormat("HH:mm:ss");
+                f.setTimeZone(GMT_TIME_ZONE);
+                return f;
+            }
+        };
+
+
+    /**
+     * ThreadLocal for a time taken formatter for 3 decimal places.
+     */
+     private static final ThreadLocal<DecimalFormat> timeTakenFormatter =
+         new ThreadLocal<DecimalFormat>() {
+             @Override
+             protected DecimalFormat initialValue() {
+                 return new DecimalFormat("0.000");
+             }
+         };
+
+
+    /**
+     * My ip address. Look it up once and remember it. Dump this if we can
+     * determine another reliable way to get server ip address since this
+     * server may have many ip's.
+     */
+    private String myIpAddress = null;
+
+
+    /**
+     * My dns name. Look it up once and remember it. Dump this if we can
+     * determine another reliable way to get server name address since this
+     * server may have many ip's.
+     */
+    private String myDNSName = null;
+
+
+    /**
+     * Holder for all of the fields to log after the pattern is decoded.
+     */
+    private FieldInfo[] fieldInfos;
+
+
+    /**
+     * The current log file we are writing to. Helpful when checkExists
+     * is true.
+     */
+    private File currentLogFile = null;
+
+
+
+    /**
+     * The system time when we last updated the Date that this valve
+     * uses for log lines.
+     */
+    private Date currentDate = null;
+
+
+    /**
+     * Instant when the log daily rotation was last checked.
+     */
+    private long rotationLastChecked = 0L;
+
+
+    /**
+     * The directory in which log files are created.
+     */
+    private String directory = "logs";
+
+
+    /**
+     * The pattern used to format our access log lines.
+     */
+    private String pattern = null;
+
+
+    /**
+     * The prefix that is added to log file filenames.
+     */
+    private String prefix = "access_log.";
+
+
+    /**
+     * Should we rotate our log file? Default is true (like old behavior)
+     */
+    private boolean rotatable = true;
+
+
+    /**
+     * The suffix that is added to log file filenames.
+     */
+    private String suffix = "";
+
+
+    /**
+     * Are we doing conditional logging. default false.
+     */
+    private String condition = null;
+
+
+    /**
+     * Do we check for log file existence? Helpful if an external
+     * agent renames the log file so we can automagically recreate it.
+     */
+    private boolean checkExists = false;
+
+
+    /**
+     * Date format to place in log file name. Use at your own risk!
+     */
+    private String fileDateFormat = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the directory in which we create log files.
+     */
+    public String getDirectory() {
+
+        return (directory);
+
+    }
+
+
+    /**
+     * Set the directory in which we create log files.
+     *
+     * @param directory The new log file directory
+     */
+    public void setDirectory(String directory) {
+
+        this.directory = directory;
+
+    }
+
+
+    /**
+     * Return descriptive information about this implementation.
+     */
+    public String getInfo() {
+
+        return (this.info);
+
+    }
+
+
+    /**
+     * Return the format pattern.
+     */
+    public String getPattern() {
+
+        return (this.pattern);
+
+    }
+
+
+    /**
+     * Set the format pattern, first translating any recognized alias.
+     *
+     * @param pattern The new pattern pattern
+     */
+    public void setPattern(String pattern) {
+
+        FieldInfo[] f= decodePattern(pattern);
+        if (f!=null) {
+            this.pattern = pattern;
+            this.fieldInfos = f;
+        }
+    }
+
+
+    /**
+     * Return the log file prefix.
+     */
+    public String getPrefix() {
+
+        return (prefix);
+
+    }
+
+
+    /**
+     * Set the log file prefix.
+     *
+     * @param prefix The new log file prefix
+     */
+    public void setPrefix(String prefix) {
+
+        this.prefix = prefix;
+
+    }
+
+
+    /**
+     * Return true if logs are automatically rotated.
+     */
+    public boolean isRotatable() {
+
+        return rotatable;
+
+    }
+
+
+    /**
+     * Set the value is we should we rotate the logs
+     *
+     * @param rotatable true is we should rotate.
+     */
+    public void setRotatable(boolean rotatable) {
+
+        this.rotatable = rotatable;
+
+    }
+
+
+    /**
+     * Return the log file suffix.
+     */
+    public String getSuffix() {
+
+        return (suffix);
+
+    }
+
+
+    /**
+     * Set the log file suffix.
+     *
+     * @param suffix The new log file suffix
+     */
+    public void setSuffix(String suffix) {
+
+        this.suffix = suffix;
+
+    }
+
+
+
+    /**
+     * Return whether the attribute name to look for when
+     * performing conditional loggging. If null, every
+     * request is logged.
+     */
+    public String getCondition() {
+
+        return condition;
+
+    }
+
+
+    /**
+     * Set the ServletRequest.attribute to look for to perform
+     * conditional logging. Set to null to log everything.
+     *
+     * @param condition Set to null to log everything
+     */
+    public void setCondition(String condition) {
+
+        this.condition = condition;
+
+    }
+
+
+
+    /**
+     * Check for file existence before logging.
+     */
+    public boolean isCheckExists() {
+
+        return checkExists;
+
+    }
+
+
+    /**
+     * Set whether to check for log file existence before logging.
+     *
+     * @param checkExists true meaning to check for file existence.
+     */
+    public void setCheckExists(boolean checkExists) {
+
+        this.checkExists = checkExists;
+
+    }
+
+
+    /**
+     *  Return the date format date based log rotation.
+     */
+    public String getFileDateFormat() {
+        return fileDateFormat;
+    }
+
+
+    /**
+     *  Set the date format date based log rotation.
+     */
+    public void setFileDateFormat(String fileDateFormat) {
+        this.fileDateFormat =  fileDateFormat;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Log a message summarizing the specified request and response, according
+     * to the format specified by the <code>pattern</code> property.
+     *
+     * @param request Request being processed
+     * @param response Response being processed
+     *
+     * @exception IOException if an input/output error has occurred
+     * @exception ServletException if a servlet error has occurred
+     */
+     public int invoke(Request request, Response response)
+         throws IOException, ServletException {
+
+        // Pass this request on to the next valve in our pipeline
+        request.setNote(REQUEST_START_TIME_NOTE, Long.valueOf(System.currentTimeMillis()));
+
+        return INVOKE_NEXT;
+    }
+
+
+    public void postInvoke(Request request, Response response)
+                                    throws IOException, ServletException{
+
+        long endTime = System.currentTimeMillis();
+        Object startTimeObj = request.getNote(REQUEST_START_TIME_NOTE);
+        if (!(startTimeObj instanceof Long)) {
+            // should not happen
+            return;
+        }
+
+        long runTime = endTime - ((Long)startTimeObj).longValue();
+
+        if (fieldInfos==null || condition!=null &&
+              null!=request.getRequest().getAttribute(condition)) {
+             return;
+
+        }
+
+
+        Date date = getDate(endTime);
+        StringBuilder result = new StringBuilder();
+
+        for (int i=0; fieldInfos!=null && i<fieldInfos.length; i++) {
+            switch(fieldInfos[i].type) {
+                case FieldInfo.DATA_CLIENT:
+                    if (FieldInfo.FIELD_IP==fieldInfos[i].location)
+                        result.append(request.getRequest().getRemoteAddr());
+                    else if (FieldInfo.FIELD_DNS==fieldInfos[i].location)
+                        result.append(request.getRequest().getRemoteHost());
+                    else
+                        result.append("?WTF?"); /* This should never happen! */
+                    break;
+                case FieldInfo.DATA_SERVER:
+                    if (FieldInfo.FIELD_IP==fieldInfos[i].location)
+                        result.append(myIpAddress);
+                    else if (FieldInfo.FIELD_DNS==fieldInfos[i].location)
+                        result.append(myDNSName);
+                    else
+                        result.append("?WTF?"); /* This should never happen! */
+                    break;
+                case FieldInfo.DATA_REMOTE:
+                    result.append('?'); /* I don't know how to handle these! */
+                    break;
+                case FieldInfo.DATA_CLIENT_TO_SERVER:
+                    result.append(getClientToServer(fieldInfos[i], request));
+                    break;
+                case FieldInfo.DATA_SERVER_TO_CLIENT:
+                    result.append(getServerToClient(fieldInfos[i], response));
+                    break;
+                case FieldInfo.DATA_SERVER_TO_RSERVER:
+                case FieldInfo.DATA_RSERVER_TO_SERVER:
+                    result.append('-');
+                    break;
+                case FieldInfo.DATA_APP_SPECIFIC:
+                    result.append(getAppSpecific(fieldInfos[i], request));
+                    break;
+                case FieldInfo.DATA_SPECIAL:
+                    if (FieldInfo.SPECIAL_DATE==fieldInfos[i].location)
+                        result.append(dateFormatter.get().format(date));
+                    else if (FieldInfo.SPECIAL_TIME_TAKEN==fieldInfos[i].location)
+                        result.append(timeTakenFormatter.get().format(runTime/1000d));
+                    else if (FieldInfo.SPECIAL_TIME==fieldInfos[i].location)
+                        result.append(timeFormatter.get().format(date));
+                    else if (FieldInfo.SPECIAL_BYTES==fieldInfos[i].location) {
+                        int length = response.getContentCount();
+                        if (length > 0)
+                            result.append(length);
+                        else
+                            result.append("-");
+                    } else if (FieldInfo.SPECIAL_CACHED==fieldInfos[i].location)
+                        result.append('-'); /* I don't know how to evaluate this! */
+                    else
+                        result.append("?WTF?"); /* This should never happen! */
+                    break;
+                default:
+                    result.append("?WTF?"); /* This should never happen! */
+            }
+
+            if (fieldInfos[i].postWhiteSpace!=null) {
+                result.append(fieldInfos[i].postWhiteSpace);
+            }
+        }
+        log(result.toString(), date);
+
+    }
+
+
+    /**
+     * Rename the existing log file to something else. Then open the
+     * old log file name up once again. Intended to be called by a JMX
+     * agent.
+     *
+     *
+     * @param newFileName The file name to move the log file entry to
+     * @return true if a file was rotated with no error
+     */
+    public synchronized boolean rotate(String newFileName) {
+
+        if (currentLogFile!=null) {
+            File holder = currentLogFile;
+            close();
+            try {
+                if (!holder.renameTo(new File(newFileName))) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.FAILED_RENAME_LOG_FILE),
+                                                      newFileName);
+                    log.log(Level.SEVERE, msg);
+                }
+            } catch(Throwable e){
+                String msg = MessageFormat.format(rb.getString(LogFacade.FAILED_RENAME_LOG_FILE),
+                                                  newFileName);
+                log.log(Level.SEVERE, msg, e);
+            }
+
+            /* Make sure date is correct */
+            currentDate = new Date();
+            fileDateFormatter = new ThreadLocal<SimpleDateFormat>() {
+                @Override
+                protected SimpleDateFormat initialValue() {
+                    return new SimpleDateFormat("yyyy-MM-dd");
+                }
+            };
+            dateStamp = dateFormatter.get().format(currentDate);
+
+            open();
+            return true;
+        } else {
+            return false;
+        }
+
+    }
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     *  Return the client to server data.
+     *  @param fieldInfo The field to decode.
+     *  @param request The object we pull data from.
+     *  @return The appropriate value.
+     */
+     private String getClientToServer(FieldInfo fieldInfo, Request request) {
+
+        ServletRequest sr = request.getRequest();
+        HttpServletRequest hsr = null;
+        if (sr instanceof HttpServletRequest)
+            hsr = (HttpServletRequest)sr;
+
+        switch(fieldInfo.location) {
+            case FieldInfo.FIELD_METHOD:
+                return hsr.getMethod();
+            case FieldInfo.FIELD_URI:
+                if (null==hsr.getQueryString())
+                    return hsr.getRequestURI();
+                else
+                    return hsr.getRequestURI() + "?" + hsr.getQueryString();
+            case FieldInfo.FIELD_URI_STEM:
+                return hsr.getRequestURI();
+            case FieldInfo.FIELD_URI_QUERY:
+                if (null==hsr.getQueryString())
+                    return "-";
+                return hsr.getQueryString();
+            case FieldInfo.FIELD_HEADER:
+                return wrap(hsr.getHeader(fieldInfo.value));
+            default:
+                ;
+        }
+
+        return "-";
+
+    }
+
+
+    /**
+     *  Return the server to client data.
+     *  @param fieldInfo The field to decode.
+     *  @param response The object we pull data from.
+     *  @return The appropriate value.
+     */
+    private String getServerToClient(FieldInfo fieldInfo, Response response) {
+        HttpResponse r = (HttpResponse)response;
+        switch(fieldInfo.location) {
+            case FieldInfo.FIELD_STATUS:
+                return "" + r.getStatus();
+            case FieldInfo.FIELD_COMMENT:
+                return "?"; /* Not coded yet*/
+            case FieldInfo.FIELD_HEADER:
+                return wrap(r.getHeader(fieldInfo.value));
+            default:
+                ;
+        }
+
+        return "-";
+
+    }
+
+
+    /**
+     * Get app specific data.
+     * @param fieldInfo The field to decode
+     * @param request Where we will pull the data from.
+     * @return The appropriate value
+     */
+    private String getAppSpecific(FieldInfo fieldInfo, Request request) {
+
+        ServletRequest sr = request.getRequest();
+        HttpServletRequest hsr = null;
+        if (sr instanceof HttpServletRequest)
+            hsr = (HttpServletRequest)sr;
+
+        switch(fieldInfo.xType) {
+            case FieldInfo.X_PARAMETER:
+                return wrap(urlEncode(sr.getParameter(fieldInfo.value)));
+            case FieldInfo.X_REQUEST:
+                return wrap(sr.getAttribute(fieldInfo.value));
+            case FieldInfo.X_SESSION:
+                HttpSession session = null;
+                if (hsr!=null){
+                    session = hsr.getSession(false);
+                    if (session!=null)
+                        return wrap(session.getAttribute(fieldInfo.value));
+                }
+                break;
+            case FieldInfo.X_COOKIE:
+                Cookie[] c = hsr.getCookies();
+                for (int i=0; c != null && i < c.length; i++){
+                    if (fieldInfo.value.equals(c[i].getName())){
+                        return wrap(c[i].getValue());
+                    }
+                 }
+            case FieldInfo.X_APP:
+                return wrap(request.getContext().getServletContext()
+                                .getAttribute(fieldInfo.value));
+            case FieldInfo.X_SERVLET_REQUEST:
+                if (fieldInfo.location==FieldInfo.X_LOC_AUTHTYPE) {
+                    return wrap(hsr.getAuthType());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_REMOTEUSER) {
+                    return wrap(hsr.getRemoteUser());
+                } else if (fieldInfo.location==
+                            FieldInfo.X_LOC_REQUESTEDSESSIONID) {
+                    return wrap(hsr.getRequestedSessionId());
+                } else if (fieldInfo.location==
+                            FieldInfo.X_LOC_REQUESTEDSESSIONIDFROMCOOKIE) {
+                    return wrap(""+hsr.isRequestedSessionIdFromCookie());
+                } else if (fieldInfo.location==
+                            FieldInfo.X_LOC_REQUESTEDSESSIONIDVALID) {
+                    return wrap(""+hsr.isRequestedSessionIdValid());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_CONTENTLENGTH) {
+                    return wrap(""+hsr.getContentLength());
+                } else if (fieldInfo.location==
+                            FieldInfo.X_LOC_CHARACTERENCODING) {
+                    return wrap(hsr.getCharacterEncoding());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_LOCALE) {
+                    return wrap(hsr.getLocale());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_PROTOCOL) {
+                    return wrap(hsr.getProtocol());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_SCHEME) {
+                    return wrap(hsr.getScheme());
+                } else if (fieldInfo.location==FieldInfo.X_LOC_SECURE) {
+                    return wrap(""+hsr.isSecure());
+                }
+                break;
+            default:
+                ;
+        }
+
+        return "-";
+
+    }
+
+
+    /**
+     *  urlEncode the given string. If null or empty, return null.
+     */
+    private String urlEncode(String value) {
+        if (null==value || value.length()==0) {
+            return null;
+        }
+        return URLEncoder.encode(value);
+    }
+
+
+    /**
+     *  Wrap the incoming value into quotes and escape any inner
+     *  quotes with double quotes.
+     *
+     *  @param value - The value to wrap quotes around
+     *  @return '-' if empty of null. Otherwise, toString() will
+     *     be called on the object and the value will be wrapped
+     *     in quotes and any quotes will be escaped with 2
+     *     sets of quotes.
+     */
+    private String wrap(Object value) {
+
+        String svalue;
+        // Does the value contain a " ? If so must encode it
+        if (value==null || "-".equals(value))
+            return "-";
+
+
+        try {
+            svalue = value.toString();
+            if ("".equals(svalue))
+                return "-";
+        } catch(Throwable e){
+            /* Log error */
+            return "-";
+        }
+
+        /* Wrap all quotes in double quotes. */
+        StringBuilder buffer = new StringBuilder(svalue.length()+2);
+        buffer.append('"');
+        int i=0;
+        while (i<svalue.length()) {
+            int j = svalue.indexOf('"', i);
+            if (j==-1) {
+                buffer.append(svalue.substring(i));
+                i=svalue.length();
+            } else {
+                buffer.append(svalue.substring(i, j+1));
+                buffer.append('"');
+                i=j+2;
+            }
+        }
+
+        buffer.append('"');
+        return buffer.toString();
+
+    }
+
+
+    /**
+     * Close the currently open log file (if any)
+     */
+    private synchronized void close() {
+
+        if (writer == null)
+            return;
+        writer.flush();
+        writer.close();
+        writer = null;
+        currentLogFile = null;
+
+    }
+
+
+    /**
+     * Log the specified message to the log file, switching files if the date
+     * has changed since the previous log call.
+     *
+     * @param message Message to be logged
+     * @param date the current Date object (so this method doesn't need to
+     *        create a new one)
+     */
+    private void log(String message, Date date) {
+
+        if (rotatable){
+            // Only do a logfile switch check once a second, max.
+            long systime = System.currentTimeMillis();
+            if ((systime - rotationLastChecked) > 1000) {
+
+                // We need a new currentDate
+                currentDate = new Date(systime);
+                rotationLastChecked = systime;
+
+                // Check for a change of date
+                String tsDate = fileDateFormatter.get().format(currentDate);
+
+                // If the date has changed, switch log files
+                if (!dateStamp.equals(tsDate)) {
+                    synchronized (this) {
+                        if (!dateStamp.equals(tsDate)) {
+                            close();
+                            dateStamp = tsDate;
+                            open();
+                        }
+                    }
+                }
+            }
+        }
+
+        /* In case something external rotated the file instead */
+        if (checkExists){
+            synchronized (this) {
+                if (currentLogFile!=null && !currentLogFile.exists()) {
+                    try {
+                        close();
+                    } catch (Throwable e){
+                        log.log(Level.INFO, LogFacade.NOT_SWALLOWED_INFO, e);
+                    }
+
+                    /* Make sure date is correct */
+                    currentDate = new Date(System.currentTimeMillis());
+                    fileDateFormatter = new ThreadLocal<SimpleDateFormat>() {
+                        @Override
+                        protected SimpleDateFormat initialValue() {
+                            return new SimpleDateFormat("yyyy-MM-dd");
+                        }
+                    };
+                    dateStamp = dateFormatter.get().format(currentDate);
+
+                    open();
+                }
+            }
+        }
+
+        // Log this message
+        if (writer != null) {
+            writer.println(message);
+        }
+
+    }
+
+
+    /**
+     * Open the new log file for the date specified by <code>dateStamp</code>.
+     */
+    private synchronized void open() {
+
+        // Create the directory if necessary
+        File dir = new File(directory);
+        if (!dir.isAbsolute())
+            dir = new File(System.getProperty("catalina.base"), directory);
+        if (!dir.mkdirs() && !dir.isDirectory()) {
+            String msg = MessageFormat.format(rb.getString(LogFacade.FAILED_CREATE_DIR),
+                                              dir);
+            log.log(Level.SEVERE, msg);
+        }
+
+        // Open the current log file
+        try {
+            String pathname;
+
+            // If no rotate - no need for dateStamp in fileName
+            if (rotatable){
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + dateStamp + suffix;
+            } else {
+                pathname = dir.getAbsolutePath() + File.separator +
+                            prefix + suffix;
+            }
+
+            currentLogFile = new File(pathname);
+            writer = new PrintWriter(new FileWriter(pathname, true), true);
+            if (currentLogFile.length()==0) {
+                writer.println("#Fields: " + pattern);
+                writer.println("#Version: 1.0");
+                writer.println("#Software: " + ServerInfo.getServerInfo());
+            }
+
+
+        } catch (IOException e) {
+            writer = null;
+            currentLogFile = null;
+        }
+
+    }
+
+
+    /**
+     * This method returns a Date object that is accurate to within one
+     * second.  If a thread calls this method to get a Date and it's been
+     * less than 1 second since a new Date was created, this method
+     * simply gives out the same Date again so that the system doesn't
+     * spend time creating Date objects unnecessarily.
+     */
+    private Date getDate(long systime) {
+        /* Avoid extra call to System.currentTimeMillis(); */
+        if (0==systime) {
+            systime = System.currentTimeMillis();
+        }
+
+        // Only create a new Date once per second, max.
+        if ((systime - currentDate.getTime()) > 1000) {
+            currentDate.setTime(systime);
+        }
+
+        return currentDate;
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    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.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+    */
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+    */
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+        if (started)
+            throw new LifecycleException
+                (sm.getString("extendedAccessLogValve.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+        */
+        // START CR 6411114
+        if (started)            // Ignore multiple starts
+            return;
+        super.start();
+        // END CR 6411114
+
+        // Initialize the timeZone, Date formatters, and currentDate
+        currentDate = new Date(System.currentTimeMillis());
+        if (fileDateFormat==null || fileDateFormat.length()==0)
+            fileDateFormat = "yyyy-MM-dd";
+        fileDateFormatter = new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                return new SimpleDateFormat(fileDateFormat);
+            }
+        };
+        dateStamp = fileDateFormatter.get().format(currentDate);
+
+        /* Everybody say ick ... ick */
+        try {
+            InetAddress inetAddress = InetAddress.getLocalHost();
+            myIpAddress = inetAddress.getHostAddress();
+            myDNSName = inetAddress.getHostName();
+        } catch(Throwable e){
+            myIpAddress="127.0.0.1";
+            myDNSName="localhost";
+        }
+
+        open();
+
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("extendedAccessLogValve.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+        */
+        // START CR 6411114
+        if (!started)       // Ignore stop if not started
+            return;
+        // END CR 6411114
+
+        close();
+        // START CR 6411114
+        super.stop();
+        // END CR 6411114
+
+    }
+
+
+    /**
+     * Decode the given pattern. Is public so a pattern may
+     * allows to be validated.
+     * @param fields The pattern to decode
+     * @return null on error.  Otherwise array of decoded fields
+     */
+    public FieldInfo[] decodePattern(String fields) {
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "decodePattern, fields=" + fields);
+
+        LinkedList<FieldInfo> list = new LinkedList<FieldInfo>();
+
+        //Ignore leading whitespace.
+        int i=0;
+        for (;i<fields.length() && Character.isWhitespace(fields.charAt(i));i++);
+
+        if (i>=fields.length()) {
+            log.log(Level.INFO, LogFacade.FIELD_EMPTY_INFO);
+            return null;
+        }
+
+        int j;
+        while(i<fields.length()) {
+            if (log.isLoggable(Level.FINE))
+                log.log(Level.FINE, "fields.substring(i)=" + fields.substring(i));
+
+            FieldInfo currentFieldInfo = new FieldInfo();
+
+
+            if (fields.startsWith("date",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_DATE;
+                i+="date".length();
+            } else if (fields.startsWith("time-taken",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_TIME_TAKEN;
+                i+="time-taken".length();
+            } else if (fields.startsWith("time",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_TIME;
+                i+="time".length();
+            } else if (fields.startsWith("bytes",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_BYTES;
+                i+="bytes".length();
+            } else if (fields.startsWith("cached",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
+                currentFieldInfo.location = FieldInfo.SPECIAL_CACHED;
+                i+="cached".length();
+            } else if (fields.startsWith("c-ip",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_CLIENT;
+                currentFieldInfo.location = FieldInfo.FIELD_IP;
+                i+="c-ip".length();
+            } else if (fields.startsWith("c-dns",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_CLIENT;
+                currentFieldInfo.location = FieldInfo.FIELD_DNS;
+                i+="c-dns".length();
+            } else if (fields.startsWith("s-ip",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SERVER;
+                currentFieldInfo.location = FieldInfo.FIELD_IP;
+                i+="s-ip".length();
+            } else if (fields.startsWith("s-dns",i)) {
+                currentFieldInfo.type = FieldInfo.DATA_SERVER;
+                currentFieldInfo.location = FieldInfo.FIELD_DNS;
+                i+="s-dns".length();
+            } else if (fields.startsWith("cs",i)) {
+                i = decode(fields, i+2, currentFieldInfo,
+                            FieldInfo.DATA_CLIENT_TO_SERVER);
+                if (i<0)
+                    return null;
+            } else if (fields.startsWith("sc",i)) {
+                i = decode(fields, i+2, currentFieldInfo,
+                            FieldInfo.DATA_SERVER_TO_CLIENT);
+                if (i<0)
+                    return null;
+            } else if (fields.startsWith("sr",i)) {
+                i = decode(fields, i+2, currentFieldInfo,
+                            FieldInfo.DATA_SERVER_TO_RSERVER);
+                if (i<0)
+                    return null;
+            } else if (fields.startsWith("rs",i)) {
+                i = decode(fields, i+2, currentFieldInfo,
+                            FieldInfo.DATA_RSERVER_TO_SERVER);
+                if (i<0)
+                    return null;
+            } else if (fields.startsWith("x",i)) {
+                i = decodeAppSpecific(fields, i, currentFieldInfo);
+            } else {
+                // Unable to decode ...
+                String msg = MessageFormat.format(rb.getString(LogFacade.UNABLE_DECODE_REST_CHARS),
+                                                  fields.substring(i));
+                log.log(Level.SEVERE, msg);
+                return null;
+            }
+
+            // By this point we should have the field, get the whitespace
+            j=i;
+            for (;j<fields.length() && Character.isWhitespace(fields.charAt(j));j++);
+
+            if (j>=fields.length()) {
+                if (j==i) {
+                    // Special case - end of string
+                    currentFieldInfo.postWhiteSpace = "";
+                } else {
+                    currentFieldInfo.postWhiteSpace = fields.substring(i);
+                    i=j;
+                }
+            } else {
+                currentFieldInfo.postWhiteSpace = fields.substring(i,j);
+                i=j;
+            }
+
+            list.add(currentFieldInfo);
+        }
+
+        i=0;
+        FieldInfo[] f = new FieldInfo[list.size()];
+        for (Iterator<FieldInfo> k = list.iterator(); k.hasNext();)
+             f[i++] = k.next();
+
+        if (log.isLoggable(Level.FINE))
+            log.log(Level.FINE, "finished decoding with length of: " + i);
+
+        return f;
+    }
+
+    /**
+     * Decode the cs or sc fields.
+     * Returns negative on error.
+     *
+     * @param fields The pattern to decode
+     * @param i The string index where we are decoding.
+     * @param fieldInfo Where to store the results
+     * @param type The type we are decoding.
+     * @return -1 on error. Otherwise the new String index.
+     */
+    private int decode(String fields, int i, FieldInfo fieldInfo, short type) {
+
+        if (fields.startsWith("-status",i)) {
+            fieldInfo.location = FieldInfo.FIELD_STATUS;
+            i+="-status".length();
+        } else if (fields.startsWith("-comment",i)) {
+            fieldInfo.location = FieldInfo.FIELD_COMMENT;
+            i+="-comment".length();
+        } else if (fields.startsWith("-uri-query",i)) {
+            fieldInfo.location = FieldInfo.FIELD_URI_QUERY;
+            i+="-uri-query".length();
+        } else if (fields.startsWith("-uri-stem",i)) {
+            fieldInfo.location = FieldInfo.FIELD_URI_STEM;
+            i+="-uri-stem".length();
+        } else if (fields.startsWith("-uri",i)) {
+            fieldInfo.location = FieldInfo.FIELD_URI;
+            i+="-uri".length();
+        } else if (fields.startsWith("-method",i)) {
+            fieldInfo.location = FieldInfo.FIELD_METHOD;
+            i+="-method".length();
+        } else if (fields.startsWith("(",i)) {
+            fieldInfo.location = FieldInfo.FIELD_HEADER;
+            i++;                                  /* Move past the ( */
+            int j = fields.indexOf(')', i);
+            if (j==-1) {                          /* Not found */
+                log.log(Level.SEVERE, LogFacade.NO_CLOSING_BRACKET_FOUND);
+                return -1;
+            }
+            fieldInfo.value = fields.substring(i,j);
+            i=j+1;                                // Move pointer past ) */
+        } else {
+            String msg = MessageFormat.format(rb.getString(LogFacade.CHARACTER_CANNOT_DECODED),
+                                              fields.substring(i));
+            log.log(Level.SEVERE, msg);
+            return -1;
+        }
+
+        fieldInfo.type = type;
+        return i;
+
+    }
+
+
+    /**
+      * Decode app specific log entry.
+      *
+      * Special fields are of the form:
+      * x-C(...) - For cookie
+      * x-A(...) - Value in servletContext
+      * x-S(...) - Value in session
+      * x-R(...) - Value in servletRequest
+      * @param fields The pattern to decode
+      * @param i The string index where we are decoding.
+      * @param fieldInfo Where to store the results
+      * @return -1 on error. Otherwise the new String index.
+      */
+    private int decodeAppSpecific(String fields, int i, FieldInfo fieldInfo) {
+
+        fieldInfo.type = FieldInfo.DATA_APP_SPECIFIC;
+        /* Move past 'x-' */
+        i+=2;
+
+        if (i>=fields.length()) {
+            log.log(Level.SEVERE, LogFacade.END_LINE_REACHED);
+            return -1;
+        }
+
+        switch(fields.charAt(i)) {
+            case 'A':
+                fieldInfo.xType = FieldInfo.X_APP;
+                break;
+            case 'C':
+                fieldInfo.xType = FieldInfo.X_COOKIE;
+                break;
+            case 'R':
+                fieldInfo.xType = FieldInfo.X_REQUEST;
+                break;
+            case 'S':
+                fieldInfo.xType = FieldInfo.X_SESSION;
+                break;
+            case 'H':
+                fieldInfo.xType = FieldInfo.X_SERVLET_REQUEST;
+                break;
+            case 'P':
+                fieldInfo.xType = FieldInfo.X_PARAMETER;
+                break;
+            default:
+                return -1;
+        }
+
+        /* test that next char is a ( */
+        if (i+1!=fields.indexOf('(',i)) {
+            log.log(Level.SEVERE, LogFacade.WRONG_X_PARAM_FORMAT);
+            return -1;
+        }
+        i+=2; /* Move inside of the () */
+
+        /* Look for ending ) and return error if not found. */
+        int j = fields.indexOf(')',i);
+        if (j==-1) {
+            log.log(Level.SEVERE, LogFacade.X_PARAM_NO_CLOSING_BRACKET);
+            return -1;
+        }
+
+        fieldInfo.value = fields.substring(i,j);
+
+        if (fieldInfo.xType == FieldInfo.X_SERVLET_REQUEST) {
+            if ("authType".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_AUTHTYPE;
+            } else if ("remoteUser".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_REMOTEUSER;
+            } else if ("requestedSessionId".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONID;
+            } else if ("requestedSessionIdFromCookie".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONIDFROMCOOKIE;
+            } else if ("requestedSessionIdValid".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONID;
+            } else if ("contentLength".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_CONTENTLENGTH;
+            } else if ("characterEncoding".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_CHARACTERENCODING;
+            } else if ("locale".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_LOCALE;
+            } else if ("protocol".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_PROTOCOL;
+            } else if ("scheme".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_SCHEME;
+            } else if ("secure".equals(fieldInfo.value)){
+                fieldInfo.location = FieldInfo.X_LOC_SECURE;
+            } else {
+                String msg = MessageFormat.format(rb.getString(LogFacade.X_PARAM_CANNOT_DECODE_VALUE),
+                                                  fieldInfo.location);
+                log.log(Level.SEVERE, msg);
+                return -1;
+            }
+        }
+
+        return j+1;
+
+    }
+
+
+}
+
+/**
+ * A simple helper for decoding the pattern.
+ */
+class FieldInfo {
+    /*
+       The goal of the constants listed below is to make the construction of the log
+       entry as quick as possible via numerci decodings of the methods to call instead
+       of performing many String comparisons on each logging request.
+    */
+
+    /* Where the data is located. */
+    static final short DATA_CLIENT = 0;
+    static final short DATA_SERVER = 1;
+    static final short DATA_REMOTE = 2;
+    static final short DATA_CLIENT_TO_SERVER = 3;
+    static final short DATA_SERVER_TO_CLIENT = 4;
+    static final short DATA_SERVER_TO_RSERVER = 5; /* Here to honor the spec. */
+    static final short DATA_RSERVER_TO_SERVER = 6; /* Here to honor the spec. */
+    static final short DATA_APP_SPECIFIC = 7;
+    static final short DATA_SPECIAL = 8;
+
+    /* The type of special fields. */
+    static final short SPECIAL_DATE         = 1;
+    static final short SPECIAL_TIME_TAKEN   = 2;
+    static final short SPECIAL_TIME         = 3;
+    static final short SPECIAL_BYTES        = 4;
+    static final short SPECIAL_CACHED       = 5;
+
+    /* Where to pull the data for prefixed values */
+    static final short FIELD_IP            = 1;
+    static final short FIELD_DNS           = 2;
+    static final short FIELD_STATUS        = 3;
+    static final short FIELD_COMMENT       = 4;
+    static final short FIELD_METHOD        = 5;
+    static final short FIELD_URI           = 6;
+    static final short FIELD_URI_STEM      = 7;
+    static final short FIELD_URI_QUERY     = 8;
+    static final short FIELD_HEADER        = 9;
+
+
+    /* Application Specific parameters */
+    static final short X_REQUEST = 1; /* For x app specific */
+    static final short X_SESSION = 2; /* For x app specific */
+    static final short X_COOKIE  = 3; /* For x app specific */
+    static final short X_APP     = 4; /* For x app specific */
+    static final short X_SERVLET_REQUEST = 5; /* For x app specific */
+    static final short X_PARAMETER = 6; /* For x app specific */
+
+    static final short X_LOC_AUTHTYPE                       = 1;
+    static final short X_LOC_REMOTEUSER                     = 2;
+    static final short X_LOC_REQUESTEDSESSIONID             = 3;
+    static final short X_LOC_REQUESTEDSESSIONIDFROMCOOKIE   = 4;
+    static final short X_LOC_REQUESTEDSESSIONIDVALID        = 5;
+    static final short X_LOC_CONTENTLENGTH                  = 6;
+    static final short X_LOC_CHARACTERENCODING              = 7;
+    static final short X_LOC_LOCALE                         = 8;
+    static final short X_LOC_PROTOCOL                       = 9;
+    static final short X_LOC_SCHEME                         = 10;
+    static final short X_LOC_SECURE                         = 11;
+
+
+
+    /** The field type */
+    short type;
+
+    /** Where to pull the data from? Icky variable name. */
+    short location;
+
+    /** The x- specific place to pull the data from. */
+    short xType;
+
+    /** The field value if needed. Needed for headers and app specific. */
+    String value;
+
+    /** Any white space after this field? Put it here. */
+    String postWhiteSpace = null;
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/JDBCAccessLogValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/JDBCAccessLogValve.java
new file mode 100644
index 0000000..71b0107
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/JDBCAccessLogValve.java
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.sql.*;
+import java.util.Properties;
+
+
+/**
+ * <p>
+ * This Tomcat extension logs server access directly to a database, and can 
+ * be used instead of the regular file-based access log implemented in 
+ * AccessLogValve.
+ * To use, copy into the server/classes directory of the Tomcat installation
+ * and configure in server.xml as:
+ * <pre>
+ * 		&lt;Valve className="AccessLogDBValve"
+ *        	driverName="<i>your_jdbc_driver</i>"
+ *        	connectionURL="<i>your_jdbc_url</i>"
+ *        	pattern="combined" resolveHosts="false"
+ * 		/&gt;
+ * </pre>
+ * </p>
+ * <p>
+ * Many parameters can be configured, such as the database connection (with
+ * <code>driverName</code> and <code>connectionURL</code>),
+ * the table name (<code>tableName</code>)
+ * and the field names (corresponding to the get/set method names).
+ * The same options as AccessLogValve are supported, such as
+ * <code>resolveHosts</code> and <code>pattern</code> ("common" or "combined" 
+ * only).
+ * </p>
+ * <p>
+ * When Tomcat is started, a database connection (with autoReconnect option)
+ * is created and used for all the log activity. When Tomcat is shutdown, the
+ * database connection is closed.
+ * This logger can be used at the level of the Engine context (being shared
+ * by all the defined hosts) or the Host context (one instance of the logger 
+ * per host, possibly using different databases).
+ * </p>
+ * <p>
+ * The database table can be created with the following command:
+ * </p>
+ * <pre>
+ * CREATE TABLE access (
+ * id INT UNSIGNED AUTO_INCREMENT NOT NULL,
+ * ts TIMESTAMP NOT NULL,
+ * remoteHost CHAR(15) NOT NULL,
+ * user CHAR(15),
+ * timestamp TIMESTAMP NOT NULL,
+ * virtualHost VARCHAR(64) NOT NULL,
+ * method VARCHAR(8) NOT NULL,
+ * query VARCHAR(255) NOT NULL,
+ * status SMALLINT UNSIGNED NOT NULL,
+ * bytes INT UNSIGNED NOT NULL,
+ * referer VARCHAR(128),
+ * userAgent VARCHAR(128),
+ * PRIMARY KEY (id),
+ * INDEX (ts),
+ * INDEX (remoteHost),
+ * INDEX (virtualHost),
+ * INDEX (query),
+ * INDEX (userAgent)
+ * );
+ * </pre>
+ * <p>
+ * If the table is created as above, its name and the field names don't need 
+ * to be defined.
+ * </p>
+ * <p>
+ * If the request method is "common", only these fields are used:
+ * <code>remoteHost, user, timeStamp, query, status, bytes</code>
+ * </p>
+ * <p>
+ * <i>TO DO: provide option for excluding logging of certain MIME types.</i>
+ * </p>
+ * 
+ * @version 1.0
+ * @author Andre de Jesus
+ */
+
+public final class JDBCAccessLogValve 
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    extends ValveBase 
+    implements Lifecycle {
+    */
+    // START CR 6411114
+    extends ValveBase {
+    // END CR 6411114
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Class constructor. Initializes the fields with the default values.
+     * The defaults are:
+     * <pre>
+     * 		driverName = null;
+     * 		connectionURL = null;
+     * 		tableName = "access";
+     * 		remoteHostField = "remoteHost";
+     * 		userField = "user";
+     * 		timestampField = "timestamp";
+     * 		virtualHostField = "virtualHost";
+     * 		methodField = "method";
+     * 		queryField = "query";
+     * 		statusField = "status";
+     * 		bytesField = "bytes";
+     * 		refererField = "referer";
+     * 		userAgentField = "userAgent";
+     * 		pattern = "common";
+     * 		resolveHosts = false;
+     * </pre>
+     */
+    public JDBCAccessLogValve() {
+        super();
+        driverName = null;
+        connectionURL = null;
+        tableName = "access";
+        remoteHostField = "remoteHost";
+        userField = "user";
+        timestampField = "timestamp";
+        virtualHostField = "virtualHost";
+        methodField = "method";
+        queryField = "query";
+        statusField = "status";
+        bytesField = "bytes";
+        refererField = "referer";
+        userAgentField = "userAgent";
+        pattern = "common";
+        resolveHosts = false;
+        conn = null;
+        ps = null;
+        currentTimeMillis = new java.util.Date().getTime();
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    private String driverName;
+    private String connectionURL;
+    private String tableName;
+    private String remoteHostField;
+    private String userField;
+    private String timestampField;
+    private String virtualHostField;
+    private String methodField;
+    private String queryField;
+    private String statusField;
+    private String bytesField;
+    private String refererField;
+    private String userAgentField;
+    private String pattern;
+    private boolean resolveHosts;
+
+
+    private Connection conn;
+    private PreparedStatement ps;
+
+
+    private long currentTimeMillis;
+
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = 
+        "org.apache.catalina.valves.JDBCAccessLogValve/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+    */
+
+
+    /**
+     * Has this component been started yet?
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    private boolean started = false;
+    */
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Sets the database driver name.
+     * 
+     * @param driverName The complete name of the database driver class.
+     */
+    public void setDriverName(String driverName) {
+        this.driverName = driverName;
+    }
+
+    /**
+     * Sets the JDBC URL for the database where the log is stored.
+     * 
+     * @param connectionURL The JDBC URL of the database.
+     */
+    public void setConnectionURL(String connectionURL) {
+        this.connectionURL = connectionURL;
+    }
+
+
+    /**
+     * Sets the name of the table where the logs are stored.
+     * 
+     * @param tableName The name of the table.
+     */
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+
+    /**
+     * Sets the name of the field containing the remote host.
+     * 
+     * @param remoteHostField The name of the remote host field.
+     */
+    public void setRemoteHostField(String remoteHostField) {
+        this.remoteHostField = remoteHostField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the remote user name.
+     * 
+     * @param userField The name of the remote user field.
+     */
+    public void setUserField(String userField) {
+        this.userField = userField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the server-determined timestamp.
+     * 
+     * @param timestampField The name of the server-determined timestamp field.
+     */
+    public void setTimestampField(String timestampField) {
+        this.timestampField = timestampField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the virtual host information 
+     * (this is in fact the server name).
+     * 
+     * @param virtualHostField The name of the virtual host field.
+     */
+    public void setVirtualHostField(String virtualHostField) {
+        this.virtualHostField = virtualHostField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the HTTP request method.
+     * 
+     * @param methodField The name of the HTTP request method field.
+     */
+    public void setMethodField(String methodField) {
+        this.methodField = methodField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the URL part of the HTTP query.
+     * 
+     * @param queryField The name of the field containing the URL part of 
+     * the HTTP query.
+     */
+    public void setQueryField(String queryField) {
+        this.queryField = queryField;
+    }
+
+
+  /**
+   * Sets the name of the field containing the HTTP response status code.
+   * 
+   * @param statusField The name of the HTTP response status code field.
+   */  
+    public void setStatusField(String statusField) {
+        this.statusField = statusField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the number of bytes returned.
+     * 
+     * @param bytesField The name of the returned bytes field.
+     */
+    public void setBytesField(String bytesField) {
+        this.bytesField = bytesField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the referer.
+     * 
+     * @param refererField The referer field name.
+     */
+    public void setRefererField(String refererField) {
+        this.refererField = refererField;
+    }
+
+
+    /**
+     * Sets the name of the field containing the user agent.
+     * 
+     * @param userAgentField The name of the user agent field.
+     */
+    public void setUserAgentField(String userAgentField) {
+        this.userAgentField = userAgentField;
+    }
+
+
+    /**
+     * Sets the logging pattern. The patterns supported correspond to the 
+     * file-based "common" and "combined". These are translated into the use 
+     * of tables containing either set of fields.
+     * <P><I>TO DO: more flexible field choices.</I></P>
+     * 
+     * @param pattern The name of the logging pattern.
+     */
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+
+    /**
+     * Determines whether IP host name resolution is done.
+     * 
+     * @param resolveHosts "true" or "false", if host IP resolution 
+     * is desired or not.
+     */
+    public void setResolveHosts(String resolveHosts) {
+        this.resolveHosts = Boolean.valueOf(resolveHosts).booleanValue();
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * This method is invoked by Tomcat on each query.
+     * 
+     * @param request The Request object.
+     * @param response The Response object.
+     * @exception IOException Should not be thrown.
+     * @exception ServletException Database SQLException is wrapped 
+     * in a ServletException.
+     */    
+     public int invoke(Request request, Response response)
+         throws IOException, ServletException {
+
+        return INVOKE_NEXT;
+    }
+
+
+    public void postInvoke(Request request, Response response)
+                                    throws IOException, ServletException{
+
+        ServletRequest req = request.getRequest();
+        HttpServletRequest hreq = null;
+        if(req instanceof HttpServletRequest) 
+            hreq = (HttpServletRequest) req;
+        String remoteHost = "";
+        if(resolveHosts)
+            remoteHost = req.getRemoteHost();
+        else
+            remoteHost = req.getRemoteAddr();
+        String user = "";
+        if(hreq != null)
+            user = hreq.getRemoteUser();
+        String query="";
+        if(hreq != null)
+            query = hreq.getRequestURI();
+        int bytes = response.getContentCount();
+        if(bytes < 0)
+            bytes = 0;
+        int status = ((HttpResponse)response).getStatus();
+
+        synchronized (ps) {
+            try {
+                ps.setString(1, remoteHost);
+                ps.setString(2, user);
+                ps.setTimestamp(3, new Timestamp(getCurrentTimeMillis()));
+                ps.setString(4, query);
+                ps.setInt(5, status);
+                ps.setInt(6, bytes);
+            } catch(SQLException e) {
+                throw new ServletException(e);
+            }
+            if (pattern.equals("common")) {
+                try {
+                    ps.executeUpdate();
+                } catch(SQLException e) {
+                    throw new ServletException(e);
+                }
+            } else if (pattern.equals("combined")) {
+                String virtualHost = "";
+                if(hreq != null)
+                    virtualHost = hreq.getServerName();
+                String method = "";
+                if(hreq != null)
+                    method = hreq.getMethod();
+                String referer = "";
+                if(hreq != null)
+                    referer = hreq.getHeader("referer");
+                String userAgent = "";
+                if(hreq != null)
+                    userAgent = hreq.getHeader("user-agent");
+                try {
+                    ps.setString(7, virtualHost);
+                    ps.setString(8, method);
+                    ps.setString(9, referer);
+                    ps.setString(10, userAgent);
+                    ps.executeUpdate();
+                } catch (SQLException e) {
+                    throw new ServletException(e);
+                }
+            }
+        }
+
+    }	
+
+
+    /**
+     * Adds a Lifecycle listener.
+     * 
+     * @param listener The listener to add.
+     */  
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    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.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return lifecycle.findLifecycleListeners();
+
+    }
+    */
+
+
+    /**
+     * Removes a Lifecycle listener.
+     * 
+     * @param listener The listener to remove.
+     */
+    /** CR 6411114 (Lifecycle implementation moved to ValveBase)
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+    */
+
+
+    /**
+     * Invoked by Tomcat on startup. The database connection is set here.
+     * 
+     * @exception LifecycleException Can be thrown on lifecycle 
+     * inconsistencies or on database errors (as a wrapped SQLException).
+     */
+    public void start() throws LifecycleException {
+
+        // START CR 6411114
+        if (started)            // Ignore multiple starts
+            return;
+        super.start();
+        // END CR 6411114
+
+        try {
+            Class.forName(driverName).newInstance(); 
+        } catch (ClassNotFoundException e) {
+            throw new LifecycleException(e);
+        } catch (InstantiationException e) {
+            throw new LifecycleException(e);
+        } catch (IllegalAccessException e) {
+            throw new LifecycleException(e);
+        }
+        Properties info = new Properties();
+        info.setProperty("autoReconnect", "true");
+        try {
+            conn = DriverManager.getConnection(connectionURL, info);
+            if (pattern.equals("common")) {
+                ps = conn.prepareStatement
+                    ("INSERT INTO " + tableName + " (" 
+                     + remoteHostField + ", " + userField + ", "
+                     + timestampField +", " + queryField + ", "
+                     + statusField + ", " + bytesField 
+                     + ") VALUES(?, ?, ?, ?, ?, ?)");
+            } else if (pattern.equals("combined")) {
+                ps = conn.prepareStatement
+                    ("INSERT INTO " + tableName + " (" 
+                     + remoteHostField + ", " + userField + ", "
+                     + timestampField + ", " + queryField + ", " 
+                     + statusField + ", " + bytesField + ", " 
+                     + virtualHostField + ", " + methodField + ", "
+                     + refererField + ", " + userAgentField
+                     + ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+            }
+        } catch (SQLException e) {
+            throw new LifecycleException(e);
+        }
+
+    }
+
+
+    /**
+     * Invoked by tomcat on shutdown. The database connection is closed here.
+     * 
+     * @exception LifecycleException Can be thrown on lifecycle 
+     * inconsistencies or on database errors (as a wrapped SQLException).
+     */
+    public void stop() throws LifecycleException {
+
+        // START CR 6411114
+        if (!started)       // Ignore stop if not started
+            return;
+        // END CR 6411114
+
+        try {
+            if (ps != null)
+                ps.close();
+            if (conn != null)
+                conn.close();
+    	} catch (SQLException e) {
+            throw new LifecycleException(e);	
+        }
+        // START CR 6411114
+        super.stop();
+        // END CR 6411114
+
+    }
+
+
+    public long getCurrentTimeMillis() {
+        long systime  =  System.currentTimeMillis();
+        if ((systime - currentTimeMillis) > 1000) {
+            currentTimeMillis  =  new java.util.Date(systime).getTime();
+        }
+        return currentTimeMillis;
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/PersistentValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/PersistentValve.java
new file mode 100644
index 0000000..20c90d2
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/PersistentValve.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+import org.apache.catalina.*;
+import org.apache.catalina.session.PersistentManager;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+
+/**
+ * Valve that implements the default basic behavior for the
+ * <code>StandardHost</code> container implementation.
+ * <p>
+ * <b>USAGE CONSTRAINT</b>: To work correctly it requires a  PersistentManager.
+ *
+ * @author Jean-Frederic Clere
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:24 $
+ */
+
+public class PersistentValve
+    extends ValveBase {
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.PersistentValve/1.0";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Select the appropriate child Context to process this request,
+     * based on the specified request URI.  If no matching Context can
+     * be found, return an appropriate HTTP error.
+     *
+     * @param request Request to be processed
+     * @param response Response to be produced
+     *
+     * @exception IOException if an input/output error occurred
+     * @exception ServletException if a servlet error occurred
+     */
+     public int invoke(Request request, Response response)
+         throws IOException, ServletException {
+
+        // Select the Context to be used for this Request
+        Context context = request.getContext();
+        if (context == null) {
+            ((HttpServletResponse) response.getResponse()).sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 rb.getString(LogFacade.NO_CONTEXT_CONFIGURED));
+            return END_PIPELINE;
+        }
+
+        // Bind the context CL to the current thread
+        Thread.currentThread().setContextClassLoader
+            (context.getLoader().getClassLoader());
+
+        // Update the session last access time for our session (if any)
+        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+        String sessionId = hreq.getRequestedSessionId();
+        Manager manager = context.getManager();
+        if (sessionId != null && manager != null) {
+            if (manager instanceof PersistentManager) {
+                Store store = ((PersistentManager) manager).getStore();
+                if (store != null) {
+                    Session session = null;
+                    try {
+                        session = store.load(sessionId);
+                    } catch (Exception e) {
+                        log("deserializeError");
+                    }
+                    if (session != null) {
+                        if (!session.isValid() ||
+                            isSessionStale(session, System.currentTimeMillis())) {
+                            log("session swapped in is invalid or expired");
+                            session.expire();
+                            store.remove(sessionId);
+                        } else {
+                            session.setManager(manager);
+                            // session.setId(sessionId); Only if new ???
+                            manager.add(session);
+                            // ((StandardSession)session).activate();
+                            session.access();
+                        }
+                    }
+                }
+            }
+        }
+        log("sessionId: " + sessionId);
+
+        // Ask the next valve to process the request.
+        return INVOKE_NEXT;
+    }
+
+
+    public void postInvoke(Request request, Response response)
+                                    throws IOException, ServletException{
+        Context context = request.getContext();
+        if (context == null) {
+            ((HttpServletResponse) response.getResponse()).sendError
+                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                 rb.getString(LogFacade.NO_CONTEXT_CONFIGURED));
+            return;
+        }
+
+        // Bind the context CL to the current thread
+        Thread.currentThread().setContextClassLoader
+            (context.getLoader().getClassLoader());
+
+        // Update the session last access time for our session (if any)
+        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+        Manager manager = context.getManager();
+
+        // Read the sessionid after the response.
+        // HttpSession hsess = hreq.getSession(false);
+        String newsessionId = null;
+        if (request instanceof org.apache.catalina.connector.Request) {
+            Session sess;
+            try {
+                sess = ((org.apache.catalina.connector.Request) request).getSessionInternal();
+            } catch (Exception ex) {
+                sess = null;
+            }
+            if (sess!=null) {
+                newsessionId = sess.getIdInternal();
+            }
+        } else {
+            HttpSession hsess;
+            try {
+                hsess = hreq.getSession();
+            } catch (Exception ex) {
+                hsess = null;
+            }
+            if (hsess!=null) {
+                newsessionId = hsess.getId();
+            }
+        }
+
+        log("newsessionId: " + newsessionId);
+        if (newsessionId!=null) {
+            /* store the session in the store and remove it from the manager */
+            if (manager instanceof PersistentManager) {
+                Session session = manager.findSession(newsessionId);
+                Store store = ((PersistentManager) manager).getStore();
+                if (store != null && session!=null &&
+                    session.isValid() &&
+                    !isSessionStale(session, System.currentTimeMillis())) {
+                    // ((StandardSession)session).passivate();
+                    store.save(session);
+                    ((PersistentManager) manager).removeSuper(session);
+                    session.recycle();
+                } else if (session!=null) {
+                    log("newsessionId store: " + store + " session: " +
+                        session + " valid: " + session.isValid() +
+                        " Staled: " +
+                        isSessionStale(session, System.currentTimeMillis()));
+
+                }
+            } else {
+                log("newsessionId Manager: " + manager);
+            }
+        }
+
+    }
+
+    /**
+     * 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 = container.getLogger();
+        if (logger != null)
+            logger.log(this.toString() + ": " + message);
+        else
+            System.out.println(this.toString() + ": " + message);
+ 
+    }
+
+    /**
+     * Indicate whether the session has been idle for longer
+     * than its expiration date as of the supplied time.
+     *
+     * FIXME: Probably belongs in the Session class.
+     */
+    protected boolean isSessionStale(Session session, long timeNow) {
+ 
+        int maxInactiveInterval = session.getMaxInactiveInterval();
+        if (maxInactiveInterval >= 0) {
+            int timeIdle = // Truncate, do not round up
+                (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+            if (timeIdle >= maxInactiveInterval)
+                return true;
+        }
+ 
+        return false;
+ 
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RemoteAddrValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RemoteAddrValve.java
new file mode 100644
index 0000000..82db9dd
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RemoteAddrValve.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+
+/**
+ * Concrete implementation of <code>RequestFilterValve</code> that filters
+ * based on the string representation of the remote client's IP address.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:28:24 $
+ */
+
+public final class RemoteAddrValve
+    extends RequestFilterValve {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.RemoteAddrValve/1.0";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Extract the desired request property, and pass it (along with the
+     * specified request and response objects) to the protected
+     * <code>process()</code> method to perform the actual filtering.
+     * This method must be implemented by a concrete subclass.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public int invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        return process(request.getRequest().getRemoteAddr(),
+                request, response);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RemoteHostValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RemoteHostValve.java
new file mode 100644
index 0000000..1300ed8
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RemoteHostValve.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+
+/**
+ * Concrete implementation of <code>RequestFilterValve</code> that filters
+ * based on the remote client's host name.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:28:24 $
+ */
+
+public final class RemoteHostValve
+    extends RequestFilterValve {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.RemoteHostValve/1.0";
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Extract the desired request property, and pass it (along with the
+     * specified request and response objects) to the protected
+     * <code>process()</code> method to perform the actual filtering.
+     * This method must be implemented by a concrete subclass.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public int invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        return process(request.getRequest().getRemoteHost(),
+                request, response);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RequestDumperValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RequestDumperValve.java
new file mode 100644
index 0000000..1313e93
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RequestDumperValve.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+import org.apache.catalina.*;
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.logging.Level;
+
+/**
+ * <p>Implementation of a Valve that logs interesting contents from the
+ * specified Request (before processing) and the corresponding Response
+ * (after processing).  It is especially useful in debugging problems
+ * related to headers and cookies.</p>
+ *
+ * <p>This Valve may be attached to any Container, depending on the granularity
+ * of the logging you wish to perform.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.3 $ $Date: 2005/12/08 01:28:25 $
+ */
+
+public class RequestDumperValve extends ValveBase {
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.RequestDumperValve/1.0";
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Log the interesting request parameters, invoke the next Valve in the
+     * sequence, and log the interesting response parameters.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+     public int invoke(Request request, Response response)
+         throws IOException, ServletException {
+
+        // Skip logging for non-HTTP requests and responses
+        if (!(request instanceof HttpRequest) ||
+            !(response instanceof HttpResponse)) {
+             return INVOKE_NEXT;
+        }
+
+        HttpRequest hrequest = (HttpRequest) request;
+        HttpServletRequest hreq =
+            (HttpServletRequest) hrequest.getRequest();
+
+        // Log pre-service information
+        log("REQUEST URI       =" + hreq.getRequestURI());
+        log("          authType=" + hreq.getAuthType());
+        log(" characterEncoding=" + hreq.getCharacterEncoding());
+        log("     contentLength=" + hreq.getContentLength());
+        log("       contentType=" + hreq.getContentType());
+        log("       contextPath=" + hreq.getContextPath());
+        Cookie cookies[] = hreq.getCookies();
+        if (cookies != null) {
+            for (int i = 0; i < cookies.length; i++)
+                log("            cookie=" + cookies[i].getName() + "=" +
+                    cookies[i].getValue());
+        }
+        Enumeration<String> hnames = hreq.getHeaderNames();
+        while (hnames.hasMoreElements()) {
+            String hname = hnames.nextElement();
+            Enumeration<String> hvalues = hreq.getHeaders(hname);
+            while (hvalues.hasMoreElements()) {
+                String hvalue = hvalues.nextElement();
+                log("            header=" + hname + "=" + hvalue);
+            }
+        }
+        log("            locale=" + hreq.getLocale());
+        log("            method=" + hreq.getMethod());
+        Enumeration<String> pnames = hreq.getParameterNames();
+        while (pnames.hasMoreElements()) {
+            String pname = pnames.nextElement();
+            String pvalues[] = hreq.getParameterValues(pname);
+            StringBuilder result = new StringBuilder(pname);
+            result.append('=');
+            for (int i = 0; i < pvalues.length; i++) {
+                if (i > 0)
+                    result.append(", ");
+                result.append(pvalues[i]);
+            }
+            log("         parameter=" + result.toString());
+        }
+        log("          pathInfo=" + hreq.getPathInfo());
+        log("          protocol=" + hreq.getProtocol());
+        log("       queryString=" + hreq.getQueryString());
+        log("        remoteAddr=" + hreq.getRemoteAddr());
+        log("        remoteHost=" + hreq.getRemoteHost());
+        log("        remoteUser=" + hreq.getRemoteUser());
+        log("requestedSessionId=" + hreq.getRequestedSessionId());
+        log("            scheme=" + hreq.getScheme());
+        log("        serverName=" + hreq.getServerName());
+        log("        serverPort=" + hreq.getServerPort());
+        log("       servletPath=" + hreq.getServletPath());
+        log("          isSecure=" + hreq.isSecure());
+        log("---------------------------------------------------------------");
+
+        // Perform the request
+        return INVOKE_NEXT;
+    }
+
+     /**
+      * Log the interesting response parameters.
+      */
+     public void postInvoke(Request request, Response response)
+         throws IOException, ServletException {
+
+        HttpRequest hrequest = (HttpRequest) request;
+        HttpResponse hresponse = (HttpResponse) response;
+        HttpServletRequest hreq =
+            (HttpServletRequest) hrequest.getRequest();
+        HttpServletResponse hres =
+            (HttpServletResponse) hresponse.getResponse();
+
+        // Log post-service information
+        log("---------------------------------------------------------------");
+        log("          authType=" + hreq.getAuthType());
+        log("     contentLength=" + hresponse.getContentLength());
+        log("       contentType=" + hresponse.getContentType());
+        Cookie[] rcookies = hreq.getCookies();
+        for (int i = 0; i < rcookies.length; i++) {
+            log("            cookie=" + rcookies[i].getName() + "=" +
+                rcookies[i].getValue() + "; domain=" +
+                rcookies[i].getDomain() + "; path=" + rcookies[i].getPath());
+        }
+        for (String rhname : hres.getHeaderNames()) {
+            for (String rhvalue : hres.getHeaders(rhname)) {
+                log("            header=" + rhname + "=" + rhvalue);
+            }
+        }
+        log("           message=" + hresponse.getMessage());
+        log("        remoteUser=" + hreq.getRemoteUser());
+        log("            status=" + hres.getStatus());
+        log("===============================================================");
+
+    }
+
+
+    /**
+     * Return a String rendering of this object.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("RequestDumperValve[");
+        if (container != null)
+            sb.append(container.getName());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+        message = neutralizeForLog(message);
+        Logger logger = container.getLogger();
+        if (logger != null) {
+            logger.log(this.toString() + ": " + message);
+        } else {
+            log.log(Level.INFO, this.toString() + ": " + message);
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @param t Associated exception
+     */
+    protected void log(String message, Throwable t) {
+        message = neutralizeForLog(message);
+        Logger logger = container.getLogger();
+        if (logger != null) {
+            logger.log(this.toString() + ": " + message, t,
+                Logger.WARNING);
+        } else {
+            log.log(Level.WARNING, this.toString() + ": " + message, t);
+        }
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RequestFilterValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RequestFilterValve.java
new file mode 100644
index 0000000..35ab3c5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/RequestFilterValve.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+
+import org.apache.catalina.LogFacade;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.core.ApplicationDispatcher;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.deploy.ErrorPage;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+
+/**
+ * Implementation of a Valve that performs filtering based on comparing the
+ * appropriate request property (selected based on which subclass you choose
+ * to configure into your Container's pipeline) against a set of regular
+ * expressions configured for this Valve.
+ * <p>
+ * This valve is configured by setting the <code>allow</code> and/or
+ * <code>deny</code> properties to a comma-delimited list of regular
+ * expressions (in the syntax supported by the jakarta-regexp library) to
+ * which the appropriate request property will be compared.  Evaluation
+ * proceeds as follows:
+ * <ul>
+ * <li>The subclass extracts the request property to be filtered, and
+ *     calls the common <code>process()</code> method.
+ * <li>If there are any deny expressions configured, the property will
+ *     be compared to each such expression.  If a match is found, this
+ *     request will be rejected with a "Forbidden" HTTP response.</li>
+ * <li>If there are any allow expressions configured, the property will
+ *     be compared to each such expression.  If a match is found, this
+ *     request will be allowed to pass through to the next Valve in the
+ *     current pipeline.</li>
+ * <li>If one or more deny expressions was specified but no allow expressions,
+ *     allow this request to pass through (because none of the deny
+ *     expressions matched it).
+ * <li>The request will be rejected with a "Forbidden" HTTP response.</li>
+ * </ul>
+ * <p>
+ * This Valve may be attached to any Container, depending on the granularity
+ * of the filtering you wish to perform.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.5 $ $Date: 2006/03/07 22:30:07 $
+ */
+
+public abstract class RequestFilterValve
+    extends ValveBase {
+
+    // ----------------------------------------------------- Class Variables
+
+
+    /**
+     * The descriptive information related to this implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.valves.RequestFilterValve/1.0";
+
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The comma-delimited set of <code>allow</code> expressions.
+     */
+    protected String allow = null;
+
+
+    /**
+     * The set of <code>allow</code> regular expressions we will evaluate.
+     */
+    protected Pattern allows[] = new Pattern[0];
+
+
+    /**
+     * The set of <code>deny</code> regular expressions we will evaluate.
+     */
+    protected Pattern denies[] = new Pattern[0];
+
+
+    /**
+     * The comma-delimited set of <code>deny</code> expressions.
+     */
+    protected String deny = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return a comma-delimited set of the <code>allow</code> expressions
+     * configured for this Valve, if any; otherwise, return <code>null</code>.
+     */
+    public String getAllow() {
+
+        return (this.allow);
+
+    }
+
+
+    /**
+     * Set the comma-delimited set of the <code>allow</code> expressions
+     * configured for this Valve, if any.
+     *
+     * @param allow The new set of allow expressions
+     */
+    public void setAllow(String allow) {
+
+        this.allow = allow;
+        allows = precalculate(allow);
+
+    }
+
+
+    /**
+     * Return a comma-delimited set of the <code>deny</code> expressions
+     * configured for this Valve, if any; otherwise, return <code>null</code>.
+     */
+    public String getDeny() {
+
+        return (this.deny);
+
+    }
+
+
+    /**
+     * Set the comma-delimited set of the <code>deny</code> expressions
+     * configured for this Valve, if any.
+     *
+     * @param deny The new set of deny expressions
+     */
+    public void setDeny(String deny) {
+
+        this.deny = deny;
+        denies = precalculate(deny);
+
+    }
+
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Extract the desired request property, and pass it (along with the
+     * specified request and response objects) to the protected
+     * <code>process()</code> method to perform the actual filtering.
+     * This method must be implemented by a concrete subclass.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public abstract int invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return an array of regular expression objects initialized from the
+     * specified argument, which must be <code>null</code> or a comma-delimited
+     * list of regular expression patterns.
+     *
+     * @param list The comma-separated list of patterns
+     *
+     * @exception IllegalArgumentException if one of the patterns has
+     *  invalid syntax
+     */
+    protected Pattern[] precalculate(String list) {
+
+        if (list == null)
+            return (new Pattern[0]);
+        list = list.trim();
+        if (list.length() < 1)
+            return (new Pattern[0]);
+        list += ",";
+
+        ArrayList<Pattern> reList = new ArrayList<Pattern>();
+        while (list.length() > 0) {
+            int comma = list.indexOf(',');
+            if (comma < 0)
+                break;
+            String pattern = list.substring(0, comma).trim();
+            try {
+                reList.add(Pattern.compile(pattern));
+            } catch (PatternSyntaxException e) {
+                String msg = MessageFormat.format(rb.getString(LogFacade.SYNTAX_ERROR),
+                                                  pattern);
+                IllegalArgumentException iae = new IllegalArgumentException
+                    (msg);
+                iae.initCause(e);
+                throw iae;
+            }
+            list = list.substring(comma + 1);
+        }
+
+        Pattern reArray[] = new Pattern[reList.size()];
+        return reList.toArray(reArray);
+
+    }
+
+
+    /**
+     * Perform the filtering that has been configured for this Valve, matching
+     * against the specified request property.
+     *
+     * @param property The request property on which to filter
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be processed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    protected int process(String property, Request request, Response response)
+        throws IOException, ServletException {
+
+        // Check the deny patterns, if any
+        for (int i = 0; i < denies.length; i++) {
+            if (denies[i].matcher(property).matches()) {
+                //ServletResponse sres = response.getResponse();
+                /* GlassFish 6386229 
+                if (sres instanceof HttpServletResponse) {
+                    HttpServletResponse hres = (HttpServletResponse) sres;
+                    hres.sendError(HttpServletResponse.SC_FORBIDDEN);
+                    return END_PIPELINE;
+                }
+                */
+                // START GlassFish 6386229 
+                //HttpServletResponse hres = (HttpServletResponse) sres;
+                //hres.sendError(HttpServletResponse.SC_FORBIDDEN);
+                handleError(request, response, HttpServletResponse.SC_FORBIDDEN);
+                // END GlassFish 6386229                 
+                return END_PIPELINE;
+                // GlassFish 638622                   
+            }
+        }
+
+        // Check the allow patterns, if any
+        for (int i = 0; i < allows.length; i++) {
+            if (allows[i].matcher(property).matches()) {
+                return INVOKE_NEXT;
+            }
+        }
+
+        // Allow if denies specified but not allows
+        if ((denies.length > 0) && (allows.length == 0)) {
+            return INVOKE_NEXT;
+        }
+
+        // Deny this request
+        //ServletResponse sres = response.getResponse();
+        /* GlassFish 6386229 
+        if (sres instanceof HttpServletResponse) {
+            HttpServletResponse hres = (HttpServletResponse) sres;
+            hres.sendError(HttpServletResponse.SC_FORBIDDEN);
+            return END_PIPELINE;
+        }
+        */
+        // START GlassFish 6386229 
+        //HttpServletResponse hres = (HttpServletResponse) sres;
+        //hres.sendError(HttpServletResponse.SC_FORBIDDEN);
+        handleError(request, response, HttpServletResponse.SC_FORBIDDEN);
+        // END GlassFish 6386229        
+        return END_PIPELINE;
+    }
+
+    private void handleError(Request request, Response response, int statusCode)
+            throws IOException {
+
+        ServletRequest sreq = request.getRequest();
+        ServletResponse sres = response.getResponse();
+        HttpServletResponse hres = (HttpServletResponse)sres;
+
+
+        ErrorPage errorPage = null;
+        if (getContainer() instanceof StandardHost) {
+            errorPage = ((StandardHost)getContainer()).findErrorPage(statusCode);
+        } else if (getContainer() instanceof StandardContext){
+            errorPage = ((StandardContext)getContainer()).findErrorPage(statusCode);
+        }
+        if (errorPage != null) {
+            try {
+                hres.setStatus(statusCode);   
+                ServletContext servletContext =
+                    request.getContext().getServletContext();
+                ApplicationDispatcher dispatcher = (ApplicationDispatcher)
+                    servletContext.getRequestDispatcher(errorPage.getLocation());
+
+                if (hres.isCommitted()) {
+                    // Response is committed - including the error page is the
+                    // best we can do 
+                    dispatcher.include(sreq, sres);
+                } else {
+                    // Reset the response (keeping the real error code and message)
+                    response.resetBuffer(true);
+
+                    dispatcher.dispatch(sreq, sres, DispatcherType.ERROR);
+
+                    // If we forward, the response is suspended again
+                    response.setSuspended(false);
+                }
+                sres.flushBuffer();
+            } catch(Throwable t) {
+                if (log.isLoggable(Level.INFO)) {
+                    String msg = MessageFormat.format(rb.getString(LogFacade.CANNOT_PROCESS_ERROR_PAGE_INFO),
+                                                      errorPage.getLocation());
+                    log.log(Level.INFO, msg, t);
+                }
+            }
+        } else {
+            hres.sendError(statusCode);
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ValveBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ValveBase.java
new file mode 100644
index 0000000..4c8021e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/ValveBase.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+import org.apache.catalina.*;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.glassfish.web.valve.GlassFishValve;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Convenience base class for implementations of the <b>Valve</b> interface.
+ * A subclass <strong>MUST</strong> implement an <code>invoke()</code>
+ * method to provide the required functionality, and <strong>MAY</strong>
+ * implement the <code>Lifecycle</code> interface to provide configuration
+ * management and lifecycle support.
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1.7 $ $Date: 2007/05/05 05:32:41 $
+ */
+
+public abstract class ValveBase
+/** CR 6411114
+    implements Contained, Valve {
+*/
+// START CR 6411114
+    implements Contained, Lifecycle, Valve, GlassFishValve {
+// END CR 6411114
+    protected static final Logger log = LogFacade.getLogger();
+    protected static final ResourceBundle rb = log.getResourceBundle();
+
+    //------------------------------------------------------ Instance Variables
+
+
+    /**
+     * The Container whose pipeline this Valve is a component of.
+     */
+    protected Container container = null;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    protected int debug = 0;
+
+
+    // START CR 6411114
+    /**
+     * Has this component been started yet?
+     */
+    protected boolean started = false;
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    // END CR 6411114
+    /**
+     * Descriptive information about this Valve implementation.  This value
+     * should be overridden by subclasses.
+     */
+    protected static final String info =
+        "org.apache.catalina.core.ValveBase/1.0";
+
+
+    /**
+     * The next Valve in the pipeline this Valve is a component of.
+     */
+    protected Valve next = null;
+
+
+
+    //-------------------------------------------------------------- Properties
+
+    /**
+     * Return the Container with which this Valve is associated, if any.
+     */
+    public Container getContainer() {
+
+        return (container);
+    }
+
+
+    /**
+     * Set the Container with which this Valve is associated, if any.
+     *
+     * @param container The new associated container
+     */
+    public void setContainer(Container container) {
+
+        this.container = container;
+    }
+
+
+   /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (this.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 descriptive information about this Valve implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+    }
+
+
+    /**
+     * Return the next Valve in this pipeline, or <code>null</code> if this
+     * is the last Valve in the pipeline.
+     */
+    public Valve getNext() {
+
+        return next;
+    }
+
+
+    /**
+     * Set the Valve that follows this one in the pipeline it is part of.
+     *
+     * @param valve The new next valve
+     */
+    public void setNext(Valve valve) {
+
+        this.next = valve;
+    }
+
+
+    //---------------------------------------------------------- Public Methods
+
+    /**
+     * Execute a periodic task, such as reloading, etc. This method will be
+     * invoked inside the classloading context of this container. Unexpected
+     * throwables will be caught and logged.
+     */
+    public void backgroundProcess() {
+        // Deliberate no-op
+    }
+
+
+    /**
+     * The implementation-specific logic represented by this Valve.  See the
+     * Valve description for the normal design patterns for this method.
+     * <p>
+     * This method <strong>MUST</strong> be provided by a subclass.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public abstract int invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+    /**
+     * A post-request processing implementation that does nothing.
+     *
+     * Very few Valves override this behaviour as most Valve logic
+     * is used for request processing.
+     */
+    public void postInvoke(Request request, Response response)
+            throws IOException, ServletException {
+        // Deliberate no-op
+    }
+
+
+    /**
+     * Tomcat-style invocation.
+     */
+    public void invoke(org.apache.catalina.connector.Request request,
+                       org.apache.catalina.connector.Response response)
+            throws IOException, ServletException {
+        // Deliberate no-op
+    }
+
+
+    /**
+     * Process a Comet event. This method will rarely need to be provided by
+     * a subclass, unless it needs to reassociate a particular object with
+     * the thread that is processing the request.
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     * @exception ServletException if a servlet error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     */
+    public void event(org.apache.catalina.connector.Request request,
+                      org.apache.catalina.connector.Response response,
+                      CometEvent event)
+            throws IOException, ServletException {
+        // Perform the request
+        if (getNext() != null) {
+            getNext().event(request, response, event);
+        }
+    }
+    
+        
+    /**    
+     * @return true if this access log valve has been started, false
+     * otherwise.
+     */
+    public boolean isStarted() {
+        return started;
+    }
+
+
+    // START CR 6411114
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+    }
+
+
+    /**
+     * Gets the (possibly empty) list of lifecycle listeners associated
+     * with this Valve.
+     */
+    public List<LifecycleListener> findLifecycleListeners() {
+        return lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+
+    /**
+     * Prepare for the beginning of active use of the public methods of this
+     * component.  This method should be called after <code>configure()</code>,
+     * and before any of the public methods of the component are utilized.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that prevents this component from being used
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (started)
+            return;
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+    }
+
+
+    /**
+     * Gracefully terminate the active use of the public methods of this
+     * component.  This method should be the last one called on a given
+     * instance of this component.
+     *
+     * @exception LifecycleException if this component detects a fatal error
+     *  that needs to be reported
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current component state
+        if (!started)
+            return;
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+    }
+
+
+
+
+    // END CR 6411114
+    // -------------------- JMX and Registration  --------------------
+    protected String domain;
+    protected ObjectName oname;
+    protected ObjectName controller;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public void setObjectName(ObjectName oname) {
+        this.oname = oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName getController() {
+        return controller;
+    }
+
+    public void setController(ObjectName controller) {
+        this.controller = controller;
+    }
+
+    /** From the name, extract the parent object name
+     *
+     * @param valveName
+     * @return parentName
+     */
+    public ObjectName getParentName( ObjectName valveName ) {
+        return null;
+    }
+
+    public ObjectName createObjectName(String domain, ObjectName parent)
+            throws MalformedObjectNameException
+    {
+        Container container=this.getContainer();
+        if( container == null || ! (container instanceof ContainerBase) )
+            return null;
+        ContainerBase containerBase=(ContainerBase)container;
+        Pipeline pipe=containerBase.getPipeline();
+        GlassFishValve valves[]=pipe.getValves();
+
+        /* Compute the "parent name" part */
+        String parentName="";
+        if (container instanceof Engine) {
+        } else if (container instanceof Host) {
+            parentName=",host=" +container.getName();
+        } else if (container instanceof Context) {
+            String path = ((Context)container).getPath();
+            if (path.length() < 1) {
+                path = "/";
+            }
+            Host host = (Host) container.getParent();
+            parentName=",path=" + path + ",host=" + host.getName();
+        } else if (container instanceof Wrapper) {
+            Context ctx = (Context) container.getParent();
+            String path = ctx.getPath();
+            if (path.length() < 1) {
+                path = "/";
+            }
+            Host host = (Host) ctx.getParent();
+            parentName=",servlet=" + container.getName() +
+                    ",path=" + path + ",host=" + host.getName();
+        }
+        
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "valve parent=" + parentName + " " + parent);
+        }
+
+        String className=this.getClass().getName();
+        int period = className.lastIndexOf('.');
+        if (period >= 0)
+            className = className.substring(period + 1);
+
+        int seq=0;
+        for( int i=0; i<valves.length; i++ ) {
+            // Find other valves with the same name
+            if (valves[i]==this) {
+                break;
+            }
+            if( valves[i]!=null &&
+                    valves[i].getClass() == this.getClass()) {
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, "Duplicate " + valves[i] + " " + this + " " +
+                            container);
+                }
+                seq++;
+            }
+        }
+        String ext="";
+        if( seq > 0 ) {
+            ext=",seq=" + seq;
+        }
+
+        ObjectName objectName = 
+            new ObjectName( domain + ":type=Valve,name=" + className + ext + parentName);
+
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "valve objectname = "+objectName);
+        }
+
+        return objectName;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/WebdavFixValve.java b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/WebdavFixValve.java
new file mode 100644
index 0000000..3716765
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/WebdavFixValve.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.valves;
+
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Valve that attempts to force MS WebDAV clients connecting on port 80 to use
+ * a WebDAV client that actually works. Other workarounds that might help
+ * include:
+ * <ul>
+ *   <li>Specifing the port, even if it is port 80, when trying to connect.</li>
+ *   <li>Canceling the first authentication dialog box and then trying to
+ *       reconnect.</li>
+ * </ul>
+ * To use this valve add the following <code>&lt;Valve
+ * className="org.apache.catalina.valves.WebdavFixValve" /&gt;</code>
+ * to your <code>Engine</code>, <code>Host</code> or <code>Context</code> as
+ * required. Normally, this valve would be used at the <code>Context</code>
+ * level.
+ *
+ * @version $Revision: 420067 $, $Date: 2006-07-08 09:16:58 +0200 (sub, 08 srp 2006) $
+ */
+
+public class WebdavFixValve
+    extends ValveBase {
+
+    /**
+     * Check for the broken MS WebDAV client and if detected issue a re-direct
+     * that hopefully will cause the non-broken client to be used.
+     */
+    public int invoke(Request request, Response response)
+        throws IOException, ServletException {
+
+        HttpServletRequest hreq = (HttpServletRequest)request.getRequest();
+        HttpServletResponse hres = (HttpServletResponse)response.getResponse();
+        String ua = hreq.getHeader("User-Agent");
+        if (ua != null && ua.contains("MiniRedir")) {
+            hres.sendRedirect(buildRedirect(hreq));
+            return END_PIPELINE;
+        } else {
+            return INVOKE_NEXT;
+        }
+    }
+
+    private String buildRedirect(HttpServletRequest request) {
+        StringBuilder location =
+            new StringBuilder(request.getRequestURL().length());
+        location.append(request.getScheme());
+        location.append("://");
+        location.append(request.getServerName());
+        location.append(':');
+        // If we include the port, even if it is 80, then MS clients will use
+        // a WebDAV client that works rather than the MiniRedir that has
+        // problems with BASIC authentication
+        location.append(request.getServerPort());
+        location.append(request.getRequestURI());
+        return location.toString();
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/valves/package.html b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/package.html
new file mode 100644
index 0000000..c9a56a9
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/catalina/valves/package.html
@@ -0,0 +1,31 @@
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<body>
+
+<p>This package contains a variety of small Valve implementations that do
+not warrant being packaged separately.  In addition, there is a convenience
+base class (<code>ValveBase</code>) that supports the usual mechanisms for
+including custom Valves into the corresponding Pipeline.</p>
+
+<p>Other packages that include Valves include
+<code>org.glassfish.grizzly.logger</code> and
+<code>org.glassfish.grizzly.security</code>.</p>
+
+</body>
diff --git a/appserver/web/web-core/src/main/java/org/apache/coyote/http11/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/coyote/http11/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/coyote/http11/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/buf/res/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/buf/res/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/buf/res/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/collections/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/collections/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/collections/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/compat/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/compat/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/compat/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java
new file mode 100644
index 0000000..5825d2b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import 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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/AbstractRulesImpl.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/AbstractRulesImpl.java
new file mode 100644
index 0000000..613fb61
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/AbstractRulesImpl.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import java.util.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 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<Rule> 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<Rule> rules();
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ArrayStack.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ArrayStack.java
new file mode 100644
index 0000000..237d313
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ArrayStack.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import java.util.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.tomcat.util.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<E> extends ArrayList<E> {
+
+    /** 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 E 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 E 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 E 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 E push(E 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(E 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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/CallMethodRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/CallMethodRule.java
new file mode 100644
index 0000000..f4a35b5
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/CallMethodRule.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import org.glassfish.web.util.IntrospectionUtils;
+import org.xml.sax.Attributes;
+
+import java.util.logging.Level;
+
+
+/**
+ * <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.
+     */
+    protected 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.
+     */
+    protected 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().log(Level.SEVERE, "(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.isLoggable(Level.FINEST)) {
+                for (int i=0,size=parameters.length;i<size;i++) {
+                    digester.log.finest("[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;
+        }
+
+        // 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[] = null;
+        if (paramTypes != null) {
+            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) {
+            StringBuilder sb = new StringBuilder();
+            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.isLoggable(Level.FINE)) {
+            StringBuilder sb = new StringBuilder("[CallMethodRule]{");
+            sb.append(digester.match);
+            sb.append("} Call ");
+            sb.append(target.getClass().getName());
+            sb.append(".");
+            sb.append(methodName);
+            sb.append("(");
+            for (int i = 0; paramValues!=null && 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.fine(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() {
+
+        StringBuilder sb = new StringBuilder("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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/CallParamRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/CallParamRule.java
new file mode 100644
index 0000000..623e4b8
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/CallParamRule.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import org.xml.sax.Attributes;
+
+import java.util.logging.Level;
+
+
+/**
+ * <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<String> 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.isLoggable(Level.FINE)) {
+            
+                StringBuilder sb = new StringBuilder("[CallParamRule]{");
+                sb.append(digester.match);
+                sb.append("} Save from stack; from stack?").append(fromStack);
+                sb.append("; object=").append(param);
+                digester.log.fine(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<String>();
+            }
+            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() {
+
+        StringBuilder sb = new StringBuilder("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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Digester.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Digester.java
new file mode 100644
index 0000000..861dd91
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Digester.java
@@ -0,0 +1,2875 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// $Id: Digester.java 565464 2007-08-13 18:13:47Z remm $
+
+package org.apache.tomcat.util.digester;
+
+
+import static com.sun.logging.LogCleanerUtil.neutralizeForLog;
+import org.apache.catalina.LogFacade;
+import org.glassfish.web.util.IntrospectionUtils;
+import org.xml.sax.*;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+
+
+/**
+ * <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);
+        }
+    }
+
+    static final 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 StringBuilder bodyText = new StringBuilder();
+
+
+    /**
+     * The stack of body text string buffers for surrounding elements.
+     */
+    protected ArrayStack<StringBuilder> bodyTexts =
+        new ArrayStack<StringBuilder>();
+
+
+    /**
+     * 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<List<Rule>> matches = new ArrayStack<List<Rule>>(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<String, String> entityValidator =
+        new HashMap<String, String>();
+
+
+    /**
+     * 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<String, ArrayStack<String>> namespaces =
+        new HashMap<String, ArrayStack<String>>();
+
+
+    /**
+     * The parameters stack being utilized by CallMethodRule and
+     * CallParamRule rules.
+     */
+    protected ArrayStack<Object> params = new ArrayStack<Object>();
+
+
+    /**
+     * 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<Object> stack = new ArrayStack<Object>();
+
+
+    /**
+     * 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;
+
+
+    
+    /**
+     * Warn on missing attributes and elements.
+     */
+    protected boolean rulesValidation = false;
+
+    
+    /**
+     * Fake attributes map (attributes are often used for object creation).
+     */
+    protected Map<Class<?>, List<String>> fakeAttributes = null;
+
+
+    /**
+     * The Log to which most logging calls will be made.
+     */
+    protected Logger log = LogFacade.getLogger();
+    protected final ResourceBundle rb = log.getResourceBundle();
+
+    /**
+     * The Log to which all SAX event related logging calls will be made.
+     */
+    protected Logger saxLog =
+        Logger.getLogger("org.apache.tomcat.util.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<String, ArrayStack<Object>> stacksByName =
+        new HashMap<String, ArrayStack<Object>>();
+    
+    // ------------------------------------------------------------- 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<String> stack = namespaces.get(prefix);
+        if (stack == null) {
+            return (null);
+        }
+        try {
+            return 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 Logger getLogger() {
+
+        return log;
+
+    }
+
+
+    /**
+     * Set the current logger for this Digester.
+     */
+    public void setLogger(Logger 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 Logger 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(Logger 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.log(Level.SEVERE, LogFacade.GET_PARRSER_EXCEPTION, 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.log(Level.SEVERE, LogFacade.CANNOT_GET_XML_READER_EXCEPTION, 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 rules validation flag.
+     */
+    public boolean getRulesValidation() {
+
+        return (this.rulesValidation);
+
+    }
+
+
+    /**
+     * Set the rules validation flag.  This must be called before
+     * <code>parse()</code> is called the first time.
+     *
+     * @param rulesValidation The new rules validation flag.
+     */
+    public void setRulesValidation(boolean rulesValidation) {
+
+        this.rulesValidation = rulesValidation;
+
+    }
+
+
+    /**
+     * Return the fake attributes list.
+     */
+    public Map<Class<?>, List<String>> getFakeAttributes() {
+
+        return (this.fakeAttributes);
+
+    }
+
+
+    /**
+     * Determine if an attribute is a fake attribute.
+     */
+    public boolean isFakeAttribute(Object object, String name) {
+
+        if (fakeAttributes == null) {
+            return false;
+        }
+        List<String> result = fakeAttributes.get(object.getClass());
+        if (result == null) {
+            result = fakeAttributes.get(Object.class);
+        }
+        if (result == null) {
+            return false;
+        } else {
+            return result.contains(name);
+        }
+
+    }
+
+
+    /**
+     * Set the fake attributes.
+     *
+     * @param fakeAttributes The new fake attributes.
+     */
+    public void setFakeAttributes(Map<Class<?>, List<String>> fakeAttributes) {
+
+        this.fakeAttributes = fakeAttributes;
+
+    }
+
+
+    /**
+     * 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 {
+        // Clear any cached parser and factory, see IT 9894
+        parser = null;
+        factory = null;
+
+        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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.isLoggable(Level.FINE)) {
+            if (getCount() > 1) {
+                saxLog.log(Level.FINE, "endDocument():  " + getCount() +
+                        " elements left");
+            } else {
+                saxLog.log(Level.FINE, "endDocument()");
+            }
+        }
+
+        while (getCount() > 1) {
+            pop();
+        }
+
+        // Fire "finish" events for all defined rules
+        Iterator<Rule> rules = getRules().rules().iterator();
+        while (rules.hasNext()) {
+            Rule rule = rules.next();
+            try {
+                rule.finish();
+            } catch (Exception e) {
+                log.log(Level.SEVERE, LogFacade.FINISH_EVENT_EXCEPTION, e);
+                throw createSAXException(e);
+            } catch (Error e) {
+                log.log(Level.SEVERE, LogFacade.FINISH_EVENT_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.isLoggable(Level.FINE);
+
+        if (debug) {
+            if (saxLog.isLoggable(Level.FINE)) {
+                saxLog.log(Level.FINE, "endElement(" + namespaceURI + "," + localName +
+                        "," + qName + ")");
+            }
+            log.log(Level.FINE, "  match='" + match + "'");
+            log.log(Level.FINE, "  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<Rule> rules = matches.pop();
+        if ((rules != null) && (rules.size() > 0)) {
+            String bodyText = this.bodyText.toString();
+            for (int i = 0; i < rules.size(); i++) {
+                try {
+                    Rule rule = rules.get(i);
+                    if (debug) {
+                        log.log(Level.FINE, "  Fire body() for " + rule);
+                    }
+                    rule.body(namespaceURI, name, bodyText);
+                } catch (Exception e) {
+                    log.log(Level.SEVERE, LogFacade.BODY_EVENT_EXCEPTION, e);
+                    throw createSAXException(e);
+                } catch (Error e) {
+                    log.log(Level.SEVERE, LogFacade.BODY_EVENT_ERROR, e);
+                    throw e;
+                }
+            }
+        } else {
+            if (debug) {
+                log.log(Level.FINE, "  No rules found matching '" + match + "'.");
+            }
+            if (rulesValidation) {
+                log.log(Level.WARNING, LogFacade.NO_RULES_FOUND_MATCHING_EXCEPTION, match);
+            }
+        }
+
+        // Recover the body text from the surrounding element
+        bodyText = bodyTexts.pop();
+        if (debug) {
+            log.log(Level.FINE, "  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 = rules.get(j);
+                    if (debug) {
+                        log.log(Level.FINE, neutralizeForLog("  Fire end() for " + rule));
+                    }
+                    rule.end(namespaceURI, name);
+                } catch (Exception e) {
+                    log.log(Level.SEVERE, LogFacade.END_EVENT_EXCEPTION, e);
+                    throw createSAXException(e);
+                } catch (Error e) {
+                    log.log(Level.SEVERE, LogFacade.END_EVENT_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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "endPrefixMapping(" + prefix + ")");
+        }
+
+        // Deregister this prefix mapping
+        ArrayStack<String> stack = 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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.isLoggable(Level.FINE);
+        
+        if (saxLog.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.log(Level.FINE, "  Pushing body text '" + bodyText.toString() + "'");
+        }
+        bodyText = new StringBuilder();
+
+        // 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
+        StringBuilder sb = new StringBuilder(match);
+        if (match.length() > 0) {
+            sb.append('/');
+        }
+        sb.append(name);
+        match = sb.toString();
+        if (debug) {
+            log.log(Level.FINE, "  New match='" + match + "'");
+        }
+
+        // Fire "begin" events for all relevant rules
+        List<Rule> 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 = rules.get(i);
+                    if (debug) {
+                        log.log(Level.FINE, "  Fire begin() for " + rule);
+                    }
+                    rule.begin(namespaceURI, name, list);
+                } catch (Exception e) {
+                    log.log(Level.SEVERE, LogFacade.BEGIN_EVENT_EXCEPTION, e);
+                    throw createSAXException(e);
+                } catch (Error e) {
+                    log.log(Level.SEVERE, LogFacade.BEGIN_EVENT_ERROR, e);
+                    throw e;
+                }
+            }
+        } else {
+            if (debug) {
+                log.log(Level.FINE, "  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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "startPrefixMapping(" + prefix + "," + namespaceURI + ")");
+        }
+
+        // Register this prefix mapping
+        ArrayStack<String> stack = namespaces.get(prefix);
+        if (stack == null) {
+            stack = new ArrayStack<String>();
+            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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "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.isLoggable(Level.FINE)) {
+            saxLog.log(Level.FINE, "resolveEntity('" + publicId + "', '" + systemId + "')");
+        }
+        
+        if (publicId != null)
+            this.publicId = publicId;
+                                       
+        // Has this system identifier been registered?
+        String entityURL = null;
+        if (publicId != null) {
+            entityURL = entityValidator.get(publicId);
+        }
+         
+        // Redirect the schema location to a local destination
+        if (schemaLocation != null && entityURL == null && systemId != null){
+            entityURL = entityValidator.get(systemId);
+        } 
+
+        if (entityURL == null) { 
+            if (systemId == null) {
+                // cannot resolve
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, " Cannot resolve entity: '" + publicId + "'");
+                }
+                return (null);
+                
+            } else {
+                // try to resolve using system ID
+                if (log.isLoggable(Level.FINE)) {
+                    log.log(Level.FINE, " Trying to resolve using system ID '" + systemId + "'");
+                }
+                entityURL = systemId;
+            }
+        }
+        
+        // Return an input source to our alternative URL
+        if (log.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, " 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 {
+
+        String msg = MessageFormat.format(rb.getString(LogFacade.PARSE_ERROR),
+                                          new Object[] {exception.getLineNumber(), exception.getColumnNumber(),
+                                                        exception.getMessage()});
+        log.log(Level.SEVERE, msg, 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 {
+
+        String msg = MessageFormat.format(rb.getString(LogFacade.PARSE_FATAL_ERROR),
+                new Object[] {exception.getLineNumber(), exception.getColumnNumber(),
+                        exception.getMessage()});
+
+        log.log(Level.SEVERE, msg, 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.log(Level.WARNING, LogFacade.PARSE_WARNING_ERROR,
+                     new Object[] {exception.getLineNumber(), exception.getColumnNumber(),
+                                   exception.getMessage()});
+
+            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.log(Level.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.log(Level.SEVERE, 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();
+        try (FileInputStream fis = new FileInputStream(file)) {
+          InputSource input = new InputSource(fis);
+          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.isLoggable(Level.FINE)) {
+            log.log(Level.FINE, "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.isLoggable(Level.FINE)) {
+            if (newNamespaceURI == null) {
+                log.log(Level.FINE, "addRuleSet() with no namespace URI");
+            } else {
+                log.log(Level.FINE, "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.log(Level.WARNING, LogFacade.EMPTY_STACK_EXCEPTION);
+            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.log(Level.WARNING, LogFacade.EMPTY_STACK_EXCEPTION);
+            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.log(Level.WARNING, LogFacade.EMPTY_STACK_EXCEPTION);
+            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<Object> namedStack = stacksByName.get(stackName);
+        if (namedStack == null) {
+            namedStack = new ArrayStack<Object>();
+            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<Object> namedStack = stacksByName.get(stackName);
+        if (namedStack == null) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "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<Object> namedStack = stacksByName.get(stackName);
+        if (namedStack == null ) {
+            if (log.isLoggable(Level.FINE)) {
+                log.log(Level.FINE, "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<Object> namedStack = 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 = Logger.getLogger("org.apache.tomcat.util.digester.Digester");
+        saxLog = Logger.getLogger("org.apache.tomcat.util.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<String, String> getRegistrations() {
+
+        return (entityValidator);
+
+    }
+
+
+    /**
+     * <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.log(Level.WARNING, LogFacade.EMPTY_STACK_EXCEPTION);
+            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.log(Level.WARNING, LogFacade.EMPTY_STACK_EXCEPTION);
+            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.isLoggable(Level.FINEST)) {
+                log.log(Level.FINEST, "Popping params");
+            }
+            return (params.pop());
+        } catch (EmptyStackException e) {
+            log.log(Level.WARNING, LogFacade.EMPTY_STACK_EXCEPTION);
+            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.isLoggable(Level.FINEST)) {
+            log.log(Level.FINEST, "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.log(Level.SEVERE, LogFacade.NO_LOCATOR_EXCEPTION);
+        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.equals(newValue)) {
+                    newAttrs.setValue(i, newValue);
+                }
+            }
+            catch (Exception e) {
+                // ignore - let the attribute have its original value
+            }
+        }
+
+        return newAttrs;
+
+    }
+
+
+    /**
+     * Return a new StringBuilder 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 StringBuilder updateBodyText(StringBuilder bodyText) {
+        String in = bodyText.toString();
+        String out;
+        try {
+            out = IntrospectionUtils.replaceProperties(in, null, source);
+        } catch(Exception e) {
+            return bodyText; // return unchanged data
+        }
+
+        if (out.equals(in)) {
+            // No substitutions required. Don't waste memory creating
+            // a new buffer
+            return bodyText;
+        } else {
+            return new StringBuilder(out);
+        }
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/FactoryCreateRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/FactoryCreateRule.java
new file mode 100644
index 0000000..e9f37ec
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/FactoryCreateRule.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import org.xml.sax.Attributes;
+
+import java.util.logging.Level;
+
+
+/**
+ * <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<Boolean> 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<Boolean>();
+            }
+            
+            try {
+                Object instance = getFactory(attributes).createObject(attributes);
+                
+                if (digester.log.isLoggable(Level.FINE)) {
+                    digester.log.log(Level.FINE, "[FactoryCreateRule]{" + digester.match +
+                            "} New " + instance.getClass().getName());
+                }
+                digester.push(instance);
+                exceptionIgnoredStack.push(Boolean.FALSE);
+                
+            } catch (Exception e) {
+                // log message and error
+                if (digester.log.isLoggable(Level.INFO)) {
+                    digester.log.log(Level.INFO, "[FactoryCreateRule] Create exception ignored: " +
+                        ((e.getMessage() == null) ? e.getClass().getName() : e.getMessage()));
+                    if (digester.log.isLoggable(Level.FINE)) {
+                        digester.log.log(Level.FINE, "[FactoryCreateRule] Ignored exception:" + e.getMessage());
+                    }
+                }
+                exceptionIgnoredStack.push(Boolean.TRUE);
+                
+            }
+            
+        } else {
+            Object instance = getFactory(attributes).createObject(attributes);
+            
+            if (digester.log.isLoggable(Level.FINE)) {
+                digester.log.log(Level.FINE, "[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 ((exceptionIgnoredStack.pop()).booleanValue()) {
+                // creation exception was ignored
+                // nothing was put onto the stack
+                if (digester.log.isLoggable(Level.FINEST)) {
+                    digester.log.log(Level.FINEST, "[FactoryCreateRule] No creation so no push so no pop");
+                }
+                return;
+            }
+        } 
+
+        Object top = digester.pop();
+        if (digester.log.isLoggable(Level.FINE)) {
+            digester.log.log(Level.FINE, "[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() {
+
+        StringBuilder sb = new StringBuilder("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.isLoggable(Level.FINE)) {
+                digester.log.log(Level.FINE, "[FactoryCreateRule]{" + digester.match +
+                        "} New factory " + realClassName);
+            }
+            Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
+            creationFactory = (ObjectCreationFactory)
+                    clazz.newInstance();
+            creationFactory.setDigester(digester);
+        }
+        return (creationFactory);
+
+    }    
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/GenericParser.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/GenericParser.java
new file mode 100644
index 0000000..016daa0
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/GenericParser.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * 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 final Logger log = 
+            Logger.getLogger("org.apache.tomcat.util.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 final 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){
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.INFO, parser.getClass().getName() + ": "  
+                                            + e.getMessage() + " not supported."); 
+            }
+        }
+        return parser;
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/NodeCreateRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/NodeCreateRule.java
new file mode 100644
index 0000000..84aa1f7
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/NodeCreateRule.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import org.w3c.dom.*;
+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;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+
+/**
+ * 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 {
+
+        digester.pop();
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectCreateRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
new file mode 100644
index 0000000..6990e68
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import org.xml.sax.Attributes;
+
+import java.util.logging.Level;
+
+
+/**
+ * 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.isLoggable(Level.FINE)) {
+            digester.log.log(Level.FINE, "[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.isLoggable(Level.FINE)) {
+            digester.log.log(Level.FINE, "[ObjectCreateRule]{" + digester.match +
+                    "} Pop " + top.getClass().getName());
+        }
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("ObjectCreateRule[");
+        sb.append("className=");
+        sb.append(className);
+        sb.append(", attributeName=");
+        sb.append(attributeName);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
new file mode 100644
index 0000000..586c2dd
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import 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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectParamRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectParamRule.java
new file mode 100644
index 0000000..3c41d9e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ObjectParamRule.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import 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() {
+        StringBuilder sb = new StringBuilder("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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ParserFeatureSetterFactory.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ParserFeatureSetterFactory.java
new file mode 100644
index 0000000..4f96776
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/ParserFeatureSetterFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import java.util.Properties;
+
+/**
+ * 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.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);
+        }
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/PathCallParamRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/PathCallParamRule.java
new file mode 100644
index 0000000..16310c6
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/PathCallParamRule.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import 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() {
+
+        StringBuilder sb = new StringBuilder("PathCallParamRule[");
+        sb.append("paramIndex=");
+        sb.append(paramIndex);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Rule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Rule.java
new file mode 100644
index 0000000..2817452
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Rule.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import 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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RuleSet.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RuleSet.java
new file mode 100644
index 0000000..d9b10bd
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RuleSet.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+/**
+ * <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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RuleSetBase.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RuleSetBase.java
new file mode 100644
index 0000000..9570780
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RuleSetBase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+/**
+ * <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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Rules.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Rules.java
new file mode 100644
index 0000000..61b0939
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/Rules.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import java.util.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 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<Rule> 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<Rule> rules();
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RulesBase.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RulesBase.java
new file mode 100644
index 0000000..88991ed
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/RulesBase.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import java.util.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<String, List<Rule>> cache =
+        new HashMap<String, List<Rule>>();
+
+
+    /**
+     * 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<Rule> rules = new ArrayList<Rule>();
+
+
+    // ------------------------------------------------------------- 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<Rule> items = rules.iterator();
+        while (items.hasNext()) {
+            Rule item = 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<Rule> list = cache.get(pattern);
+        if (list == null) {
+            list = new ArrayList<Rule>();
+            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 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<Rule> match(String namespaceURI, String pattern) {
+
+        // List rulesList = (List) this.cache.get(pattern);
+        List<Rule> rulesList = lookup(namespaceURI, pattern);
+        if ((rulesList == null) || (rulesList.size() < 1)) {
+            // Find the longest key, ie more discriminant
+            String longKey = "";
+            Iterator<String> keys = this.cache.keySet().iterator();
+            while (keys.hasNext()) {
+                String key = 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<Rule>();
+        }
+        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<Rule> 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<Rule> lookup(String namespaceURI, String pattern) {
+
+        // Optimize when no namespace URI is specified
+        List<Rule> 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<Rule> results = new ArrayList<Rule>();
+        Iterator<Rule> items = list.iterator();
+        while (items.hasNext()) {
+            Rule item = items.next();
+            if ((namespaceURI.equals(item.getNamespaceURI())) ||
+                    (item.getNamespaceURI() == null)) {
+                results.add(item);
+            }
+        }
+        return (results);
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetNextRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetNextRule.java
new file mode 100644
index 0000000..febb19e
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetNextRule.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import org.glassfish.web.util.IntrospectionUtils;
+
+import java.util.logging.Level;
+
+
+/**
+ * <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.isLoggable(Level.FINE)) {
+            if (parent == null) {
+                digester.log.log(Level.FINE, "[SetNextRule]{" + digester.match +
+                        "} Call [NULL PARENT]." +
+                        methodName + "(" + child + ")");
+            } else {
+                digester.log.log(Level.FINE, "[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() {
+
+        StringBuilder sb = new StringBuilder("SetNextRule[");
+        sb.append("methodName=");
+        sb.append(methodName);
+        sb.append(", paramType=");
+        sb.append(paramType);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetPropertiesRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
new file mode 100644
index 0000000..b14c1db
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import org.apache.catalina.LogFacade;
+import org.glassfish.web.util.IntrospectionUtils;
+import org.xml.sax.Attributes;
+
+import java.util.logging.Level;
+
+
+/**
+ * <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.isLoggable(Level.FINE)) {
+            if (top != null) {
+                digester.log.log(Level.FINE, "[SetPropertiesRule]{" + digester.match +
+                        "} Set " + top.getClass().getName() +
+                        " properties");
+            } else {
+                digester.log.log(Level.FINE, "[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.isLoggable(Level.FINE)) {
+                digester.log.log(Level.FINE, "[SetPropertiesRule]{" + digester.match +
+                        "} Setting property '" + name + "' to '" +
+                        value + "'");
+            }
+            if (!digester.isFakeAttribute(top, name) 
+                    && !IntrospectionUtils.setProperty(top, name, value) 
+                    && digester.getRulesValidation()) {
+                digester.log.log(Level.WARNING, LogFacade.PROPERTIES_RULE_NOT_FIND_MATCHING_PROPERTY,
+                                 new Object[] {digester.match, 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() {
+
+        StringBuilder sb = new StringBuilder("SetPropertiesRule[");
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetPropertyRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetPropertyRule.java
new file mode 100644
index 0000000..60137ec
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetPropertyRule.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import org.apache.catalina.LogFacade;
+import org.glassfish.web.util.IntrospectionUtils;
+import org.xml.sax.Attributes;
+
+import java.util.logging.Level;
+
+
+/**
+ * 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.isLoggable(Level.FINE)) {
+            digester.log.log(Level.FINE, "[SetPropertyRule]{" + digester.match +
+                    "} Set " + top.getClass().getName() + " property " +
+                    actualName + " to " + actualValue);
+        }
+
+        // Set the property (with conversion as necessary)
+        if (!digester.isFakeAttribute(top, actualName) 
+                && !IntrospectionUtils.setProperty(top, actualName, actualValue) 
+                && digester.getRulesValidation()) {
+            digester.log.log(Level.WARNING, LogFacade.PROPERTY_RULE_NOT_FIND_MATCHING_PROPERTY,
+                             new Object[] {digester.match, name, value});
+        }
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuilder sb = new StringBuilder("SetPropertyRule[");
+        sb.append("name=");
+        sb.append(name);
+        sb.append(", value=");
+        sb.append(value);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetRootRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetRootRule.java
new file mode 100644
index 0000000..b47546c
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetRootRule.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import org.glassfish.web.util.IntrospectionUtils;
+
+import java.util.logging.Level;
+
+
+/**
+ * <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.isLoggable(Level.FINE)) {
+            if (parent == null) {
+                digester.log.log(Level.FINE, "[SetRootRule]{" + digester.match +
+                        "} Call [NULL ROOT]." +
+                        methodName + "(" + child + ")");
+            } else {
+                digester.log.log(Level.FINE, "[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() {
+
+        StringBuilder sb = new StringBuilder("SetRootRule[");
+        sb.append("methodName=");
+        sb.append(methodName);
+        sb.append(", paramType=");
+        sb.append(paramType);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetTopRule.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetTopRule.java
new file mode 100644
index 0000000..9a9b6f8
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/SetTopRule.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+
+import org.glassfish.web.util.IntrospectionUtils;
+
+import java.util.logging.Level;
+
+
+/**
+ * <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.isLoggable(Level.FINE)) {
+            if (child == null) {
+                digester.log.log(Level.FINE, "[SetTopRule]{" + digester.match +
+                        "} Call [NULL CHILD]." +
+                        methodName + "(" + parent + ")");
+            } else {
+                digester.log.log(Level.FINE, "[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() {
+
+        StringBuilder sb = new StringBuilder("SetTopRule[");
+        sb.append("methodName=");
+        sb.append(methodName);
+        sb.append(", paramType=");
+        sb.append(paramType);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/WithDefaultsRulesWrapper.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/WithDefaultsRulesWrapper.java
new file mode 100644
index 0000000..1e3034b
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/WithDefaultsRulesWrapper.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import java.util.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<Rule> defaultRules = new ArrayList<Rule>();
+    /** All rules (preserves order in which they were originally added) */
+    private List<Rule> allRules = new ArrayList<Rule>();
+    
+    // --------------------------------------------------------- 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<Rule> it = defaultRules.iterator();
+        while (it.hasNext()) {
+            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<Rule> getDefaults() {
+        return defaultRules;
+    }	
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Return list of rules matching given pattern.
+     * If wrapped implementation returns any matches return those.
+     * Otherwise, return default matches.
+     */
+    public List<Rule> match(String namespaceURI, String pattern) {
+        List<Rule> matches = wrappedRules.match(namespaceURI, pattern);
+        if (matches ==  null || matches.isEmpty()) {	
+            // a little bit of defensive programming
+            return new ArrayList<Rule>(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<Rule> 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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/XercesParser.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/XercesParser.java
new file mode 100644
index 0000000..0848b46
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/digester/XercesParser.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.digester;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.lang.reflect.Method;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * 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 final Logger log =
+        Logger.getLogger("org.apache.tomcat.util.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 final String JAXP_SCHEMA_LANGUAGE =
+        "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+
+
+    /**
+     * Xerces dynamic validation property
+     */
+    protected static final String XERCES_DYNAMIC = 
+        "http://apache.org/xml/features/validation/dynamic";
+
+
+    /**
+     * Xerces schema validation property
+     */
+    protected static final String XERCES_SCHEMA =
+        "http://apache.org/xml/features/validation/schema";
+
+
+    /**
+     * A <code>float</code> representing the underlying Xerces version
+     */
+    static float version;
+
+
+    /**
+     * The current Xerces version.
+     */
+    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){
+            if (log.isLoggable(Level.INFO)) {
+                log.log(Level.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/appserver/web/web-core/src/main/java/org/apache/tomcat/util/http/res/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/http/res/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/http/res/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/net/res/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/net/res/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/net/res/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/res/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/res/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/res/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/security/PrivilegedGetTccl.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/security/PrivilegedGetTccl.java
new file mode 100644
index 0000000..84083e0
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/security/PrivilegedGetTccl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.security;
+
+import java.security.PrivilegedAction;
+
+public class PrivilegedGetTccl implements PrivilegedAction<ClassLoader> {
+    @Override
+    public ClassLoader run() {
+        return Thread.currentThread().getContextClassLoader();
+    }
+}
+
+
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/security/PrivilegedSetTccl.java b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/security/PrivilegedSetTccl.java
new file mode 100644
index 0000000..0d427bc
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/security/PrivilegedSetTccl.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.security;
+
+import java.security.PrivilegedAction;
+
+public class PrivilegedSetTccl implements PrivilegedAction<Void> {
+
+    private ClassLoader cl;
+
+    public PrivilegedSetTccl(ClassLoader cl) {
+        this.cl = cl;
+    }
+
+    @Override
+    public Void run() {
+        Thread.currentThread().setContextClassLoader(cl);
+        return null;
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/apache/tomcat/util/threads/res/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/threads/res/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/apache/tomcat/util/threads/res/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/glassfish/web/util/.gitkeep_empty_dir b/appserver/web/web-core/src/main/java/org/glassfish/web/util/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/glassfish/web/util/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/java/org/glassfish/web/valve/GlassFishValve.java b/appserver/web/web-core/src/main/java/org/glassfish/web/valve/GlassFishValve.java
new file mode 100644
index 0000000..5e3bc42
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/glassfish/web/valve/GlassFishValve.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.glassfish.web.valve;
+
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+/**
+ * <p>A <b>Valve</b> is a request processing component associated with a
+ * particular Container.  A series of Valves are generally associated with
+ * each other into a Pipeline.  The detailed contract for a Valve is included
+ * in the description of the <code>invoke()</code> method below.</p>
+ *
+ * <b>HISTORICAL NOTE</b>:  The "Valve" name was assigned to this concept
+ * because a valve is what you use in a real world pipeline to control and/or
+ * modify flows through it.
+ *
+ * @author Craig R. McClanahan
+ * @author Gunnar Rjnning
+ * @author Peter Donald
+ * @version $Revision: 1.2 $ $Date: 2005/12/08 01:27:21 $
+ */
+
+public interface GlassFishValve {
+
+    // ----------------------------------------------------- Manifest Constants
+
+    /**
+     * A valve returns this value to indicate (to the pipeline) that the next
+     * valve in the pipeline can be invoked.
+     */
+    public static final int INVOKE_NEXT = 1;
+
+    /**
+     * A valve returns this value to indicate that no further processing
+     * of the request should take place (along the rest of the pipeline).
+     * All valves that are function as 'basic' valves return this value
+     * as they are the 'last' valve in the pipeline. A valve (such as an
+     * authenticator) may return this value to stop a request from being
+     * processed further because the user/password could not be verified.
+     */
+    public static final int END_PIPELINE = 2;
+
+
+    //-------------------------------------------------------------- Properties
+
+    /**
+     * Return descriptive information about this Valve implementation.
+     */
+    public String getInfo();
+
+
+    //---------------------------------------------------------- Public Methods
+
+    /**
+     * <p>Perform request processing as required by this Valve.</p>
+     *
+     * <p>An individual Valve <b>MAY</b> perform the following actions, in
+     * the specified order:</p>
+     * <ul>
+     * <li>Examine and/or modify the properties of the specified Request and
+     *     Response.
+     * <li>Examine the properties of the specified Request, completely generate
+     *     the corresponding Response, and return control to the caller.
+     * <li>Examine the properties of the specified Request and Response, wrap
+     *     either or both of these objects to supplement their functionality,
+     *     and pass them on.
+     * <li>If the corresponding Response was not generated (and control was not
+     *     returned, call the next Valve in the pipeline (if there is one) by
+     *     executing <code>context.invokeNext()</code>.
+     * <li>Examine, but not modify, the properties of the resulting Response
+     *     (which was created by a subsequently invoked Valve or Container).
+     * </ul>
+     *
+     * <p>A Valve <b>MUST NOT</b> do any of the following things:</p>
+     * <ul>
+     * <li>Change request properties that have already been used to direct
+     *     the flow of processing control for this request (for instance,
+     *     trying to change the virtual host to which a Request should be
+     *     sent from a pipeline attached to a Host or Context in the
+     *     standard implementation).
+     * <li>Create a completed Response <strong>AND</strong> pass this
+     *     Request and Response on to the next Valve in the pipeline.
+     * <li>Consume bytes from the input stream associated with the Request,
+     *     unless it is completely generating the response, or wrapping the
+     *     request before passing it on.
+     * <li>Modify the HTTP headers included with the Response after the
+     *     <code>invokeNext()</code> method has returned.
+     * <li>Perform any actions on the output stream associated with the
+     *     specified Response after the <code>invokeNext()</code> method has
+     *     returned.
+     * </ul>
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     *
+     * @exception IOException if an input/output error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     * @exception ServletException if a servlet error occurs, or is thrown
+     *  by a subsequently invoked Valve, Filter, or Servlet
+     *
+     * @return <code>INVOKE_NEXT</code> or <code>END_PIPELINE</code>
+     */
+    public int invoke(Request request, Response response)
+        throws IOException, ServletException;
+
+
+    /**
+     * <p>Perform post-request processing as required by this Valve.</p>
+     *
+     * @param request The servlet request to be processed
+     * @param response The servlet response to be created
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void postInvoke(Request request, Response response)
+        throws IOException, ServletException;
+}
+
diff --git a/appserver/web/web-core/src/main/java/org/glassfish/web/valve/GlassFishValveAdapter.java b/appserver/web/web-core/src/main/java/org/glassfish/web/valve/GlassFishValveAdapter.java
new file mode 100644
index 0000000..409a562
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/glassfish/web/valve/GlassFishValveAdapter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.web.valve;
+
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.Valve;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * Adapter valve for wrapping a GlassFish-style valve that was compiled
+ * against the "old" org.apache.catalina.Valve interface from GlassFish
+ * releases prior to V3 (which has been renamed to
+ * org.glassfish.web.valve.GlassFishValve in GlassFish V3).
+ *
+ * @author jluehe
+ */
+public class GlassFishValveAdapter implements GlassFishValve {
+
+    // The wrapped GlassFish-style valve to which to delegate
+    private Valve gfValve;
+
+    private Method invokeMethod;
+    private Method postInvokeMethod;
+
+    /**
+     * Constructor.
+     *
+     * @param gfValve The GlassFish valve to which to delegate
+     */
+    public GlassFishValveAdapter(Valve gfValve) throws Exception {
+        this.gfValve = gfValve;
+        invokeMethod = gfValve.getClass().getMethod("invoke", Request.class,
+                                                    Response.class);
+        postInvokeMethod = gfValve.getClass().getMethod("postInvoke",
+                                                        Request.class,
+                                                        Response.class);
+    }
+
+    public String getInfo() {
+        return gfValve.getInfo();
+    }
+
+    /**
+     * Delegates to the invoke() of the wrapped GlassFish-style valve.
+     */
+    public int invoke(Request request,
+                      Response response)
+                throws IOException, ServletException {
+        try {
+            return ((Integer) invokeMethod.invoke(gfValve, request, response)).intValue();
+        } catch (Exception e) {
+            throw new ServletException(e);
+        }
+    }
+
+    /**
+     * Delegates to the postInvoke() of the wrapped GlassFish-style valve.
+     */
+    public void postInvoke(Request request, Response response)
+                throws IOException, ServletException {
+        try {
+            postInvokeMethod.invoke(gfValve, request, response);
+        } catch (Exception e) {
+            throw new ServletException(e);
+        }
+    }
+}
diff --git a/appserver/web/web-core/src/main/java/org/glassfish/web/valve/ServletContainerInterceptor.java b/appserver/web/web-core/src/main/java/org/glassfish/web/valve/ServletContainerInterceptor.java
new file mode 100644
index 0000000..fa6e701
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/glassfish/web/valve/ServletContainerInterceptor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.web.valve;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.jvnet.hk2.annotations.Contract;
+
+/**
+ * Contract interface for registering ServletContainerInterceptor 
+ * to the Web Container. It can be inherited by anyone who want to 
+ * extend the Web Container.
+ *
+ * @author Jeremy_Lv
+ *
+ */
+@Contract
+public interface ServletContainerInterceptor {
+
+	/**
+	 * User can set some useful informations before 
+	 * invoking the Servlet application
+	 * @param req
+	 * @param res
+	 */
+	public void preInvoke(Request req, Response res);
+	
+	/**
+	 * User can remove some useful informations after 
+	 * invoking the Servlet application
+	 * @param req
+	 * @param res
+	 */
+	public void postInvoke(Request req, Response res);
+}
diff --git a/appserver/web/web-core/src/main/java/org/glassfish/web/valve/TomcatValveAdapter.java b/appserver/web/web-core/src/main/java/org/glassfish/web/valve/TomcatValveAdapter.java
new file mode 100644
index 0000000..1bad048
--- /dev/null
+++ b/appserver/web/web-core/src/main/java/org/glassfish/web/valve/TomcatValveAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.web.valve;
+
+import org.apache.catalina.CometEvent;
+import org.apache.catalina.Valve;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+/**
+ * Tomcat-style wrapper valve around GlassFish-style valve.
+ *
+ * This allows Tomcat- and GlassFish-style valves to be added to a 
+ * pipeline in arbitrary order.
+ *
+ * @author jluehe
+ */
+public class TomcatValveAdapter implements Valve {
+
+    // The next valve in the pipeline to be invoked
+    private Valve next = null;
+
+    // The wrapped GlassFish-style valve to which to delegate
+    private GlassFishValve gfValve;
+
+    /**
+     * Constructor.
+     *
+     * @param gfValve The GlassFish-style valve to wrap
+     */
+    public TomcatValveAdapter(GlassFishValve gfValve) {
+        this.gfValve = gfValve;
+    }
+
+    public String getInfo() {
+        return gfValve.getInfo();
+    }
+
+    public Valve getNext() {
+        return next;
+    }
+
+    public void setNext(Valve valve) {
+        this.next = valve;
+    }
+
+    public void backgroundProcess() {
+        // Deliberate no-op
+    }
+
+    /**
+     * Delegates to the invoke() and postInvoke() methods of the wrapped
+     * GlassFish-style valve.
+     */
+    public void invoke(org.apache.catalina.connector.Request request,
+                       org.apache.catalina.connector.Response response)
+            throws IOException, ServletException {
+        int rc = gfValve.invoke(request, response);
+        if (rc != GlassFishValve.INVOKE_NEXT) {
+            return;
+        }
+        getNext().invoke(request, response);
+        gfValve.postInvoke(request, response);
+    }
+
+    public void event(org.apache.catalina.connector.Request request,
+                      org.apache.catalina.connector.Response response,
+                      CometEvent event)
+            throws IOException, ServletException {
+        // Deliberate no-op
+    }
+
+}
diff --git a/appserver/web/web-core/src/main/resources/com/sun/enterprise/web/connector/grizzly/.gitkeep_empty_dir b/appserver/web/web-core/src/main/resources/com/sun/enterprise/web/connector/grizzly/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/com/sun/enterprise/web/connector/grizzly/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/authenticator/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/authenticator/mbeans-descriptors.xml
new file mode 100644
index 0000000..cef636b
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/authenticator/mbeans-descriptors.xml
@@ -0,0 +1,183 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean name="BasicAuthenticator"
+         description="An Authenticator and Valve implementation of HTTP BASIC Authentication"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.BasicAuthenticator">
+    
+    <attribute   name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+      
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+    <attribute name="entropy"
+               description="A String initialization parameter used to increase the  entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+  
+  <mbean name="DigestAuthenticator"
+         description="An Authenticator and Valve implementation of HTTP DIGEST Authentication"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.DigestAuthenticator">
+    
+    <attribute name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute   name="entropy"
+               description="A String initialization parameter used to increase the  entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+  <mbean name="FormAuthenticator"
+         description="An Authenticator and Valve implementation of FORM BASED Authentication"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.FormAuthenticator">
+    
+    <attribute   name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute   name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute   name="entropy"
+               description="A String initialization parameter used to increase the entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+  <mbean name="NonLoginAuthenticator"
+         description="An Authenticator and Valve implementation that checks only security constraints not involving user authentication"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.NonLoginAuthenticator">
+    
+    <attribute name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+      
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+    <attribute name="entropy"
+               description="A String initialization parameter used to increase the entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+  
+  <mbean name="SingleSignOn"
+         description="A Valve that supports a 'single signon' user experience"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.SingleSignOn">
+    
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+  </mbean>
+
+
+  <mbean  name="SSLAuthenticator"
+         description="An Authenticator and Valve implementation of authentication that utilizes SSL certificates to identify client users"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.authenticator.SSLAuthenticator">
+
+    <attribute name="algorithm"
+               description="The message digest algorithm to be used when generating session identifiers"
+               type="java.lang.String"/>
+      
+    <attribute name="cache"
+               description="Should we cache authenticated Principals if the request is part of an HTTP session?"
+               type="boolean"/>
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute name="entropy"
+               description="A String initialization parameter used to increase the entropy of the initialization of our random number generator"
+               type="java.lang.String"/>
+  </mbean>
+  
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/connector/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/connector/mbeans-descriptors.xml
new file mode 100644
index 0000000..6a58b53
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/connector/mbeans-descriptors.xml
@@ -0,0 +1,200 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean         name="CoyoteConnector"
+          description="Implementation of a Coyote connector"
+               domain="Catalina"
+                group="Connector"
+                 type="org.apache.catalina.connector.Connector">
+
+    <attribute   name="acceptCount"
+          description="The accept count for this Connector"
+                 type="int"/>
+
+    <attribute   name="address"
+          description="The IP address on which to bind"
+                 type="java.lang.String"/>
+
+    <attribute   name="algorithm"
+          description="The certificate encoding algorithm to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="bufferSize"
+          description="The input buffer size we should create on input streams"
+                 type="int"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="clientAuth"
+          description="Should we require client authentication?"
+                 type="boolean"/>
+
+    <attribute   name="ciphers"
+          description="Comma-separated list of SSL cipher suites to be enabled"
+                 type="java.lang.String"/>
+
+    <attribute   name="compression"
+          description="Compression value"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionLinger"
+          description="Linger value on the incoming connection"
+                 type="int"/>
+
+    <attribute   name="connectionTimeout"
+          description="Timeout value on the incoming connection"
+                 type="int"/>
+
+    <attribute   name="connectionUploadTimeout"
+          description="Timeout value on the incoming connection during request processing"
+                 type="int"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute    name="disableUploadTimeout"
+           description="Should Tomcat ignore setting a timeout for uploads?" 
+                  type="boolean"/>
+
+    <attribute   name="enableLookups"
+          description="The 'enable DNS lookups' flag for this Connector"
+                 type="boolean"/>
+
+    <attribute   name="keystoreFile"
+          description="Pathname to the key store file to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="keystorePass"
+          description="Password for accessing the key store file"
+                 type="java.lang.String"/>
+
+    <attribute   name="keystoreType"
+          description="Type of keystore file to be used for the server certificate"
+                 type="java.lang.String"/>
+
+    <attribute   name="keyAlias"
+          description="Alias name of this connector's keypair and supporting certificate chain"
+                 type="java.lang.String"/>
+
+    <attribute   name="maxKeepAliveRequests"
+          description="Maximum number of Keep-Alive requests to honor per connection"
+                 type="int"/>
+
+    <attribute   name="maxPostSize"
+          description="Maximum size in bytes of a POST which will be handled by the servlet API provided features"
+                 type="int"/>
+
+    <attribute   name="maxProcessors"
+          description="The maximum number of processors allowed"
+                 type="int"/>
+
+    <attribute   name="minProcessors"
+          description="The minimum number of processors to start at
+                       initialization time"
+                 type="int"/>
+
+    <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="port"
+          description="The port number on which we listen for ajp13 requests"
+                type="int"/>
+
+    <attribute   name="protocol"
+          description="Coyote protocol handler in use"
+                 type="java.lang.String"/>
+
+    <attribute   name="protocolHandlerClassName"
+          description="Coyote Protocol handler class name"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="proxyName"
+          description="Ther Server name to which we should pretend requests to this Connector"
+                 type="java.lang.String"/>
+
+    <attribute   name="proxyPort"
+          description="Ther Server port to which we should pretend requests to this Connector"
+                 type="int"/>
+
+    <attribute   name="redirectPort"
+          description="The redirect port for non-SSL to SSL redirects"
+                 type="int"/>
+
+    <attribute   name="scheme"
+          description="Protocol name for this Connector (http, https)"
+                 type="java.lang.String"/>
+
+    <attribute   name="secret"
+          description="Authentication secret (I guess ... not in Javadocs)"
+            readable = "false" 
+                 type="java.lang.String"/>
+
+    <attribute   name="secure"
+          description="Is this a secure (SSL) Connector?"
+                 type="boolean"/>
+
+    <attribute   name="sslProtocol"
+          description="SSL protocol variant to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="sslProtocols"
+          description="Comma-separated list of SSL protocol variants to be enabled"
+                 type="java.lang.String"/>
+
+    <attribute   name="tcpNoDelay"
+          description="Should we use TCP no delay?"
+                 type="boolean"/>
+
+    <attribute    name="tomcatAuthentication"
+           description="Should Tomcat perform all authentications?"
+                  type="boolean"/>
+
+    <attribute    name="xpoweredBy"
+           description="Is generation of X-Powered-By response header enabled/disabled?"
+                    is="true"
+                  type="boolean"/>
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+
+  </mbean>
+
+
+</mbeans-descriptors>
+
+
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/core/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/core/mbeans-descriptors.xml
new file mode 100644
index 0000000..bed85bb
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/core/mbeans-descriptors.xml
@@ -0,0 +1,1404 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+
+<mbean name="WebModule"
+         description="Standard WebModule Component"
+         domain="com.sun.appserv"
+         group="Context"
+         type="com.sun.enterprise.web.WebModule">
+    
+    <attribute name="allowLinking"
+               description="Allow symlinking to outside the webapp root directory, if the webapp is an exploded directory"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="cacheMaxSize"
+               description="Maximum cache size in KB"
+               type="int"/>
+      
+    <attribute name="cacheTTL"
+               description="Time interval in ms between cache refeshes"
+               type="int"/>
+      
+    <attribute name="cachingAllowed"
+               description="Should we cache static resources for this webapp"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="caseSensitive"
+               description="Should case sensitivity checks be performed"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="children"
+               description="Object names of all children"
+               type="[Ljavax.management.ObjectName;"/>
+    <attribute   name="cookies"
+               description="Should we attempt to use cookies for session id
+               communication?"
+               type="boolean"/>
+      
+    <attribute name="compilerClasspath"
+               description="The compiler classpath to use"
+               type="java.lang.String"/>
+      
+    <attribute name="crossContext"
+               description="Should we allow the ServletContext.getContext() method to access the context of other web applications in this server?"
+               type="boolean"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+               
+    <attribute name="defaultWebXml"
+               description="Location of the default web.xml resource or file"
+               type="java.lang.String"/>
+               
+    <attribute name="delegate"
+               description=""
+               type="boolean"/>
+               
+    <attribute name="deploymentDescriptor"
+               description="String deployment descriptor "
+               type="java.lang.String"/>
+                     
+    <attribute name="docBase"
+               description="The document root for this web application"
+               type="java.lang.String"/>
+
+    <attribute name="endpointAddresses"
+               description="An array of URL addresses defined in this Web Module to invoke web services endpoints implementations"
+               type="[Ljava.lang.String;"/>
+
+    <attribute name="engineName"
+               description="Name of the engine domain, if different from the context domain"
+               type="java.lang.String"/>
+
+    <attribute name="environments"
+               description="MBean Names of the set of defined environment entries for this web application"
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+
+    <attribute name="eventProvider"
+               description="Event provider support for this managed object"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="hasWebServices"
+               description="True if the web module implements web services endpoints"
+               type="boolean"/>
+                              
+    <attribute name="javaVMs"
+               description="The Java virtual machines on which this module is running"
+               type="[Ljava.lang.String;"/>
+
+    <attribute name="managerChecksFrequency"
+               description="The frequency of the manager checks (expiration and passivation)"
+               type="int"/>
+
+    <attribute name="name"
+               description="Name of the web module"
+               type="java.lang.String"/>
+
+    <attribute name="objectName"
+               description="Object name of the web module"
+               type="java.lang.String"/>
+      
+    <attribute name="override"
+               description="The DefaultContext override flag for this web application"
+               type="boolean"/>
+      
+    <attribute name="path"
+               description="The context path for this Context"
+               type="java.lang.String"/>
+               
+    <attribute name="privileged"
+               description="Access to tomcat internals"
+               type="boolean"/>
+      
+    <attribute name="reloadable"
+               description="The reloadable flag for this web application"
+               type="boolean"/>
+
+    <attribute name="resourceNames"
+               description="MBean Names of all the defined resource references for this application."
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+
+    <attribute name="server"
+               description="The J2EE Server this module is deployed on"
+               type="java.lang.String"/>
+                              
+    <attribute name="servlets"
+               description="JSR77 list of servlets"
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+               
+    <attribute name="startupTime"
+               description="Time (in milliseconds) it took to start this context"
+               type="long"/>
+
+    <attribute name="startTimeMillis"
+               description="Time (in milliseconds since January 1, 1970, 00:00:00) when this context was started"
+               type="long"/>
+
+    <attribute name="processingTimeMillis"
+               description="Cumulative execution times of all servlets in this context"
+               type="long"
+               writeable="false" />
+
+    <attribute name="state"
+               description="Current state of this component"
+               type="int"/>
+                     
+    <attribute name="stateManageable"
+               description="State management support for this managed object"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="statisticsProvider"
+               description="Performance statistics support for this managed object"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="swallowOutput"
+               description="Flag to set to cause the system.out and system.err to be redirected to the logger when executing a servlet"
+               type="boolean"/>
+               
+    <attribute name="tldScanTime"
+               description="Time spend scanning jars for TLDs for this context"
+               type="long"/>
+
+    <attribute name="useNaming"
+               description="Create a JNDI naming context for this application?"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="valveObjectNames"
+               description="ObjectNames for the valves associated with this container"
+               type="[Ljavax.management.ObjectName;"
+               writeable="false"/>
+
+    <attribute name="welcomeFiles"
+               description="The welcome files for this context"
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+      
+    <attribute name="workDir"
+               description="The pathname to the work directory for this context"
+               type="java.lang.String"/>
+      
+    <operation name="addEnvironment"
+               description="Add an environment entry for this web application"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="envName"
+                 description="New environment entry name"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="addResource"
+               description="Add a resource reference for this web application"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="resourceName"
+                 description="New resource reference name"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation   name="addValve"
+               description="Add a valve to this Context"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="valve"
+                 description="New valve to be added"
+                 type="org.apache.catalina.Valve"/>
+    </operation>
+    
+    <operation   name="removeEnvironment"
+               description="Remove any environment entry with the specified name"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="envName"
+                 description="Name of the environment entry to remove"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation   name="removeResource"
+               description="Remove any resource reference with the specified name"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="resourceName"
+                 description="Name of the resource reference to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeValve"
+               description="Remove a valve from this Context"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="valve"
+                 description="New valve to be removed"
+                 type="org.apache.catalina.Valve"/>
+    </operation>
+    
+    <operation   name="reload"
+               description="Reload the webapplication"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation   name="init"
+               description="Register the context into the running server"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation   name="start"
+               description="Start the contex"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="stop"
+               description="Stop the contex"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="destroy"
+               description="Destroy the contex"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+
+    <operation name="findMappingObject"
+               description="Return an object which may be utilized for mapping to this component"
+               impact="INFO"
+               returnType="org.apache.catalina.Context">    
+    </operation>    
+    
+    <operation name="findStaticResources"
+               description="Return the naming resources associated with this web application"
+               impact="INFO"
+               returnType="javax.naming.directory.DirContext">    
+    </operation>
+
+  </mbean>
+
+
+  <mbean name="VirtualServer"
+         description="Standard Virtual Server Component"
+         domain="com.sun.appserv"
+         group="Host"
+         type="com.sun.enterprise.web.VirtualServer">
+    
+    <attribute name="appBase"
+               description="The application root for this Host"
+               type="java.lang.String"/>
+      
+    <attribute name="autoDeploy"
+               description="The auto deploy flag for this Host"
+               type="boolean"/>
+                 
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute name="deployOnStartup"
+               description="The deploy on startup flag for this Host"
+               type="boolean"/>
+
+    <attribute name="deployXML"
+               description="deploy Context XML config files property"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="name"
+               description="Unique name of this Host"
+               type="java.lang.String"/>
+      
+    <attribute name="unpackWARs"
+               description="Unpack WARs property"
+               is="true"
+               type="boolean"/>
+    
+    <attribute name="xmlNamespaceAware"
+               description="Attribute value used to turn on/off XML namespace awareness"
+               type="boolean"/>
+      
+    <attribute name="xmlValidation"
+               description="Attribute value used to turn on/off XML validation"
+               type="boolean"/>
+
+    <attribute name="children"
+               description="Object names of all children"
+               type="[Ljavax.management.ObjectName;"/>
+      
+    <attribute name="aliases"
+               description="Host aliases"
+               type="[Ljava.lang.String;"/>
+      
+    <attribute name="valveNames"
+               description="Return the MBean Names of the Valves associated with this Host"
+               type="[Ljava.lang.String;"/>
+      
+    <attribute name="valveObjectNames"
+               description="Return the MBean ObjectNames of the Valves associated with this Host"
+               type="[Ljavax.management.ObjectName;"/>
+      
+    <operation name="addAlias"
+               description="Add an alias name that should be mapped to this Host"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="alias"
+                 description="The alias to be added"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation name="findAliases"
+               description="Return the set of alias names for this Host"
+               impact="INFO"
+               returnType="[Ljava.lang.String;"/>
+
+    <operation name="addChild"
+               description="Add a context"
+               impact="ACTION">
+      <parameter name="child"
+                 description="Context to be added"
+                 type="org.apache.catalina.Container"/>
+    </operation>
+      
+    <operation   name="removeAlias"
+               description="Remove the specified alias name from the aliases for this  Host"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="alias"
+                 description="Alias name to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation name="findPorts"
+               description="Return the port numbers with which this StandardHost is associated."
+               impact="INFO"
+               returnType="[Lint;">
+    </operation>
+
+    <operation name="findMappingObject"
+               description="Return an object which may be utilized for mapping to this component"
+               impact="INFO"
+               returnType="org.apache.catalina.Host">    
+    </operation>
+    
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+
+  <mbean         name="PECoyoteConnector"
+          description="Implementation of a PE Coyote connector"
+               domain="com.sun.appserv"
+                group="Connector"
+                 type="com.sun.enterprise.web.connector.coyote.PECoyoteConnector">
+
+    <attribute   name="acceptCount"
+          description="The accept count for this Connector"
+                 type="int"/>
+
+    <attribute   name="address"
+          description="The IP address on which to bind"
+                 type="java.lang.String"/>
+
+    <attribute   name="algorithm"
+          description="The certificate encoding algorithm to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="bufferSize"
+          description="The input buffer size we should create on input streams"
+                 type="int"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="clientAuth"
+          description="Should we require client authentication?"
+                 type="boolean"/>
+
+    <attribute   name="ciphers"
+          description="Comma-separated list of SSL cipher suites to be enabled"
+                 type="java.lang.String"/>
+
+    <attribute   name="compression"
+          description="Compression value"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionLinger"
+          description="Linger value on the incoming connection"
+                 type="int"/>
+
+    <attribute   name="connectionTimeout"
+          description="Timeout value on the incoming connection"
+                 type="int"/>
+
+    <attribute   name="connectionUploadTimeout"
+          description="Timeout value on the incoming connection during request processing"
+                 type="int"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute    name="disableUploadTimeout"
+           description="Should Tomcat ignore setting a timeout for uploads?" 
+                  type="boolean"/>
+
+    <attribute   name="enableLookups"
+          description="The 'enable DNS lookups' flag for this Connector"
+                 type="boolean"/>
+
+    <attribute   name="keystoreFile"
+          description="Pathname to the key store file to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="keystorePass"
+          description="Password for accessing the key store file"
+                 type="java.lang.String"/>
+
+    <attribute   name="keystoreType"
+          description="Type of keystore file to be used for the server certificate"
+                 type="java.lang.String"/>
+
+    <attribute   name="keyAlias"
+          description="Alias name of this connector's keypair and supporting certificate chain"
+                 type="java.lang.String"/>
+
+    <attribute   name="maxKeepAliveRequests"
+          description="Maximum number of Keep-Alive requests to honor per connection"
+                 type="int"/>
+
+    <attribute   name="maxPostSize"
+          description="Maximum size in bytes of a POST which will be handled by the servlet API provided features"
+                 type="int"/>
+
+    <attribute   name="maxProcessors"
+          description="The maximum number of processors allowed"
+                 type="int"/>
+
+    <attribute   name="minProcessors"
+          description="The minimum number of processors to start at
+                       initialization time"
+                 type="int"/>
+
+    <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="minProcessors"
+          description="The minimum number of processors to start at
+                       initialization time"
+                 type="int"/>
+
+    <attribute   name="port"
+          description="The port number on which we listen for ajp13 requests"
+                type="int"/>
+
+    <attribute   name="protocol"
+          description="Coyote protocol handler in use"
+                 type="java.lang.String"/>
+
+    <attribute   name="protocolHandlerClassName"
+          description="Coyote Protocol handler class name"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="proxyName"
+          description="Ther Server name to which we should pretend requests to this Connector"
+                 type="java.lang.String"/>
+
+    <attribute   name="proxyPort"
+          description="Ther Server port to which we should pretend requests to this Connector"
+                 type="int"/>
+
+    <attribute   name="redirectPort"
+          description="The redirect port for non-SSL to SSL redirects"
+                 type="int"/>
+
+    <attribute   name="scheme"
+          description="Protocol name for this Connector (http, https)"
+                 type="java.lang.String"/>
+
+    <attribute   name="secure"
+          description="Is this a secure (SSL) Connector?"
+                 type="boolean"/>
+
+    <attribute   name="sslProtocol"
+          description="SSL protocol variant to be used"
+                 type="java.lang.String"/>
+
+    <attribute   name="sslProtocols"
+          description="Comma-separated list of SSL protocol variants to be enabled"
+                 type="java.lang.String"/>
+
+    <attribute   name="tcpNoDelay"
+          description="Should we use TCP no delay?"
+                 type="boolean"/>
+
+    <attribute    name="tomcatAuthentication"
+           description="Should Tomcat perform all authentications?"
+                  type="boolean"/>
+
+    <attribute    name="xpoweredBy"
+           description="Is generation of X-Powered-By response header enabled/disabled?"
+                    is="true"
+                  type="boolean"/>
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+
+  </mbean>
+
+
+  <mbean         name="RequestInfo"
+          description="Structure holding the Request and Response objects"
+               domain="Catalina"
+                group="Connector"
+                 type="org.apache.coyote.RequestInfo">
+
+    <attribute    name="method"
+                  type="java.lang.String"
+             writeable="false"/>
+
+    <attribute    name="currentUri"
+                  type="java.lang.String"
+             writeable="false"/>
+
+    <attribute    name="currentQueryString"
+                  type="java.lang.String"
+             writeable="false"/>
+
+    <attribute    name="protocol"
+                  type="java.lang.String"
+             writeable="false"/>
+
+    <attribute    name="virtualHost"
+                  type="java.lang.String"
+             writeable="false"/>
+
+    <attribute    name="serverPort"
+                  type="int"
+             writeable="false"/>
+
+    <attribute    name="remoteAddr"
+                  type="java.lang.String"
+             writeable="false"/>
+
+    <attribute    name="contentLength"
+                  type="int"
+             writeable="false"/>
+
+    <attribute    name="requestBytesReceived"
+                  type="long"
+             writeable="false"/>
+
+    <attribute    name="requestBytesSent"
+                  type="long"
+             writeable="false"/>
+
+    <attribute    name="requestProcessingTime"
+                  type="long"
+             writeable="false"/>
+
+    <attribute   name="stage"
+                 type="int"/>
+
+    <attribute   name="bytesSent"
+                 type="long"/>
+
+    <attribute   name="bytesReceived"
+                 type="long"/>
+
+    <attribute   name="processingTime"
+                 type="long"/>
+
+    <attribute   name="maxTime"
+                 type="long"/>
+
+    <attribute   name="maxRequestUri"
+                 type="java.lang.String"/>
+
+    <attribute   name="requestCount"
+                 type="int"/>
+
+    <attribute   name="errorCount"
+                 type="int"/>
+
+    <attribute   name="workerThreadID"
+                 type="long"/>
+
+    <attribute   name="requestCompletionTime"
+                 type="long"/>
+
+
+  </mbean>
+
+
+  <mbean name="DefaultContext"
+         description="Used to store the default configuration a Host will use when creating a Context"
+         domain="Catalina"
+         group="Default-Context"
+         type="org.apache.catalina.core.StandardDefaultContext">
+
+    <attribute name="cookies"
+               description="Should we attempt to use cookies for session id communication?"
+               type="boolean"/>
+      
+    <attribute name="crossContext"
+               description="Should we allow the ServletContext.getContext() method to access the context of other web applications in this server?"
+               type="boolean"/>
+      
+    <attribute name="reloadable"
+               description="The reloadable flag for this web application"
+               type="boolean"/>
+
+    <attribute name="swallowOutput"
+               description="Flag to set to cause the system.out and system.err to be redirected to the logger when executing a servlet"
+               type="boolean"/>
+
+    <attribute name="useNaming"
+               description="Create JNDI naming context?"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="resourceNames"
+               description="Object names for the resources"
+               is="true"
+               type="[Ljava.lang.String;"/>
+                 
+    <operation name="addEnvironment"
+               description="Add an environment entry for this web application"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="envName"
+                 description="New environment entry name"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation name="addResource"
+               description="Add a resource reference for this web application"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="resourceName"
+                 description="New resource reference name"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation name="removeEnvironment"
+               description="Remove any environment entry with the specified name"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="envName"
+                 description="Name of the environment entry to remove"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation name="removeResource"
+               description="Remove any resource reference with the specified name"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="resourceName"
+                 description="Name of the resource reference to remove"
+                 type="java.lang.String"/>
+    </operation>
+    
+  </mbean>
+  
+  <mbean name="NamingContextListener"
+         description="Helper class used to initialize and populate the JNDI context associated with each context and server"
+         domain="Catalina"
+         group="Listener"
+         type="org.apache.catalina.core.NamingContextListener">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+  </mbean>
+
+  <mbean name="StandardContext"
+         description="Standard Context Component"
+         domain="Catalina"
+         group="Context"
+         type="org.apache.catalina.core.StandardContext">
+    
+    <attribute name="children"
+               description="Object names of all children"
+               type="[Ljavax.management.ObjectName;"/>
+               
+    <attribute   name="cookies"
+               description="Should we attempt to use cookies for session id
+               communication?"
+               type="boolean"/>
+      
+    <attribute name="compilerClasspath"
+               description="The compiler classpath to use"
+               type="java.lang.String"/>
+      
+    <attribute name="crossContext"
+               description="Should we allow the ServletContext.getContext() method to access the context of other web applications in this server?"
+               type="boolean"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+               
+    <attribute name="defaultWebXml"
+               description="Location of the default web.xml resource or file"
+               type="java.lang.String"/>
+               
+    <attribute name="deploymentDescriptor"
+               description="String deployment descriptor "
+               type="java.lang.String"/>
+                     
+    <attribute name="docBase"
+               description="The document root for this web application"
+               type="java.lang.String"/>
+
+    <attribute name="endpointAddresses"
+               description="An array of URL addresses defined in this Web Module to invoke web services endpoints implementations"
+               type="[Ljava.lang.String;"/> 
+      
+    <attribute name="engineName"
+               description="Name of the engine domain, if different from the context domain"
+               type="java.lang.String"/>
+
+    <attribute name="environments"
+               description="MBean Names of the set of defined environment entries for this web application"
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+
+    <attribute name="eventProvider"
+               description="Event provider support for this managed object"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="hasWebServices"
+               description="True if the web module implements web services endpoints"
+               type="boolean"/>
+                              
+    <attribute name="javaVMs"
+               description="The Java virtual machines on which this module is running"
+               type="[Ljava.lang.String;"/>
+
+    <attribute name="loader"
+               description="Associated loader."
+               type="org.apache.catalina.Loader" />
+      
+    <attribute name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute name="mappingObject"
+               description="The object used for mapping"
+               type="java.lang.Object"/>
+      
+    <attribute name="objectName"
+               description="Name of the object"
+               type="java.lang.String"/>
+      
+    <attribute name="override"
+               description="The DefaultContext override flag for this web application"
+               type="boolean"/>
+      
+    <attribute name="parentClassLoader"
+               description="Parent class loader."
+               type="java.lang.ClassLoader" />
+      
+    <attribute name="path"
+               description="The context path for this Context"
+               type="java.lang.String"/>
+               
+    <attribute name="privileged"
+               description="Access to tomcat internals"
+               type="boolean"/>
+      
+    <attribute name="reloadable"
+               description="The reloadable flag for this web application"
+               type="boolean"/>
+
+    <attribute name="resourceNames"
+               description="MBean Names of all the defined resource references for this application."
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+
+    <attribute name="server"
+               description="The J2EE Server this module is deployed on"
+               type="java.lang.String"/>
+                              
+    <attribute name="servlets"
+               description="JSR77 list of servlets"
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+               
+    <attribute name="startupTime"
+               description="Startup time for this context"
+               type="long"/>
+               
+    <attribute name="state"
+               description="Current state of this component"
+               type="int"/>
+                     
+    <attribute name="stateManageable"
+               description="State management support for this managed object"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="statisticsProvider"
+               description="Performance statistics support for this managed object"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="staticResources"
+               description="Static resources associated with the context."
+               type="javax.naming.directory.DirContext"
+               writeable="false"/>
+      
+    <attribute   name="swallowOutput"
+               description="Flag to set to cause the system.out and system.err to be redirected to the logger when executing a servlet"
+               type="boolean"/>
+               
+    <attribute name="tldScanTime"
+               description="Time spend scanning jars for TLDs for this context"
+               type="long"/>
+
+    <attribute name="useNaming"
+               description="Create a JNDI naming context for this application?"
+               is="true"
+               type="boolean"/>
+               
+    <attribute name="valveObjectNames"
+               description="ObjectNames for the valves associated with this container"
+               type="[Ljavax.management.ObjectName;"
+               writeable="false"/>
+
+    <attribute name="welcomeFiles"
+               description="The welcome files for this context"
+               type="[Ljava.lang.String;"
+               writeable="false"/>
+      
+    <attribute name="workDir"
+               description="The pathname to the work directory for this context"
+               type="java.lang.String"/>
+      
+    <operation name="addEnvironment"
+               description="Add an environment entry for this web application"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="envName"
+                 description="New environment entry name"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation   name="addResource"
+               description="Add a resource reference for this web application"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="resourceName"
+                 description="New resource reference name"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation   name="removeEnvironment"
+               description="Remove any environment entry with the specified name"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="envName"
+                 description="Name of the environment entry to remove"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation   name="removeResource"
+               description="Remove any resource reference with the specified name"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="resourceName"
+                 description="Name of the resource reference to remove"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation   name="setRealm"
+               description="Configures the realm for this webapp"
+               impact="ACTION" returnType="void">
+      <parameter name="realm"  
+                 description="Realm object"
+                 type="org.apache.catalina.Realm"/>
+    </operation>
+    
+    <operation   name="reload"
+               description="Reload the webapplication"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation   name="init"
+               description="Register the context into the running server"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation   name="start"
+               description="Start the contex"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="stop"
+               description="Stop the contex"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="destroy"
+               description="Destroy the contex"
+               impact="ACTION"
+               returnType="void">
+    </operation>
+    
+    <operation name="findMappingObject"
+               description="Return an object which may be utilized for mapping to this component"
+               impact="INFO"
+               returnType="org.apache.catalina.Context">    
+    </operation>    
+    
+    <operation name="findStaticResources"
+               description="Return the naming resources associated with this web application"
+               impact="INFO"
+               returnType="javax.naming.directory.DirContext">    
+    </operation>
+    
+  </mbean>
+  
+  <mbean name="StandardContextValve"
+         description="Valve that implements the default basic behavior for the
+         StandardContext container implementation"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.core.StandardContextValve">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+  </mbean>
+  
+  <mbean name="StandardEngine"
+         type="org.apache.catalina.core.StandardEngine"
+         description="Standard Engine Component"
+         domain="Catalina"
+         group="Engine">
+    
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute name="defaultHost"
+               description="Name of the default Host for this Engine"
+               type="java.lang.String"/>
+      
+    <attribute   name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute   name="name"
+               description="Unique name of this Engine"
+               type="java.lang.String"/>
+      
+    <attribute   name="baseDir"
+               description="Base dir for this engine, typically same as catalina.base system property"
+               type="java.lang.String"/>
+      
+    <attribute   name="jvmRoute"
+               description="Route used for load balancing"
+               type="java.lang.String"/>
+      
+    <attribute name="valveObjectNames"
+               description="ObjectNames for the valves associated with this container"
+               type="[Ljavax.management.ObjectName;"
+               writeable="false"/>
+
+    <operation name="addChild"
+               description="Add a virtual host"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="child"
+                 description="Host object"
+                 type="org.apache.catalina.Container"/>
+    </operation>
+    
+    <operation   name="setRealm"
+               description="Configures the realm for this engine"
+               impact="ACTION" returnType="void">
+      <parameter name="realm"  description="Realm object"
+                 type="org.apache.catalina.Realm"/>
+    </operation>
+    
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+      
+  </mbean>
+
+  
+  <mbean name="StandardEngineValve"
+         description="Valve that implements the default basic behavior for the
+         StandardEngine container implementation"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.core.StandardEngineValve">
+    
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+  </mbean>
+  
+  <mbean name="StandardHost"
+         description="Standard Host Component"
+         domain="Catalina"
+         group="Host"
+         type="org.apache.catalina.core.StandardHost">
+    
+    <attribute name="appBase"
+               description="The application root for this Host"
+               type="java.lang.String"/>
+      
+    <attribute name="autoDeploy"
+               description="The auto deploy flag for this Host"
+               type="boolean"/>
+                 
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute name="deployOnStartup"
+               description="The deploy on startup flag for this Host"
+               type="boolean"/>
+
+    <attribute name="deployXML"
+               description="deploy Context XML config files property"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute name="name"
+               description="Unique name of this Host"
+               type="java.lang.String"/>
+      
+    <attribute name="unpackWARs"
+               description="Unpack WARs property"
+               is="true"
+               type="boolean"/>
+      
+    <attribute name="xmlNamespaceAware"
+               description="Attribute value used to turn on/off XML namespace awareness"
+               type="boolean"/>
+      
+    <attribute name="xmlValidation"
+               description="Attribute value used to turn on/off XML validation"
+               type="boolean"/>
+
+    <attribute name="children"
+               description="Object names of all children"
+               type="[Ljavax.management.ObjectName;"/>
+      
+    <attribute name="aliases"
+               description="Host aliases"
+               type="[Ljava.lang.String;"/>
+      
+    <attribute name="ports"
+               description="Ports with which this StandardHost is associated"
+               type="[I;"/>
+
+    <attribute name="valveNames"
+               description="Return the MBean Names of the Valves associated with this Host"
+               type="[Ljava.lang.String;"/>
+      
+    <attribute name="valveObjectNames"
+               description="Return the MBean ObjectNames of the Valves associated with this Host"
+               type="[Ljavax.management.ObjectName;"/>
+      
+    <operation name="addAlias"
+               description="Add an alias name that should be mapped to this Host"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="alias"
+                 description="The alias to be added"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation name="findAliases"
+               description="Return the set of alias names for this Host"
+               impact="INFO"
+               returnType="[Ljava.lang.String;"/>
+
+    <operation name="findPorts"
+               description="Returns the ports that this StandardHost is associated with"
+               impact="INFO"
+               returnType="[I;"/>
+
+    <operation name="findMappingObject"
+               description="Return an object which may be utilized for mapping to this component"
+               impact="INFO"
+               returnType="org.apache.catalina.Host">    
+    </operation>
+
+    <operation name="addChild"
+               description="Add a context"
+               impact="ACTION">
+      <parameter name="child"
+                 description="Context to be added"
+                 type="org.apache.catalina.Container"/>
+    </operation>
+      
+    <operation   name="removeAlias"
+               description="Remove the specified alias name from the aliases for this  Host"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="alias"
+                 description="Alias name to be removed"
+                 type="java.lang.String"/>
+    </operation>
+    
+    <operation   name="setRealm"
+               description="Configures the realm for this host"
+               impact="ACTION" returnType="void">
+      <parameter name="realm"  description="Realm object"
+                 type="org.apache.catalina.Realm"/>
+    </operation>
+    
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+  
+  <mbean name="StandardHostValve"
+         description="Valve that implements the default basic behavior for the
+         StandardHost container implementation"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.core.StandardHostValve">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+  </mbean>
+
+  <mbean name="StandardServer"
+         description="Standard Server Component"
+         domain="Catalina"
+         group="Server"
+         type="org.apache.catalina.core.StandardServer">
+    
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute name="managedResource"
+               description="The managed resource this MBean is associated with"
+               type="java.lang.Object"/>
+      
+    <attribute name="port"
+               description="TCP port for shutdown messages"
+               type="int"/>
+      
+    <attribute name="shutdown"
+               description="Shutdown password"
+               type="java.lang.String"/>
+      
+    <attribute name="serviceNames"
+               description="Object names of all services we know about"
+               type="[Ljavax.management.ObjectName;"/>
+      
+    <operation name="await"
+               description="Wait for the shutdown message"
+               impact="ACTION"
+               returnType="void" />
+  </mbean>
+
+  
+  <mbean name="StandardService"
+         description="Standard Service Component"
+         domain="Catalina"
+         group="Service"
+         type="org.apache.catalina.core.StandardService">
+    
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute name="name"
+               description="Unique name of this Service"
+               type="java.lang.String"/>
+      
+    <attribute name="connectorNames"
+               description="ObjectNames of the connectors"
+               type="[Ljavax.management.ObjectName;"
+               writeable="false" />
+      
+    <attribute name="containerName"
+               description="ObjectNames of the engine"
+               type="javax.management.ObjectName"
+               writeable="false" />
+
+    <operation name="addConnector"
+               description="Add a new connector"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="connector"
+                 description="Connector object"
+                 type="org.apache.catalina.Connector"/>
+    </operation>
+    
+    <operation name="setContainer"
+               description="Set the servlet engine that will process the requests "
+               impact="ACTION"
+               returnType="void">
+      <parameter name="engine"
+                 description="Engine object"
+                 type="org.apache.catalina.Container"/>
+    </operation>
+    
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+  </mbean>
+
+  <mbean name="StandardWrapper"
+         description="Wrapper that represents an individual servlet definition"
+         domain="Catalina"
+         group="Wrapper"
+         type="org.apache.catalina.core.StandardWrapper">
+               
+    <attribute name="engineName"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+               
+    <attribute name="eventProvider"
+               description="Event provider support for this managed object"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="objectName"
+               description="Name of the object"
+               type="java.lang.String"/>
+
+    <attribute name="stateManageable"
+               description="State management support for this managed object"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="statisticsProvider"
+               description="Performance statistics support for this managed object"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="loadTime"
+               description="Load time"
+               type="long"
+               writeable="false" />
+
+    <attribute name="classLoadTime"
+               description="Class loading time"
+               type="int"
+               writeable="false" />
+
+    <operation name="findMappings"
+               description="Return the mappings associated with this wrapper"
+               impact="INFO"
+               returnType="[Ljava.lang.String;">
+    </operation>
+               
+    <operation name="findMappingObject"
+               description="Return an object which may be utilized for mapping to this component"
+               impact="INFO"
+               returnType="org.apache.catalina.Wrapper">
+    </operation>
+    
+  </mbean>
+  
+  <mbean name="StandardWrapperValve"
+         description="Valve that implements the default basic behavior for the
+         StandardWrapper container implementation"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.core.StandardWrapperValve">
+         
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>  
+                    
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/deploy/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/deploy/mbeans-descriptors.xml
new file mode 100644
index 0000000..e7a1292
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/deploy/mbeans-descriptors.xml
@@ -0,0 +1,204 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean name="ContextEnvironment"
+         className="org.apache.catalina.mbeans.ContextEnvironmentMBean"
+         description="Representation of an application environment entry"
+         domain="Catalina"
+         group="Resources"
+         type="org.apache.catalina.deploy.ContextEnvironment">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="description"
+               description="The description of this environment entry"
+               type="java.lang.String"/>
+
+    <attribute name="name"
+               description="The name of this environment entry"
+               type="java.lang.String"/>
+      
+    <attribute name="override"
+               description="Does this environment entry allow overrides by the
+               application deployment descriptor"
+               type="boolean"/>
+
+    <attribute name="type"
+               description="The type of this environment entry"
+               type="java.lang.String"/>
+
+    <attribute name="value"
+               description="The value of this environment entry"
+               type="java.lang.String"/>
+  </mbean>
+
+
+  <mbean         name="ContextResource"
+         className="org.apache.catalina.mbeans.ContextResourceMBean"
+         description="Representation of a resource reference for a web application"
+         domain="Catalina"
+         group="Resources"
+         type="org.apache.catalina.deploy.ContextResource">
+    
+    <attribute   name="auth"
+               description="The authorization requirement for this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="description"
+               description="The description of this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="name"
+               description="The name of this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="scope"
+               description="The sharing scope of this resource factory"
+               type="java.lang.String"/>
+      
+    <attribute   name="type"
+               description="The type of this environment entry"
+               type="java.lang.String"/>
+  </mbean>
+  
+  
+   <mbean         name="ContextResourceLink"
+          className="org.apache.catalina.mbeans.ContextResourceLinkMBean"
+          description="Representation of a resource link for a web application"
+          domain="Catalina"
+          group="Resources"
+          type="org.apache.catalina.deploy.ContextResourceLink">
+    
+    <attribute   name="global"
+               description="The global name of this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="name"
+               description="The name of this resource"
+               type="java.lang.String"/>
+      
+    <attribute   name="type"
+               description="The type of this resource"
+               type="java.lang.String"/>
+      
+  </mbean>
+  
+  <mbean         name="NamingResources"
+            className="org.apache.catalina.mbeans.NamingResourcesMBean"
+          description="Holds and manages the naming resources defined in the
+                       J2EE Enterprise Naming Context and their associated 
+                       JNDI context"
+               domain="Catalina"
+                group="Resources"
+                 type="org.apache.catalina.deploy.NamingResources">
+
+    <attribute   name="environments"
+          description="MBean Names of the set of defined environment entries
+                       for this web application"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="resources"
+          description="MBean Names of all the defined resource references
+                       for this application."
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="resourceLinks"
+          description="MBean Names of all the defined resource link references
+                       for this application."
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <operation   name="addEnvironment"
+          description="Add an environment entry for this web application"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="envName"
+          description="New environment entry name"
+                 type="java.lang.String"/>
+      <parameter name="type"
+          description="New environment entry type"
+                 type="java.lang.String"/>
+      <parameter name="value"
+          description="New environment entry value"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="addResource"
+          description="Add a resource reference for this web application"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="resourceName"
+          description="New resource reference name"
+                 type="java.lang.String"/>
+      <parameter name="type"
+          description="New resource reference type"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="addResourceLink"
+          description="Add a resource link reference for this web application"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="resourceLinkName"
+          description="New resource reference name"
+                 type="java.lang.String"/>
+      <parameter name="type"
+          description="New resource reference type"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeEnvironment"
+          description="Remove any environment entry with the specified name"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="envName"
+          description="Name of the environment entry to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeResource"
+          description="Remove any resource reference with the specified name"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="resourceName"
+          description="Name of the resource reference to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeResourceLink"
+          description="Remove any resource link reference with the specified name"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="resourceLinkName"
+          description="Name of the resource reference to remove"
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/fileupload/.gitkeep_empty_dir b/appserver/web/web-core/src/main/resources/org/apache/catalina/fileupload/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/fileupload/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/filters/.gitkeep_empty_dir b/appserver/web/web-core/src/main/resources/org/apache/catalina/filters/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/filters/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/loader/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/loader/mbeans-descriptors.xml
new file mode 100644
index 0000000..eb1ea9e
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/loader/mbeans-descriptors.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean         name="WebappLoader"
+          description="Classloader implementation which is specialized for
+                       handling web applications"
+               domain="Catalina"
+                group="Loader"
+                 type="org.apache.catalina.loader.WebappLoader">
+                 
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute   name="delegate"
+          description="The 'follow standard delegation model' flag that will be
+                       used to configure our ClassLoader"
+                 type="boolean"/>
+
+    <attribute   name="reloadable"
+          description="The reloadable flag for this Loader"
+                 type="boolean"/>
+
+    <attribute   name="repositories"
+          description="Extra repositories managed by this loader"
+                 type="[Ljava.lang.String;"/>
+
+    <attribute   name="repositoriesString"
+          description="Extra repositories managed by this loader"
+             writeable="false" 
+                 type="java.lang.String"/>
+
+    <attribute   name="loaderRepositories"
+          description="Repositories set in the real loader"
+                 type="[Ljava.lang.String;"
+            writeable="false" />
+
+    <attribute   name="loaderRepositoriesString"
+          description="Repositories set in the real loader"
+                 type="java.lang.String"
+             writeable="false" />
+
+    <operation   name="toString"
+          description="Info about the loader"
+               impact="INFO"
+           returnType="String">
+    </operation>
+  </mbean>
+
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/logger/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/logger/mbeans-descriptors.xml
new file mode 100644
index 0000000..c0897f6
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/logger/mbeans-descriptors.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean name="FileLogger"
+         description="Implementation of Logger that appends log messages to a file"
+         domain="Catalina"
+         group="Logger"
+         type="org.apache.catalina.logger.FileLogger">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute name="directory"
+               description="The directory in which log files are created"
+               type="java.lang.String"/>
+      
+    <attribute name="prefix"
+               description="The prefix that is added to log file filenames"
+               type="java.lang.String"/>
+      
+    <attribute name="suffix"
+               description="The suffix that is added to log file filenames"
+               type="java.lang.String"/>
+      
+    <attribute name="timestamp"
+               description="Should logged messages be date/time stamped?"
+               type="boolean"/>
+      
+    <attribute   name="verbosity"
+               description="The verbosity level for above which log messages may be
+               filtered"
+               type="int"/>
+  </mbean>
+
+  <mbean name="SystemErrLogger"
+         description="Simple implementation of Logger that writes to  System.err"
+         domain="Catalina"
+         group="Logger"
+         type="org.apache.catalina.logger.SystemErrLogger">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute   name="verbosity"
+               description="The verbosity level for above which log messages may be filtered"
+               type="int"/>
+    </mbean>
+  
+  
+  <mbean name="SystemOutLogger"
+         description="Simple implementation of Logger that writes to System.out"
+         domain="Catalina"
+         group="Logger"
+         type="org.apache.catalina.logger.SystemOutLogger">
+    
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+      
+    <attribute   name="verbosity"
+               description="The verbosity level for above which log messages may be filtered"
+               type="int"/>
+  </mbean>
+  
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/mbeans-descriptors.xml
new file mode 100644
index 0000000..65414ed
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/mbeans-descriptors.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean name="Group"
+         className="org.apache.catalina.mbeans.GroupMBean"
+          description="Group from a user database"
+               domain="Users"
+                group="Group"
+                 type="org.apache.catalina.Group">
+
+    <attribute   name="description"
+          description="Description of this group"
+                 type="java.lang.String"/>
+
+    <attribute   name="groupname"
+          description="Group name of this group"
+                 type="java.lang.String"/>
+
+    <attribute   name="roles"
+          description="MBean Names of roles for this group"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="users"
+          description="MBean Names of user members of this group"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <operation   name="addRole"
+          description="Add a new authorized role for this group"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="role"
+          description="Role to be added"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRole"
+          description="Remove an old authorized role for this group"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="role"
+          description="Role to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRoles"
+          description="Remove all authorized roles for this group"
+               impact="ACTION"
+           returnType="void">
+    </operation>
+
+  </mbean>
+
+  <mbean         name="Role"
+            className="org.apache.catalina.mbeans.RoleMBean"
+          description="Security role from a user database"
+               domain="Users"
+                group="Role"
+                 type="org.apache.catalina.Role">
+
+    <attribute   name="description"
+          description="Description of this role"
+                 type="java.lang.String"/>
+
+    <attribute   name="rolename"
+          description="Role name of this role"
+                 type="java.lang.String"/>
+
+  </mbean>
+
+  <mbean         name="User"
+            className="org.apache.catalina.mbeans.UserMBean"
+          description="User from a user database"
+               domain="Users"
+                group="User"
+                 type="org.apache.catalina.User">
+
+    <attribute   name="fullName"
+          description="Full name of this user"
+                 type="java.lang.String"/>
+
+    <attribute   name="groups"
+          description="MBean Names of groups this user is a member of"
+                 type="[Ljava.lang.String;"/>
+
+    <attribute   name="password"
+          description="Password of this user"
+                 type="java.lang.String"/>
+
+    <attribute   name="roles"
+          description="MBean Names of roles for this user"
+                 type="[Ljava.lang.String;"
+            writeable="false"/>
+
+    <attribute   name="username"
+          description="User name of this user"
+                 type="java.lang.String"/>
+
+    <operation   name="addGroup"
+          description="Add a new group membership for this user"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="groupname"
+          description="Group name of the new group"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="addRole"
+          description="Add a new authorized role for this user"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="role"
+          description="Role to be added"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeGroup"
+          description="Remove an old group membership for this user"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="groupname"
+          description="Group name of the old group"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeGroups"
+          description="Remove all group memberships for this user"
+               impact="ACTION"
+           returnType="void">
+    </operation>
+
+    <operation   name="removeRole"
+          description="Remove an old authorized role for this user"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="role"
+          description="Role to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRoles"
+          description="Remove all authorized roles for this user"
+               impact="ACTION"
+           returnType="void">
+    </operation>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/mbeans/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/mbeans/mbeans-descriptors.xml
new file mode 100644
index 0000000..34f185b
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/mbeans/mbeans-descriptors.xml
@@ -0,0 +1,338 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+
+  <mbean         name="MBeanFactory"
+            className="org.apache.catalina.mbeans.MBeanFactory"
+          description="Factory for MBeans and corresponding components"
+               domain="Catalina">
+
+    <!-- IMPLEMENTATION NOTE - all of the createXxxxx methods create a new  -->
+    <!-- component and attach it to Catalina's component tree.  The return  -->
+    <!-- value is the object name of the corresponding MBean for the new    -->
+    <!-- component.                                                         -->
+
+    <operation   name="createAccessLoggerValve"
+          description="Create a new AccessLoggerValve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createDefaultContext"
+          description="Create a new DefaultContext"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createFileLogger"
+          description="Create a new FileLogger"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createJDBCRealm"
+          description="Create a new JDBC Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createJNDIRealm"
+          description="Create a new JNDI Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createMemoryRealm"
+          description="Create a new Memory Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createRemoteAddrValve"
+          description="Create a new Remote Address Filter Valve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createRemoteHostValve"
+          description="Create a new Remote Host Filter Valve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createRequestDumperValve"
+          description="Create a new Request Dumper Valve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createSingleSignOn"
+          description="Create a new Single Sign On Valve"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardContext"
+          description="Create a new StandardContext"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="path"
+          description="The context path for this Context"
+                 type="java.lang.String"/>
+      <parameter name="docBase"
+          description="Document base directory (or WAR) for ths Context"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardEngine"
+          description="Create a new StandardEngine"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="name"
+          description="Unique name of this Engine"
+                 type="java.lang.String"/>
+      <parameter name="defaultHost"
+          description="Default host name for this Engine"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardEngineService"
+          description="Create a new StandardEngine and StandardService"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent Service"
+                 type="java.lang.String"/>
+      <parameter name="engineName"
+          description="Unique name of this Engine"
+                 type="java.lang.String"/>
+      <parameter name="defaultHost"
+          description="Default host name for this Engine"
+                 type="java.lang.String"/>
+      <parameter name="serviceName"
+          description="Unique name of this Service"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardHost"
+          description="Create a new StandardHost"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="name"
+          description="Unique name of this Host"
+                 type="java.lang.String"/>
+      <parameter name="appBase"
+          description="Application base directory for this Host"
+                 type="java.lang.String"/>
+      <parameter name="unpackWARs"
+          description="Should we unpack WARs when auto-deploying?"
+                 type="boolean"/>
+    </operation>
+
+    <operation   name="createStandardManager"
+          description="Create a new StandardManager"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createStandardService"
+          description="Create a new StandardService"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="name"
+          description="Unique name of this Service"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createSystemErrLogger"
+          description="Create a new System Error Logger"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createSystemOutLogger"
+          description="Create a new System Output Logger"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createUserDatabaseRealm"
+          description="Create a new UserDatabase Realm"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+      <parameter name="resourceName"
+          description="Global JNDI resource name of our UserDatabase instance"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="createWebappLoader"
+          description="Create a new Web Application Loader"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="parent"
+          description="MBean Name of the associated parent component"
+                 type="java.lang.String"/>
+    </operation>
+
+    <!-- IMPLEMENTATION NOTE - all of the removeXxxxx methods cause the     -->
+    <!-- corresponding Catalina component (and any related child            -->
+    <!-- components to be stopped (if necessary) and removed, and the       -->
+    <!-- corresponding MBeans to be destroyed.                              -->
+
+    <operation   name="removeConnector"
+          description="Remove an existing Connector"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeContext"
+          description="Remove an existing Context"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeHost"
+          description="Remove an existing Host"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeLoader"
+          description="Remove an existing Loader"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeLogger"
+          description="Remove an existing Logger"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeManager"
+          description="Remove an existing Manager"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeRealm"
+          description="Remove an existing Realm"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeService"
+          description="Remove an existing Service"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="removeValve"
+          description="Remove an existing Valve"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="name"
+          description="MBean Name of the component to be removed"
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/realm/LocalStrings.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/realm/LocalStrings.properties
new file mode 100644
index 0000000..55b6b68
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/realm/LocalStrings.properties
@@ -0,0 +1,32 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+#
+# Message IDs reserved for this file: PWC2070-PWC2309
+#
+# $Id: LocalStrings.properties,v 1.3 2006/04/10 17:40:57 kmeduri Exp $
+
+# language 
+
+# package org.apache.catalina.realm
+
+#no ID on realmBase.forbidden as it is sent to client
+realmBase.forbidden=Access to the requested resource has been denied
+#no ID on realmBase.notAuthenticated as it is sent to client
+realmBase.notAuthenticated=Configuration error: Cannot perform access control without an authenticated principal
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/realm/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/realm/mbeans-descriptors.xml
new file mode 100644
index 0000000..e255520
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/realm/mbeans-descriptors.xml
@@ -0,0 +1,280 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+  <mbean         name="JAASRealm"
+          description="Implmentation of Realm that authenticates users via the
+                       Java Authentication and Authorization Service (JAAS)"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.JAASRealm">
+
+    <attribute   name="appName"
+          description="The application name passed to the JAAS LoginContext,
+                       which uses it to select the set of relevant
+                       LoginModules"
+                 type="java.lang.String"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute   name="digest"
+          description="Digest algorithm used in storing passwords in a
+                       non-plaintext format"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleClassNames"
+          description="Comma-delimited list of javax.security.Principal classes
+                       that represent security roles"
+                 type="java.lang.String"/>
+
+    <attribute   name="userClassNames"
+          description="Comma-delimited list of javax.security.Principal classes
+                       that represent individual users"
+                 type="java.lang.String"/>
+
+    <attribute   name="validate"
+          description="Should we validate client certificate chains when they
+                       are presented?"
+                 type="java.lang.String"/>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+
+  <mbean         name="JDBCRealm"
+          description="Implementation of Realm that works with any JDBC
+                       supported database"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.JDBCRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="connectionName"
+          description="The connection username to use when trying to connect to
+                       the database"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionPassword"
+          description="The connection URL to use when trying to connect to the
+                       database"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionURL"
+          description="The connection URL to use when trying to connect to the
+                       database"
+                 type="java.lang.String"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute   name="digest"
+          description="Digest algorithm used in storing passwords in a
+                       non-plaintext format"
+                 type="java.lang.String"/>
+
+    <attribute   name="driverName"
+          description="The JDBC driver to use"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleNameCol"
+          description="The column in the user role table that names a role"
+                 type="java.lang.String"/>
+
+    <attribute   name="userCredCol"
+          description="The column in the user table that holds the user's
+                       credentials"
+                 type="java.lang.String"/>
+
+    <attribute   name="userNameCol"
+          description="The column in the user table that holds the user's
+                       username"
+                 type="java.lang.String"/>
+
+    <attribute   name="userRoleTable"
+          description="The table that holds the relation between user's and
+                       roles"
+                 type="java.lang.String"/>
+
+    <attribute   name="userTable"
+          description="The table that holds user data"
+                 type="java.lang.String"/>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+  <mbean         name="JNDIRealm"
+          description="Implementation of Realm that works with a directory
+                       server accessed via the Java Naming and Directory
+                       Interface (JNDI) APIs"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.JNDIRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="connectionName"
+          description="The connection username for the server we will contact"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionPassword"
+          description="The connection password for the server we will contact"
+                 type="java.lang.String"/>
+
+    <attribute   name="connectionURL"
+          description="The connection URL for the server we will contact"
+                 type="java.lang.String"/>
+
+    <attribute   name="contextFactory"
+          description="The JNDI context factory for this Realm"
+                 type="java.lang.String"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute   name="digest"
+          description="Digest algorithm used in storing passwords in a
+                       non-plaintext format"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleBase"
+          description="The base element for role searches"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleName"
+          description="The name of the attribute containing roles held elsewhere"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleSearch"
+          description="The message format used to select roles for a user"
+                 type="java.lang.String"/>
+
+    <attribute   name="roleSubtree"
+          description="Should we search the entire subtree for matching
+                       memberships?"
+                 type="boolean"/>
+
+    <attribute   name="userBase"
+          description="The base element for user searches"
+                 type="java.lang.String"/>
+
+    <attribute   name="userPassword"
+          description="The attribute name used to retrieve the user password"
+                 type="java.lang.String"/>
+
+    <attribute   name="userPattern"
+          description="The message format used to select a user"
+                 type="java.lang.String"/>
+
+     <attribute   name="userRoleName"
+          description="The name of the attribute in the user's entry containing
+                       roles for that user"
+                 type="java.lang.String"/>
+
+   <attribute   name="userSearch"
+         description="The message format used to search for a user"
+                type="java.lang.String"/>
+
+    <attribute   name="userSubtree"
+          description="Should we search the entire subtree for matching
+                       users?"
+                 type="boolean"/>
+
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+  </mbean>
+
+  <mbean         name="MemoryRealm"
+          description="Simple implementation of Realm that reads an XML file to
+                       configure the valid users, passwords, and roles"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.MemoryRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute   name="pathname"
+          description="The pathname of the XML file containing our database
+                       information"
+                 type="java.lang.String"/>
+
+    <operation name="start" description="Start" impact="ACTION" returnType="void" />
+    <operation name="stop" description="Stop" impact="ACTION" returnType="void" />
+    <operation name="init" description="Init" impact="ACTION" returnType="void" />
+    <operation name="destroy" description="Destroy" impact="ACTION" returnType="void" />
+
+  </mbean>
+
+  <mbean         name="UserDatabaseRealm"
+          description="Realm connected to a UserDatabase as a global JNDI
+                       resource"
+               domain="Catalina"
+                group="Realm"
+                 type="org.apache.catalina.realm.UserDatabaseRealm">
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute   name="resourceName"
+          description="The global JNDI name of the UserDatabase resource to use"
+                 type="java.lang.String"/>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/security/LocalStrings.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/security/LocalStrings.properties
new file mode 100644
index 0000000..433790a
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/security/LocalStrings.properties
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+#
+# Message IDs reserved for this file: PWC2310-PWC2519
+#
+SecurityUtil.doAsPrivilege=PWC2310: An exception occurs when running the PrivilegedExceptionAction block.
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/security/LocalStrings_fr.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/security/LocalStrings_fr.properties
new file mode 100644
index 0000000..c6c1f71
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/security/LocalStrings_fr.properties
@@ -0,0 +1,21 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+SecurityUtil.doAsPrivilege=Une exception s''est produite lors de l''ex?cution du bloc "PrivilegedExceptionAction".
+
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/servlets/.gitkeep_empty_dir b/appserver/web/web-core/src/main/resources/org/apache/catalina/servlets/.gitkeep_empty_dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/servlets/.gitkeep_empty_dir
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/session/LocalStrings.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/session/LocalStrings.properties
new file mode 100644
index 0000000..dfb54dd
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/session/LocalStrings.properties
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+#
+# Message IDs reserved for this file: PWC2740-PWC3009
+#
+standardSession.notSerializable=PWC2785: Cannot serialize session attribute {0} for session {1}
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/session/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/session/mbeans-descriptors.xml
new file mode 100644
index 0000000..2598f5f
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/session/mbeans-descriptors.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean         name="StandardManager"
+          description="Standard implementation of the Manager interface"
+               domain="Catalina"
+                group="Manager"
+                 type="org.apache.catalina.session.StandardManager">
+
+    <attribute   name="algorithm"
+          description="The message digest algorithm to be used when generating
+                       session identifiers"
+                 type="java.lang.String"/>
+
+
+    <attribute   name="randomFile"
+          description="File source of random - /dev/urandom or a pipe"
+                 type="java.lang.String"/>
+
+    <attribute   name="className"
+          description="Fully qualified class name of the managed object"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="debug"
+          description="The debugging detail level for this component"
+                 type="int"/>
+
+    <attribute   name="distributable"
+          description="The distributable flag for Sessions created by this
+                       Manager"
+                 type="boolean"/>
+
+    <attribute   name="entropy"
+          description="A String initialization parameter used to increase the
+                       entropy of the initialization of our random number
+                       generator"
+                 type="java.lang.String"/>
+ 
+    <attribute   name="maxActiveSessions"
+          description="The maximum number of active Sessions allowed, or -1
+                       for no limit"
+                 type="int"/>
+
+    <attribute   name="maxInactiveInterval"
+          description="The default maximum inactive interval for Sessions
+                       created by this Manager"
+                 type="int"/>
+
+    <attribute   name="sessionIdLength"
+          description="The session id length (in bytes) of Sessions
+                       created by this Manager"
+                 type="int"/>
+
+    <attribute   name="name"
+          description="The descriptive name of this Manager implementation
+                       (for logging)"
+                 type="java.lang.String"
+            writeable="false"/>
+
+    <attribute   name="pathname"
+          description="Path name of the disk file in which active sessions"
+                 type="java.lang.String"/>
+
+    <attribute   name="activeSessions"
+          description="Number of active sessions at this moment"
+                 type="int" 
+            writeable="false"/>
+
+    <attribute   name="sessionCount"
+          description="Total number of sessions created by this manager"
+                 type="int" />
+
+    <attribute   name="maxActive"
+          description="Maximum number of active sessions so far"
+                 type="int" />
+
+    <attribute   name="sessionMaxAliveTimeSeconds"
+          description="Longest time an expired session had been alive"
+                 type="int" />
+
+    <attribute   name="sessionAverageAliveTimeSeconds"
+          description="Average time an expired session had been alive"
+                 type="int" />
+
+    <attribute   name="rejectedSessions"
+          description="Number of sessions we rejected due to maxActive beeing reached"
+                 type="int" />
+
+    <attribute   name="expiredSessions"
+          description="Number of sessions that expired ( doesn't include explicit invalidations )"
+                 type="int" />
+
+    <attribute   name="processingTime"
+          description="Time spent doing housekeeping and expiration"
+                 type="long" />
+
+    <attribute   name="duplicates"
+          description="Number of duplicated session ids generated"
+                 type="int" />
+
+    <operation   name="listSessionIds"
+          description="Return the list of active session ids"
+               impact="ACTION"
+           returnType="java.lang.String">
+    </operation>
+
+    <operation   name="getSessionAttribute"
+          description="Return a session attribute"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+      <parameter name="key"
+          description="key of the attribute"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="getSession"
+          description="Get information about a session"
+               impact="ACTION"
+           returnType="java.util.HashMap">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="expireSession"
+          description="Expire a session"
+               impact="ACTION"
+           returnType="void">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+    <operation   name="getLastAccessedTime"
+          description="Get the last access time"
+               impact="ACTION"
+           returnType="java.lang.String">
+      <parameter name="sessionId"
+          description="Id of the session"
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/LocalStrings.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/LocalStrings.properties
new file mode 100644
index 0000000..d65d8c2
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/LocalStrings.properties
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+#
+# Message IDs reserved for this file: PWC3010-PWC3289
+#
+contextConfig.applicationMissing=PWC3013: Missing application web.xml, using defaults only
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/catalina.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/catalina.properties
new file mode 100644
index 0000000..dcfa9dd
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/catalina.properties
@@ -0,0 +1,74 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+##
+# 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.
+package.access=
+#
+# 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.
+#
+package.definition=
+
+#
+#
+# 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 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}/common/classes,${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 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=${catalina.home}/server/classes,${catalina.home}/server/lib/*.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.home}/shared/classes,${catalina.home}/shared/lib/*.jar
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/mbeans-descriptors.xml
new file mode 100644
index 0000000..cf00829
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/startup/mbeans-descriptors.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean name="ContextConfig"
+         description="Startup event listener for a Context that configures the properties of that Context, and the associated defined servlets"
+         domain="Catalina"
+         group="Listener"
+         type="org.apache.catalina.startup.ContextConfig">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+  </mbean>
+
+  <mbean name="EngineConfig"
+         description="Startup event listener for a Engine that configures the properties of that Engine, and the associated defined contexts"
+         domain="Catalina"
+         group="Listener"
+         type="org.apache.catalina.startup.EngineConfig">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+      
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+  </mbean>
+
+
+  <mbean name="HostConfig"
+         description="Startup event listener for a Host that configures the properties of that Host, and the associated defined contexts"
+         domain="Catalina"
+         group="Listener"
+         type="org.apache.catalina.startup.HostConfig">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="configClass"
+               description="The Java class name of the Context configuration class we should use"
+               type="java.lang.String"/>
+
+    <attribute name="contextClass"
+               description="The Java class name of the Context implementation we should use"
+               type="java.lang.String"/>
+      
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/util/CharsetMapperDefault.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/util/CharsetMapperDefault.properties
new file mode 100644
index 0000000..879e911
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/util/CharsetMapperDefault.properties
@@ -0,0 +1,59 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+ar=ISO-8859-6
+be=ISO-8859-5
+bg=ISO-8859-5
+ca=ISO-8859-1
+cs=ISO-8859-2
+da=ISO-8859-1
+de=ISO-8859-1
+el=ISO-8859-7
+en=ISO-8859-1
+es=ISO-8859-1
+et=ISO-8859-1
+fi=ISO-8859-1
+fr=ISO-8859-1
+hr=ISO-8859-2
+hu=ISO-8859-2
+is=ISO-8859-1
+it=ISO-8859-1
+iw=ISO-8859-8
+ja=Shift_JIS
+ko=EUC-KR
+lt=ISO-8859-2
+lv=ISO-8859-2
+mk=ISO-8859-5
+nl=ISO-8859-1
+no=ISO-8859-1
+pl=ISO-8859-2
+pt=ISO-8859-1
+ro=ISO-8859-2
+ru=ISO-8859-5
+sh=ISO-8859-5
+sk=ISO-8859-2
+sl=ISO-8859-2
+sq=ISO-8859-2
+sr=ISO-8859-5
+sv=ISO-8859-1
+tr=ISO-8859-9
+uk=ISO-8859-5
+zh=GB2312
+zh_HK=Big5
+zh_TW=Big5
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/util/LocalStrings.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/util/LocalStrings.properties
new file mode 100644
index 0000000..0fbceae
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/util/LocalStrings.properties
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+#
+# Message IDs reserved for this file: PWC3500-PWC3709
+#
+#Default Messages Utilized by the ExtensionValidator
+SecurityUtil.doAsPrivilege=PWC3507: An exception occurs when running the PrivilegedExceptionAction block.
+
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/util/ServerInfo.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/util/ServerInfo.properties
new file mode 100644
index 0000000..e6044f7
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/util/ServerInfo.properties
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+
+server.info=Sun-Java-System/Application-Server-PE-8.0
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/valves/LocalStrings.properties b/appserver/web/web-core/src/main/resources/org/apache/catalina/valves/LocalStrings.properties
new file mode 100644
index 0000000..f9e2729
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/valves/LocalStrings.properties
@@ -0,0 +1,97 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+#
+# Message IDs reserved for this file: PWC3710-PWC3979
+#
+accessLogValve.alreadyStarted=PWC3710: Access Logger has already been started
+accessLogValve.notStarted=PWC3711: Access Logger has not yet been started
+
+# Error report valve
+errorReportValve.errorReport=Error report
+errorReportValve.statusHeader=HTTP Status {0} - {1}
+errorReportValve.exceptionReport=Exception report
+errorReportValve.statusReport=Status report
+errorReportValve.message=message
+errorReportValve.description=description
+errorReportValve.exception=exception
+errorReportValve.rootCause=root cause
+errorReportValve.note=note
+errorReportValve.rootCauseInLogs=The full stack traces of the exception and its root causes are available in the {0} logs.
+
+# HTTP status reports
+# All status codes registered with IANA can be found at
+# http://www.iana.org/assignments/http-status-codes/http-status-codes.xml
+# The list might be kept in sync with the one in
+# org/apache/tomcat/util/http/res/LocalStrings.properties.
+http.100=The client may continue.
+http.101=The server is switching protocols according to the "Upgrade" header.
+http.102=The server has accepted the complete request, but has not yet completed it.
+http.201=The request succeeded and a new resource has been created on the server.
+http.202=This request was accepted for processing, but has not been completed.
+http.203=The meta information presented by the client did not originate from the server.
+http.204=The request succeeded but there is no information to return.
+http.205=The client should reset the document view which caused this request to be sent.
+http.206=The server has fulfilled a partial GET request for this resource.
+http.207=Multiple status values have been returned.
+http.208=This collection binding was already reported.
+http.226=The response is a representation of the result of one or more instance-manipulations applied to the current instance.
+http.300=The requested resource corresponds to any one of a set of representations, each with its own specific location.
+http.301=The requested resource has moved permanently to a new location.
+http.302=The requested resource has moved temporarily to a new location.
+http.303=The response to this request can be found under a different URI.
+http.304=The requested resource is available and has not been modified.
+http.305=The requested resource must be accessed through the proxy given by the "Location" header.
+http.307=The requested resource resides temporarily under a different URI.
+http.308=The target resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs.
+http.400=The request sent by the client was syntactically incorrect.
+http.401=This request requires HTTP authentication.
+http.402=Payment is required for access to this resource.
+http.403=Access to the specified resource has been forbidden.
+http.404=The requested resource is not available.
+http.405=The specified HTTP method is not allowed for the requested resource.
+http.406=The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.
+http.407=The client must first authenticate itself with the proxy.
+http.408=The client did not produce a request within the time that the server was prepared to wait.
+http.409=The request could not be completed due to a conflict with the current state of the resource.
+http.410=The requested resource is no longer available, and no forwarding address is known.
+http.411=This request cannot be handled without a defined content length.
+http.412=A specified precondition has failed for this request.
+http.413=The request entity is larger than the server is willing or able to process.
+http.414=The server refused this request because the request URI was too long.
+http.415=The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
+http.416=The requested byte range cannot be satisfied.
+http.417=The expectation given in the "Expect" request header could not be fulfilled.
+http.422=The server understood the content type and syntax of the request but was unable to process the contained instructions.
+http.423=The source or destination resource of a method is locked.
+http.424=The method could not be performed on the resource because the requested action depended on another action and that action failed.
+http.426=The request can only be completed after a protocol upgrade.
+http.428=The request is required to be conditional.
+http.429=The user has sent too many requests in a given amount of time.
+http.431=The server refused this request because the request header fields are too large.
+http.500=The server encountered an internal error that prevented it from fulfilling this request.
+http.501=The server does not support the functionality needed to fulfill this request.
+http.502=This server received an invalid response from a server it consulted when acting as a proxy or gateway.
+http.503=The requested service is not currently available.
+http.504=The server received a timeout from an upstream server while acting as a gateway or proxy.
+http.505=The server does not support the requested HTTP protocol version.
+http.506=The chosen variant resource is configured to engage in transparent content negotiation itself, and is therefore not a proper end point in the negotiation process.
+http.507=The resource does not have sufficient space to record the state of the resource after execution of this method.
+http.508=The server terminated an operation because it encountered an infinite loop.
+http.510=The policy for accessing the resource has not been met in the request.
+http.511=The client needs to authenticate to gain network access.
diff --git a/appserver/web/web-core/src/main/resources/org/apache/catalina/valves/mbeans-descriptors.xml b/appserver/web/web-core/src/main/resources/org/apache/catalina/valves/mbeans-descriptors.xml
new file mode 100644
index 0000000..8bbd6b5
--- /dev/null
+++ b/appserver/web/web-core/src/main/resources/org/apache/catalina/valves/mbeans-descriptors.xml
@@ -0,0 +1,264 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<mbeans-descriptors>
+
+  <mbean name="AccessLogValve"
+         description="Valve that generates a web server access log"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.AccessLogValve">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="directory"
+               description="The directory in which log files are created"
+               type="java.lang.String"/>
+
+    <attribute   name="pattern"
+               description="The pattern used to format our access log lines"
+               type="java.lang.String"/>
+
+    <attribute name="prefix"
+               description="The prefix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="resolveHosts"
+               description="Resolve hosts"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="rotatable"
+               description="Flag to indicate automatic log rotation."
+               is="true"
+               type="boolean"/>
+
+    <attribute name="suffix"
+               description="The suffix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="condition"
+               description="The value to look for conditional logging."
+               type="java.lang.String"/>
+
+    <attribute name="fileDateFormat"
+               description="The format for the date date based log rotation."
+               type="java.lang.String"/>
+  </mbean>
+
+  <mbean name="ErrorReportValve"
+         description="Implementation of a Valve that outputs HTML error pages"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.ErrorReportValve">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+  </mbean>
+
+  <mbean name="ExtendedAccessLogValve"
+         description="Valve that generates a web server access log"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.ExtendedAccessLogValve">
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="directory"
+               description="The directory in which log files are created"
+               type="java.lang.String"/>
+
+    <attribute   name="pattern"
+               description="The pattern used to format our access log lines"
+               type="java.lang.String"/>
+
+    <attribute name="prefix"
+               description="The prefix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="rotatable"
+               description="Rotate log"
+               is="true"
+               type="boolean"/>
+
+    <attribute name="condition"
+               description="The value to look for conditional logging."
+               type="java.lang.String"/>
+
+    <attribute name="checkExists"
+               description="Check for file existence before each logging."
+               is="true"
+               type="boolean"/>
+
+    <attribute name="suffix"
+               description="The suffix that is added to log file filenames"
+               type="java.lang.String"/>
+
+    <attribute name="fileDateFormat"
+               description="The format for the date date based log rotation."
+               type="java.lang.String"/>
+
+    <operation name="rotate"
+               description="Move the existing log file to a new name"
+               impact="ACTION"
+               returnType="boolean">
+      <parameter name="newFileName"
+                 description="File name to move the log file to."
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+
+  <mbean name="RemoteAddrValve"
+         description="Concrete implementation of RequestFilterValve that  filters based on the string representation of the remote client's IP address"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RemoteAddrValve">
+
+    <attribute name="allow"
+               description="The comma-delimited set of allow expressions"
+               type="java.lang.String"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+    <attribute   name="deny"
+               description="The comma-delimited set of deny expressions"
+               type="java.lang.String"/>
+
+  </mbean>
+
+  <mbean name="RemoteHostValve"
+         description="Concrete implementation of RequestFilterValve that
+         filters based on the string representation of the remote
+         client's host name"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RemoteHostValve">
+
+    <attribute   name="allow"
+               description="The comma-delimited set of allow expressions"
+               type="java.lang.String"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+    <attribute   name="deny"
+               description="The comma-delimited set of deny expressions"
+               type="java.lang.String"/>
+
+  </mbean>
+
+  <mbean name="RequestDumperValve"
+         description="Implementation of a Valve that logs interesting contents from the specified Request and the corresponding Response"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RequestDumperValve">
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+  </mbean>
+
+  <mbean name="RequestListenerValve"
+         description="Valve that handles request initialization and destroy events"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RequestListenerValve">
+
+    <attribute   name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="containerName"
+               description="Object name of the container"
+               type="javax.management.ObjectName"/>
+
+    <attribute   name="debug"
+               description="The debugging detail level for this component"
+               type="int"/>
+
+  </mbean>
+
+</mbeans-descriptors>
diff --git a/appserver/web/web-core/src/test/java/org/apache/catalina/fileupload/MultipartStreamTest.java b/appserver/web/web-core/src/test/java/org/apache/catalina/fileupload/MultipartStreamTest.java
new file mode 100644
index 0000000..5c823ba
--- /dev/null
+++ b/appserver/web/web-core/src/test/java/org/apache/catalina/fileupload/MultipartStreamTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.apache.catalina.fileupload;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+
+
+/**
+ * Unit tests {@link org.apache.commons.fileupload.MultipartStream}.
+ * 
+ * @version $Id$
+ */
+public class MultipartStreamTest {
+    
+    static private final String BOUNDARY_TEXT = "myboundary";
+    
+    /**
+     * The Carriage Return ASCII character value.
+     */
+    public static final byte CR = 0x0D;
+
+
+    /**
+     * The Line Feed ASCII character value.
+     */
+    public static final byte LF = 0x0A;
+
+
+    /**
+     * The dash (-) ASCII character value.
+     */
+    public static final byte DASH = 0x2D;
+    
+    @Test
+    public void testThreeParamConstructor() throws Exception {
+        final String strData = "foobar";
+        final byte[] contents = strData.getBytes();
+        InputStream input = new ByteArrayInputStream(contents);
+        byte[] boundary = BOUNDARY_TEXT.getBytes();
+        byte[] BOUNDARY_PREFIX = {CR, LF, DASH, DASH};
+        int iBufSize =
+                boundary.length + BOUNDARY_PREFIX.length + 1;
+        MultipartStream ms = new MultipartStream(
+                input,
+                boundary,
+                iBufSize,
+                new MultipartStream.ProgressNotifier(null, contents.length));
+        assertNotNull(ms);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testSmallBuffer() throws Exception {
+        final String strData = "foobar";
+        final byte[] contents = strData.getBytes();
+        InputStream input = new ByteArrayInputStream(contents);
+        byte[] boundary = BOUNDARY_TEXT.getBytes();
+        int iBufSize = 1;
+        @SuppressWarnings("unused")
+        MultipartStream ms = new MultipartStream(
+                input,
+                boundary,
+                iBufSize,
+                new MultipartStream.ProgressNotifier(null, contents.length));
+    }
+
+    @Test
+    public void testTwoParamConstructor() throws Exception {
+        final String strData = "foobar";
+        final byte[] contents = strData.getBytes();
+        InputStream input = new ByteArrayInputStream(contents);
+        byte[] boundary = BOUNDARY_TEXT.getBytes();
+        MultipartStream ms = new MultipartStream(
+                input,
+                boundary,
+                new MultipartStream.ProgressNotifier(null, contents.length));
+        assertNotNull(ms);
+    }
+    
+}