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><description>/<version></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><Context></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><jsp-file></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><security-constraint></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><description>/<version></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><description>/<version></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><jsp-config></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><jsp-config></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><description>/<version></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><description>/<version></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><jsp-config></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><url-pattern></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><url-pattern></code>s.
+ * See Bugzilla 34805, 43079 & 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><description>/<version></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><description>/<version></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><Context></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><Context></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><description>/<version></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><description>/<version></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><description>/<version></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><ejb-ref></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><env-entry></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><ejb-local-ref></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><resource-ref></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><ResourceLink></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><error-page></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><filter></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><filter-mapping></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><filter-mapping></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><login-config></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><message-destination></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><message-destination-ref></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><web-resource-collection></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><security-constraint></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 := <ignore><br>
+ * epilogue := <ignore><br>
+ * body := header-part CRLF body-part<br>
+ * header-part := 1*header CRLF<br>
+ * header := header-name ":" header-value<br>
+ * header-name := <printable ascii characters except ":"><br>
+ * header-value := <any ascii characters except CR & LF><br>
+ * body-data := <arbitrary data><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><description>/<version></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><description>/<version></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><description>/<version></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><web-app>/WEB-INF/web.xml</code>) to:
+ * </p>
+ * <p>
+ * <code>
+ * <web-app>/cgi-bin/*
+ * </code>
+ * </p>
+ * <p>
+ * then the following request:
+ * </p>
+ * <p>
+ * <code>
+ * http://localhost:8080/<web-app>/cgi-bin/dir1/script/pathinfo1
+ * </code>
+ * </p>
+ * <p>
+ * would result in the execution of the script
+ * </p>
+ * <p>
+ * <code>
+ * <web-app-root>/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><webapp>/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
+ * <my-webapp-root> 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>
+ * <servlet-mapping>
+ * <servlet-name>default</servlet-name>
+ * <url-pattern>/</url-pattern>
+ * </servlet-mapping>
+ * </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>
+ * <servlet-mapping>
+ * <servlet-name>default</servlet-name>
+ * <url-pattern>/static/*</url-pattern>
+ * </servlet-mapping>
+ * </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\"> \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(" ");
+ 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>
+ * <servlet><br/>
+ * <servlet-name>webdav</servlet-name><br/>
+ * <servlet-class>org.apache.catalina.servlets.WebdavServlet</servlet-class><br/>
+ * <init-param><br/>
+ * <param-name>debug</param-name><br/>
+ * <param-value>0</param-value><br/>
+ * </init-param><br/>
+ * <init-param><br/>
+ * <param-name>listings</param-name><br/>
+ * <param-value>true</param-value><br/>
+ * </init-param><br/>
+ * </servlet><br/>
+ * <servlet-mapping><br/>
+ * <servlet-name>webdav</servlet-name><br/>
+ * <url-pattern>/*</url-pattern><br/>
+ * </servlet-mapping>
+ * </code>
+ * <p/>
+ * This will enable read only access. To enable read-write access add:<br/>
+ * <code>
+ * <init-param><br/>
+ * <param-name>readonly</param-name><br/>
+ * <param-value>false</param-value><br/>
+ * </init-param><br/>
+ * </code>
+ * <p/>
+ * To make the content editable via a different URL, using the following
+ * mapping:<br/>
+ * <code>
+ * <servlet-mapping><br/>
+ * <servlet-name>webdav</servlet-name><br/>
+ * <url-pattern>/webdavedit/*</url-pattern><br/>
+ * </servlet-mapping>
+ * </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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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><description>/<version></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("<", "<");
+ val = val.replace(">", ">");
+ val = val.replace(""", "\"");
+ val = val.replace("&", "&");
+
+ 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. <!--#config errmsg="error?"--></li>
+<li><b>SsiEcho</b> - Implementation of the NCSA command Echo i.e. <!--#echo var="SERVER_NAME"--></li>
+<li><b>SsiExec</b> - Not implemented</li>
+<li><b>SsiFlastMod</b> - Implementation of the NCSA command flastmod i.e. <!--#flastmod virtual="file"--></li>
+<li><b>SsiFsize</b> - Implementation of the NCSA command fsize i.e. <!--#fsize file="file"--></li>
+<li><b>SsiInclude</b> - Implementation of the NCSA command Include i.e. <!--#config virtual="includefile"--></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><description>/<version></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("<");
+ break;
+ }
+ case '>': {
+ str.append(">");
+ break;
+ }
+ case '&': {
+ str.append("&");
+ break;
+ }
+ case '"': {
+ str.append(""");
+ 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 <kent@trl.ibm.co.jp>
+ */
+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>
+ * <Valve className="AccessLogDBValve"
+ * driverName="<i>your_jdbc_driver</i>"
+ * connectionURL="<i>your_jdbc_url</i>"
+ * pattern="combined" resolveHosts="false"
+ * />
+ * </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><Valve
+ * className="org.apache.catalina.valves.WebdavFixValve" /></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><c></code> element, nested inside a <code><b></code>
+ * element, which is nested inside an <code><a></code> element.</li>
+ * <li><em>Tail Match</em> - A pattern "*/a/b" matches a
+ * <code><b></code> element, nested inside an <code><a></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);
+ }
+
+}