[maven-release-plugin] copy for tag cocoon-servlet-service-impl-1.2.0
git-svn-id: https://svn.apache.org/repos/asf/cocoon/tags/cocoon-servlet-service@761938 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/LICENSE.txt b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/LICENSE.txt
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/LICENSE.txt
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/NOTICE.txt b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/NOTICE.txt
new file mode 100644
index 0000000..bb0f11b
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/NOTICE.txt
@@ -0,0 +1,5 @@
+Apache Cocoon
+Copyright 1999-2008 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/pom.xml b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/pom.xml
new file mode 100644
index 0000000..5f5837e
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/pom.xml
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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: pom.xml 638085 2008-03-17 22:01:25Z reinhard $ -->
+<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">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>cocoon</artifactId>
+ <groupId>org.apache.cocoon</groupId>
+ <version>8</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>cocoon-servlet-service-impl</artifactId>
+ <packaging>jar</packaging>
+ <version>1.2.0</version>
+ <name>Cocoon Servlet Service Implementation</name>
+ <description>
+ The servlet service framework makes it easy to use servlets as Spring components.
+ It also contains functionality so that a servlet service can call other servlet
+ services that it is connected to in the Spring configuration.
+ </description>
+ <url>http://cocoon.apache.org/${docs.m.servlet-service-impl.relPath}</url>
+
+ <properties>
+ <docs.name>${project.name}</docs.name>
+ <docs.version>${docs.m.servlet-service-impl.version}</docs.version>
+ <docs.urlRelativizer>../../../../../</docs.urlRelativizer>
+ </properties>
+
+ <distributionManagement>
+ <site>
+ <id>website</id>
+ <url>${docs.deploymentBaseUrl}/${docs.m.servlet-service-impl.relPath}</url>
+ </site>
+ </distributionManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cocoon</groupId>
+ <artifactId>cocoon-spring-configurator</artifactId>
+ <version>2.1.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <!-- Spring Framework -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjweaver</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cocoon</groupId>
+ <artifactId>cocoon-jnet</artifactId>
+ <version>1.1.0</version>
+ </dependency>
+ <!-- commons -->
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ </dependency>
+ <!-- test -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-release-plugin</artifactId>
+ <configuration>
+ <tagBase>https://svn.apache.org/repos/asf/cocoon/tags/cocoon-servlet-service/${project.artifactId}</tagBase>
+ </configuration>
+ </plugin>
+ </plugins>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ <resource>
+ <directory>.</directory>
+ <targetPath>META-INF</targetPath>
+ <includes>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ </includes>
+ </resource>
+ </resources>
+ </build>
+
+ <scm>
+ <connection>scm:svn:https://svn.apache.org/repos/asf/cocoon/tags/cocoon-servlet-service/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/cocoon/tags/cocoon-servlet-service/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0</developerConnection>
+ <url>https://svn.apache.org/repos/asf/cocoon/tags/cocoon-servlet-service/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0</url>
+ </scm>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>project-team</report>
+ <report>dependencies</report>
+ <report>license</report>
+ <report>summary</report>
+ <report>index</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>changes-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ </plugins>
+ </reporting>
+
+ <profiles>
+ <profile>
+ <id>daisy</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.daisycms</groupId>
+ <artifactId>daisy-maven-plugin</artifactId>
+ <configuration>
+ <navDocId>1413</navDocId>
+ <collection>cdocs-servlet-service-impl</collection>
+ <skipFirstNavigationDocumentLevel>false</skipFirstNavigationDocumentLevel>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/changes/changes.xml b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/changes/changes.xml
new file mode 100644
index 0000000..e0fbc79
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/changes/changes.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ - See http://maven.apache.org/plugins/maven-changes-plugin/usage.html
+ -
+ - $Id$
+-->
+<document>
+ <body>
+ <release version="1.2.0-SNAPSHOT" date="2008-??-??" description="unreleased">
+ <action dev="reinhard" type="add">
+ ServletUrlConnection correctly implements #getHeaderFields() instead of relying on the NOP implementation of HttpURLConnection.
+ </action>
+ <action dev="gkossakowski" type="fix">
+ Fixed NPE thrown by HttpServletResponseBufferingWrapper and ServletServiceResponse after resetBuffer() method was called.
+ </action>
+ <action dev="gkossakowski" type="update">
+ Java 5 as minimum requirement.
+ </action>
+ <action dev="gkossakowski" type="fix" issue="COCOON-2237">
+ Fixed HttpServletResponseBufferingWrapper implementation so it does not throw IllegalStateException
+ when resestBufferedResponse is being called.
+ </action>
+ <action dev="gkossakowski" type="fix" issue="COCOON-2236">
+ ServletFactoryBean will install JNet handlers before calling init() method
+ of Servlet that is being created. This should make SSF compatible with Cocoon Core 2.2.0 again.
+ </action>
+ </release>
+ <release version="1.1.0" date="2008-08-09" description="released">
+ <action dev="reinhard" type="fix">
+ The dependency on the Cocoon SourceResolver, that introduced a circular dependency with Cocoon 2.2, was
+ removed. The Cocoon SourceResolver was used to resolve URLs in the context-path attribute of a servlet
+ bean definition. After the change the Servlet-Service framework only relies on the default URL handling
+ of the JVM. In order to dynamically support different URLStreamHandlers, it is recommended to use
+ Cocoon JNet.
+ </action>
+ <action dev="reinhard" type="fix">
+ Fix the lastModified() method of the ServletConnection: make sure that there is an existing connection
+ before reading the Last-Modified header
+ </action>
+ <action dev="reinhard" type="add">
+ Add a ServletURLConnection, ServletURLStreamHandler and a ServletURLStreamHandlerFactory to provide
+ support for service: URLs.
+ </action>
+ <action dev="gkossakowski" type="fix" issue="COCOON-2150">
+ Fixed bug in ServletServiceContext that was responsible for errors on resetting response that was already committed.
+ Now the Servlet-Service framework buffers response but only if 404 status code has been set.
+ This allows to reset the response safely.
+ </action>
+ </release>
+ <release version="1.0.0" date="2008-04-07" description="unreleased">
+ <action dev="gkossakowski" type="update">
+ Made attributes 'mount-path' and 'context-path' of servlet-context tag required.
+ </action>
+ <action dev="reinhard" type="add">
+ The "+" character is not allowed at the end of connection names anymore. Starting
+ Cocoon will fail hard with a RuntimeException if this rule is broken. See
+ org.apache.cocoon.servletservice.spring.ServletDecorator.
+ </action>
+ <action dev="reinhard" type="add" issue="COCOON-2154">
+ Add support for absolute ServetConnections.
+ </action>
+ <action dev="reinhard" type="add" issue="COCOON-1831">
+ The ServletServiceResponse inherits the request attributes, request parameters and the session
+ from the caller.
+ </action>
+ <action dev="gkossakowski" type="add" issue="COCOON-2036" due-to="Alexander Klimetschek" due-to-email="alexander.klimetschek@googlemail.com">
+ Throw an exception when circular dependencies in servlet connections are detected.
+ </action>
+ <action dev="gkossakowski" type="fix">
+ DispatcherServlet does not throw an exception if it fails to find suitable servlet for processing incomming request.
+ It returns 404 error and puts information to log about the failure, instead.
+ </action>
+ </release>
+ <release version="1.0.0-RC1" date="2007-10-29" description="released">
+ <action dev="gkossakowski" type="add" issue="COCOON-2038">
+ Implemented true Object Oriented approach for handling servlet calls.
+ This change removes the need for explicit super calls.
+ </action>
+ <action dev="gkossakowski" type="fix" issue="COCOON-1939">
+ Fixed handling of multilevel inheritance that could cause stack overflow.
+ </action>
+ <action dev="gkossakowski" type="fix" issue="COCOON-2121">
+ Fixed bug in DispatcherServlet that caused servlet (blocks) mounted at "/" to be handled improperly.
+ </action>
+ </release>
+ </body>
+</document>
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallFrame.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallFrame.java
new file mode 100644
index 0000000..76b6d22
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallFrame.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.callstack;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Attributes in the call frame and destruction callbacks that should be
+ * executed when the call frame is left.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class CallFrame {
+ private Map attributes;
+ private Map destructionCallbacks;
+
+ public boolean hasAttribute(String name) {
+ return this.attributes != null && this.attributes.containsKey(name);
+ }
+
+ public Object getAttribute(String name) {
+ return this.attributes != null ? this.attributes.get(name) : null;
+ }
+
+ public void setAttribute(String name, Object value) {
+ if (this.attributes == null) {
+ this.attributes = new HashMap();
+ }
+
+ this.attributes.put(name, value);
+ }
+
+ public Object removeAttribute(String name) {
+ return this.attributes != null ? this.attributes.remove(name) : null;
+ }
+
+ public void registerDestructionCallback(String name, Runnable callback) {
+ if (this.destructionCallbacks == null) {
+ this.destructionCallbacks = new HashMap();
+ }
+
+ this.destructionCallbacks.put(name, callback);
+ }
+
+ void executeDestructionCallbacks() {
+ if (this.destructionCallbacks == null) {
+ return;
+ }
+
+ Iterator i = this.destructionCallbacks.values().iterator();
+ while (i.hasNext()) {
+ ((Runnable) i.next()).run();
+ }
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallScope.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallScope.java
new file mode 100644
index 0000000..a2e0076
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallScope.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.callstack;
+
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.Scope;
+
+/**
+ * Stack based scope implementation. It is based on the CallStack and
+ * an object is in scope when it is in the top frame of the stack.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class CallScope implements Scope {
+
+ public Object get(String name, ObjectFactory objectFactory) {
+ CallFrame frame = CallStack.getCurrentFrame();
+ Object scopedObject = frame.getAttribute(name);
+ if (scopedObject == null) {
+ scopedObject = objectFactory.getObject();
+ frame.setAttribute(name, scopedObject);
+ }
+
+ return scopedObject;
+ }
+
+ public Object remove(String name) {
+ CallFrame frame = CallStack.getCurrentFrame();
+ Object scopedObject = frame.getAttribute(name);
+ if (scopedObject != null) {
+ frame.removeAttribute(name);
+ }
+
+ return scopedObject;
+ }
+
+ public String getConversationId() {
+ // There is no conversation id concept for the call stack
+ return null;
+ }
+
+ public void registerDestructionCallback(String name, Runnable callback) {
+ CallStack.getCurrentFrame().registerDestructionCallback(name, callback);
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallStack.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallStack.java
new file mode 100644
index 0000000..f021099
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/CallStack.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.callstack;
+
+import java.util.Stack;
+
+/**
+ * Stack used for storing objects in the current call frame.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class CallStack {
+
+ /** The call stack */
+ private static final ThreadLocal callStack = new ThreadLocal();
+
+ /**
+ * This hook must be called each time a call frame is entered.
+ */
+ public static void enter() {
+ Stack stack = (Stack) callStack.get();
+ if (stack == null) {
+ stack = new Stack();
+ callStack.set(stack);
+ }
+
+ CallFrame info = new CallFrame();
+ stack.push(info);
+ }
+
+ /**
+ * This hook must be called each time a call frame is left.
+ *
+ * <p>It's the counterpart to the {@link #enter()}
+ * method.</p>
+ */
+ public static void leave() {
+ final Stack stack = (Stack) callStack.get();
+ CallFrame info = (CallFrame) stack.pop();
+ info.executeDestructionCallbacks();
+ }
+
+ /**
+ * Use this method for getting the current call frame
+ * @return a call frame
+ */
+ public static CallFrame getCurrentFrame() {
+ final Stack stack = (Stack) callStack.get();
+ if (stack != null && !stack.isEmpty()) {
+ return (CallFrame) stack.peek();
+ }
+
+ return null;
+ }
+
+ /**
+ * @return the size of the call stack
+ */
+ public static int size() {
+ final Stack stack = (Stack) callStack.get();
+ return stack != null ? stack.size() : 0;
+ }
+
+ /**
+ * Get the frame at the i:th position in the call stack
+ * @param i
+ * @return
+ */
+ public static CallFrame frameAt(int i) {
+ final Stack stack = (Stack) callStack.get();
+ return (CallFrame) (stack != null ? stack.elementAt(i) : null);
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/CallFrameHelper.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/CallFrameHelper.java
new file mode 100644
index 0000000..97d6b8d
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/CallFrameHelper.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.callstack.environment;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.cocoon.callstack.CallFrame;
+import org.apache.cocoon.callstack.CallStack;
+
+/**
+ * A set of constants and methods to access the content of the call frame.
+ *
+ * <p>
+ * The call frame is used to pass information about the Request, Response and
+ * Context of the calling environment to components used while the call frame
+ * is active.</p>
+ *
+ * @version $Id$
+ */
+public abstract class CallFrameHelper {
+
+ /** Key for the environment {@link HttpServletRequest} in the call frame. */
+ public final static String REQUEST_OBJECT = "request";
+
+ /** Key for the environment {@link HttpServletResponse} in the call frame. */
+ public final static String RESPONSE_OBJECT = "response";
+
+ /** Key for the environment {@link ServletContext} in the call frame. */
+ public final static String CONTEXT_OBJECT = "context";
+
+ public static final void setEnvironment(HttpServletRequest request, HttpServletResponse response, ServletContext context) {
+ CallFrame frame = CallStack.getCurrentFrame();
+ frame.setAttribute(REQUEST_OBJECT, request);
+ frame.setAttribute(RESPONSE_OBJECT, response);
+ frame.setAttribute(CONTEXT_OBJECT, context);
+ }
+
+ public static final HttpServletRequest getRequest() {
+ CallFrame currentCallFrame = CallStack.getCurrentFrame();
+
+ if(currentCallFrame == null) {
+ return null;
+ }
+
+ return (HttpServletRequest) currentCallFrame.getAttribute(REQUEST_OBJECT);
+ }
+
+ public static final void setRequest(HttpServletRequest request) {
+ CallStack.getCurrentFrame().setAttribute(REQUEST_OBJECT, request);
+ }
+
+ public static final HttpServletResponse getResponse() {
+ CallFrame currentCallFrame = CallStack.getCurrentFrame();
+
+ if(currentCallFrame == null) {
+ return null;
+ }
+
+ return (HttpServletResponse) currentCallFrame.getAttribute(RESPONSE_OBJECT);
+ }
+
+ public static final void setResponse(HttpServletResponse response) {
+ CallStack.getCurrentFrame().setAttribute(RESPONSE_OBJECT, response);
+ }
+
+ public static final ServletContext getContext() {
+ CallFrame currentCallFrame = CallStack.getCurrentFrame();
+
+ if(currentCallFrame == null) {
+ return null;
+ }
+
+ return (ServletContext) currentCallFrame.getAttribute(CONTEXT_OBJECT);
+ }
+
+ public static final void setContext(ServletContext context) {
+ CallStack.getCurrentFrame().setAttribute(CONTEXT_OBJECT, context);
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/HttpServletRequestFactoryBean.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/HttpServletRequestFactoryBean.java
new file mode 100644
index 0000000..b554f59
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/HttpServletRequestFactoryBean.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.callstack.environment;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * {@link FactoryBean} that exposes the {@link HttpServletRequest} in the
+ * current call frame. It will typically be used together with the call scope.
+ *
+ * @version $Id$
+ */
+public final class HttpServletRequestFactoryBean implements FactoryBean {
+
+ public Object getObject() throws Exception {
+ return CallFrameHelper.getRequest();
+ }
+
+ public Class getObjectType() {
+ return HttpServletRequest.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/HttpServletResponseFactoryBean.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/HttpServletResponseFactoryBean.java
new file mode 100644
index 0000000..7a1a932
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/HttpServletResponseFactoryBean.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.callstack.environment;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * {@link FactoryBean} that exposes the {@link HttpServletResponse} in the
+ * current call frame. It will typically be used together with the call scope.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public final class HttpServletResponseFactoryBean implements FactoryBean {
+
+ public Object getObject() throws Exception {
+ return CallFrameHelper.getResponse();
+ }
+
+ public Class getObjectType() {
+ return HttpServletResponse.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/ServletContextFactoryBean.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/ServletContextFactoryBean.java
new file mode 100644
index 0000000..44a3145
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/callstack/environment/ServletContextFactoryBean.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.callstack.environment;
+
+import javax.servlet.ServletContext;
+
+import org.apache.cocoon.servletservice.ServletServiceContext;
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * {@link FactoryBean} that exposes the {@link ServletContext} in the current call
+ * frame. It will typically be used together with the call scope.
+ *
+ * @version $Id$
+ */
+public final class ServletContextFactoryBean implements FactoryBean {
+
+
+ public Object getObject() throws Exception {
+ return CallFrameHelper.getContext();
+ }
+
+ public Class getObjectType() {
+ // A ServletServiceContext rather than a ServletContext is needed to make the
+ // Absolutizable interface available through the ScopedProxyFactoryBean
+ return ServletServiceContext.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletscope/ServletScope.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletscope/ServletScope.java
new file mode 100644
index 0000000..d94144b
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletscope/ServletScope.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletscope;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.Scope;
+
+
+/**
+ * Stack based scope implementation. It is based on the CallStack and
+ * an object is in scope when it is in the top frame of the stack.
+ *
+ * @version $Id: CallScope.java 562806 2007-08-05 02:26:41Z vgritsenko $
+ * @since 2.2
+ */
+public class ServletScope implements Scope {
+
+ static private String destructionCallbacksAttributeName = ServletScope.class.getName() + "/destructionCallbacks";
+
+ private ServletContext servletContext;
+
+ public void setServletContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#get(java.lang.String, org.springframework.beans.factory.ObjectFactory)
+ */
+ public Object get(String name, ObjectFactory objectFactory) {
+ Object scopedObject = servletContext.getAttribute(name);
+ if (scopedObject == null) {
+ scopedObject = objectFactory.getObject();
+ servletContext.setAttribute(name, scopedObject);
+ }
+
+ return scopedObject;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
+ */
+ public Object remove(String name) {
+ Object scopedObject = servletContext.getAttribute(name);
+ if (scopedObject != null) {
+ servletContext.removeAttribute(name);
+ }
+
+ return scopedObject;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#getConversationId()
+ */
+ public String getConversationId() {
+ // There is no conversation id concept for the call stack
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String, java.lang.Runnable)
+ */
+ public void registerDestructionCallback(String name, Runnable callback) {
+ Map destructionCallbacks = getDestructionCallbacks(servletContext);
+ destructionCallbacks.put(name, callback);
+ }
+
+ /**
+ * @param servletContext
+ * @return the destruction callbacks map that is stored as a attribute of servletContext
+ */
+ private static Map getDestructionCallbacks(ServletContext servletContext) {
+ Map destructionCallbacks = (Map)servletContext.getAttribute(destructionCallbacksAttributeName);
+ if (destructionCallbacks == null) {
+ destructionCallbacks = new HashMap();
+ servletContext.setAttribute(destructionCallbacksAttributeName, destructionCallbacks);
+ }
+ return destructionCallbacks;
+ }
+
+ /**
+ * Executes destruction callbacks of beans from servlet scope. This method should be called once the Servlet that the scope
+ * is tied to is being destroyed.
+ * @param servletContext
+ */
+ public static void executeDestructionCallbacks(ServletContext servletContext) {
+ Map destructionCallbacks = getDestructionCallbacks(servletContext);
+ Iterator i = destructionCallbacks.values().iterator();
+ while (i.hasNext()) {
+ ((Runnable) i.next()).run();
+ }
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/AbsoluteServletConnection.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/AbsoluteServletConnection.java
new file mode 100644
index 0000000..8a47bf8
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/AbsoluteServletConnection.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+
+import org.apache.cocoon.callstack.environment.CallFrameHelper;
+import org.apache.cocoon.servletservice.util.ServletServiceRequest;
+import org.apache.cocoon.servletservice.util.ServletServiceResponse;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Create a connection to a servlet service. In order to use it, the fully qualified service name must be available.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public final class AbsoluteServletConnection extends AbstractServletConnection {
+
+ public static String ABSOLUTE_SERVLET_SOURCE_POSTFIX = "+";
+
+ private Servlet servlet;
+
+ /**
+ * Create an absolute connection to a servlet service.
+ *
+ * @param serviceName The fully qualified service name (= the name of the Spring bean).
+ * @param path The requested path of the service.
+ * @param queryString The query parameters formatted as HTTP request query string.
+ */
+ public AbsoluteServletConnection(String serviceName, String path, String queryString) {
+ if (serviceName == null) {
+ throw new IllegalArgumentException("The serviceName parameter must be passed.");
+ }
+ this.context = CallStackHelper.getBaseServletContext();
+ final ApplicationContext applicationContext = WebApplicationContextUtils
+ .getRequiredWebApplicationContext(this.context);
+ try {
+ this.servlet = (Servlet) applicationContext.getBean(serviceName);
+ } catch (ClassCastException cce) {
+ throw new IllegalArgumentException("The service '" + serviceName + "' is not of type "
+ + Servlet.class.getName() + ".");
+ }
+ if (this.servlet == null) {
+ throw new IllegalArgumentException("The service '" + serviceName + "' does not exist.");
+ }
+
+ URI reqUri = null;
+ try {
+ this.uri = new URI(serviceName + ABSOLUTE_SERVLET_SOURCE_POSTFIX, null, path, queryString, null);
+ this.uri = new URI("servlet", this.uri.toASCIIString(), null);
+ reqUri = new URI("servlet", null, path, queryString, null);
+ } catch (URISyntaxException e) {
+ IllegalArgumentException iae = new IllegalArgumentException("Can't create a URI using the passed path '"
+ + path + "' and query string '" + queryString + "' values.");
+ iae.initCause(e);
+ throw iae;
+ }
+ this.request = new ServletServiceRequest(reqUri, CallFrameHelper.getRequest());
+ this.response = new ServletServiceResponse();
+ }
+
+ /**
+ * Perform the actual connect that invokes the servlet service.
+ */
+ protected void performConnect() throws ServletException, IOException {
+ try {
+ CallStackHelper.enterServlet(this.context, this.request, this.response);
+ this.servlet.service(this.request, this.response);
+ } finally {
+ CallStackHelper.leaveServlet();
+ }
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/Absolutizable.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/Absolutizable.java
new file mode 100644
index 0000000..36a56c6
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/Absolutizable.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * By this interface, a {@link ServletServiceContext} provides absolute information
+ * about a mounted servlet service.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public interface Absolutizable {
+
+ /**
+ * Takes the scheme specific part of a servlet service URI (the scheme is the
+ * responsibilty of the ServletSource) and resolve it with respect to the
+ * servlets mount point.
+ *
+ * @param uri relative uri
+ * @return absolutized uri
+ * @throws URISyntaxException
+ */
+ URI absolutizeURI(URI uri) throws URISyntaxException;
+
+ /**
+ * Get the fully qualified servlet service name of a connected service.
+ *
+ * @param connectionName
+ * @return The fully qualified servlet service name of a connected service.
+ */
+ String getServiceName(String connectionName);
+
+ /**
+ * Get the fully qualifed service name.
+ *
+ * @return The fully qualified servlet service name.
+ */
+ String getServiceName();
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/AbstractServletConnection.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/AbstractServletConnection.java
new file mode 100644
index 0000000..fb695ea
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/AbstractServletConnection.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.cocoon.servletservice.util.ServletServiceRequest;
+import org.apache.cocoon.servletservice.util.ServletServiceResponse;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @version $Id$
+ * @since 1.0.0
+ */
+public abstract class AbstractServletConnection implements ServletConnection {
+
+ /** By default we use the logger for this class. */
+ protected final Log logger = LogFactory.getLog(this.getClass());
+
+ /** Connection request */
+ protected ServletServiceRequest request;
+
+ /** Connection response */
+ protected ServletServiceResponse response;
+
+ /** The current block context */
+ protected ServletContext context;
+
+ /** If already connected */
+ protected boolean connected;
+
+ protected ByteArrayOutputStream requestBody;
+
+ protected InputStream responseBody;
+
+ protected URI uri;
+
+ public void connect() throws IOException, ServletException {
+ // if already connected, do nothing
+ if (this.connected) {
+ return;
+ }
+
+ this.request.setContext(this.context);
+
+ if (this.requestBody != null) {
+ this.request.setMethod("POST");
+ this.request.setInputStream(new ByteArrayInputStream(this.requestBody.toByteArray()));
+ }
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ this.response.setOutputStream(os);
+
+ try {
+ this.performConnect();
+ this.response.flushBuffer();
+ this.responseBody = new ByteArrayInputStream(os.toByteArray());
+ } finally {
+ os.close();
+ }
+
+ this.connected = true;
+ }
+
+ /**
+ * Access the servlet and fill the response object.
+ *
+ * @throws ServletException
+ * @throws IOException
+ */
+ protected abstract void performConnect() throws ServletException, IOException;
+
+ /**
+ * Return an <code>InputStream</code> object to read from the source.
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
+ public InputStream getInputStream() throws IOException, ServletException {
+ this.connect();
+ return this.responseBody;
+ }
+
+ public void setIfModifiedSince(long ifmodifiedsince) {
+ if (this.connected) {
+ throw new IllegalStateException("Already connected");
+ }
+
+ this.request.setDateHeader("If-Modified-Since", ifmodifiedsince);
+ }
+
+ public long getLastModified() {
+ return this.getDateHeader("Last-Modified", 0);
+ }
+
+ public String getContentType() {
+ return this.getHeaderField("Content-Type");
+ }
+
+ public long getDateHeader(String name, long defaultValue) {
+ try {
+ return this.response.getDateHeader(name);
+ } catch (Exception e) {
+ this.logger.warn("Exception while reading the response header '" + name + "'.");
+ }
+
+ return defaultValue;
+ }
+
+ public String getHeaderField(String name) {
+ try {
+ this.connect();
+ } catch (Exception e) {
+ this.logger.warn("Exception while reading the response header '" + name + "'.");
+ return null;
+ }
+
+ return this.response.getHeader(name);
+ }
+
+ public long getHeaderFieldDate(String name, long defaultValue) {
+ try {
+ return this.response.getDateHeader(name);
+ } catch (Exception e) {
+ this.logger.warn("Exception while reading the response header '" + name + "'.");
+ }
+
+ return defaultValue;
+ }
+
+ public Map getHeaders() {
+ try {
+ this.connect();
+ } catch (Exception e) {
+ this.logger.warn("Exception while reading the response headers.");
+ return null;
+ }
+
+ return this.response.getHeaders();
+ }
+
+ public int getResponseCode() throws IOException {
+ try {
+ this.connect();
+ } catch (ServletException e) {
+ throw new IOException("Could not get response status code");
+ }
+
+ return this.response.getStatus();
+ }
+
+ /**
+ * Returns an output stream that writes as POST to this connection.
+ *
+ * @return an output stream that writes as POST to this connection.
+ * @throws IllegalStateException - if already connected
+ */
+ public OutputStream getOutputStream() throws IllegalStateException {
+ if (this.connected) {
+ throw new IllegalStateException("You cannot write to the connection already connected.");
+ }
+
+ if (this.requestBody == null) {
+ this.requestBody = new ByteArrayOutputStream();
+ }
+ return this.requestBody;
+
+ }
+
+ public URI getURI() {
+ return this.uri;
+ }
+
+ /**
+ * A special exception indicating that there is no servlet context available.
+ */
+ protected static class NoServletContextAvailableException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public NoServletContextAvailableException(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/CallStackHelper.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/CallStackHelper.java
new file mode 100644
index 0000000..dbf8cff
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/CallStackHelper.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.cocoon.callstack.CallFrame;
+import org.apache.cocoon.callstack.CallStack;
+import org.apache.cocoon.callstack.environment.CallFrameHelper;
+
+/**
+ * Helper class used for geting hold on the current servlet service
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class CallStackHelper {
+
+ /** Key for a value determing wether a call frame contains a super call or not */
+ public final static String SUPER_CALL = "super";
+
+ /**
+ * This hook must be called each time a servlet service is entered.
+ *
+ * <p>This method should never raise an exception, except when the
+ * parameters are not set!</p>
+ *
+ * @throws ServletException if at least one of the parameters is null
+ */
+ public static void enterServlet(ServletContext context, HttpServletRequest request, HttpServletResponse response)
+ throws ServletException {
+ enterServlet(context, request, response, false);
+ }
+
+ /**
+ * This hook must be called each time a super servlet service is entered.
+ *
+ * <p>This method should never raise an exception, except when the
+ * parameters are not set!</p>
+ *
+ * @throws ServletException if at least one of the parameters is null
+ */
+ public static void enterSuperServlet(ServletContext context, HttpServletRequest request, HttpServletResponse response)
+ throws ServletException {
+ enterServlet(context, request, response, true);
+ }
+
+ private static void enterServlet(ServletContext context, HttpServletRequest request, HttpServletResponse response, boolean superCall)
+ throws ServletException {
+ if (null == context) throw new ServletException("The context is not set.");
+ if (null == request) throw new ServletException("The request is not set.");
+ if (null == response) throw new ServletException("The response is not set.");
+
+
+ CallStack.enter();
+ CallStack.getCurrentFrame().setAttribute(SUPER_CALL, Boolean.valueOf(superCall));
+ CallFrameHelper.setContext(context);
+ CallFrameHelper.setRequest(request);
+ CallFrameHelper.setResponse(response);
+ }
+
+ /**
+ * This hook must be called each time a servlet service is left.
+ *
+ * <p>It's the counterpart to the {@link #enterServlet} method.</p>
+ */
+ public static void leaveServlet() {
+ CallStack.leave();
+ }
+
+ /**
+ * Use this method for getting the context that should be used for
+ * resolving a polymorphic servlet protocol call.
+ *
+ * @return a servlet context
+ */
+ public static ServletContext getBaseServletContext() {
+ for (int i = CallStack.size() - 1; i >= 0; i--) {
+ CallFrame frame = CallStack.frameAt(i);
+ if (frame.hasAttribute(SUPER_CALL) && !((Boolean) frame.getAttribute(SUPER_CALL)).booleanValue()) {
+ return (ServletContext) frame.getAttribute(CallFrameHelper.CONTEXT_OBJECT);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Use this method for getting the context that should be used for
+ * resolving a servlet protocol call to a super servlet service.
+ *
+ * @return a servlet context
+ */
+ public static ServletContext getCurrentServletContext() {
+ return CallFrameHelper.getContext();
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/DispatcherServlet.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/DispatcherServlet.java
new file mode 100644
index 0000000..88ab98b
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/DispatcherServlet.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * A servlet that dispatch to managed sevlets from the context Spring container.
+ * It dispatch to servlets that has the property mountPath, and dispatches to the
+ * servlet with the longest prefix of the request pathInfo.
+ *
+ * This servlet will also initialize and destroy all the servlets that it finds
+ * from the context container. This means that there must only be one dispatcher
+ * servlet, otherwise the managed servlets will be initialized several times.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class DispatcherServlet extends HttpServlet {
+
+ /** By default we use the logger for this class. */
+ private final Log logger = LogFactory.getLog(getClass());
+
+ /**
+ * The startup date of the Spring application context used to setup the {@link #blockServletCollector}.
+ * TODO: Use a better way to reload {@link #blockServletCollector} when RCL is used, see COCOON-2076
+ */
+ private long applicationContextStartDate;
+
+ /** The servlet collector bean */
+ private Map blockServletCollector;
+
+ public void init() throws ServletException {
+ this.log("Block dispatcher was initialized successfully.");
+ }
+
+ protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
+ final Map mountableServlets = getBlockServletMap();
+ String path = req.getPathInfo();
+ if (path == null) {
+ path = "";
+ }
+
+ // find the servlet which mount path is the longest prefix of the path info
+ int index = path.length();
+ Servlet servlet = null;
+ while (servlet == null && index != -1) {
+ path = path.substring(0, index);
+ servlet = (Servlet) mountableServlets.get(path);
+ index = path.lastIndexOf('/');
+ }
+ //case when servlet is mounted at "/" must be handled separately
+ servlet = servlet == null ? (Servlet) mountableServlets.get("/") : servlet;
+ if (servlet == null) {
+ String message = "No block for " + req.getPathInfo();
+ res.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ this.logger.info(message);
+ return;
+ }
+
+ // Create a dynamic proxy class that overwrites the getServletPath and
+ // getPathInfo methods to provide reasonable values in the called servlet
+ // the dynamic proxy implements all interfaces of the original request
+ HttpServletRequest request = (HttpServletRequest) Proxy.newProxyInstance(
+ req.getClass().getClassLoader(),
+ getInterfaces(req.getClass()),
+ new DynamicProxyRequestHandler(req, path));
+
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("DispatcherServlet: service servlet=" + servlet +
+ " mountPath=" + path +
+ " servletPath=" + request.getServletPath() +
+ " pathInfo=" + request.getPathInfo());
+ }
+
+ servlet.service(request, res);
+ }
+
+ private void getInterfaces(Set interfaces, Class clazz) {
+ Class[] clazzInterfaces = clazz.getInterfaces();
+ for (int i = 0; i < clazzInterfaces.length; i++) {
+ //add all interfaces extended by this interface or directly
+ //implemented by this class
+ getInterfaces(interfaces, clazzInterfaces[i]);
+ }
+
+ // the superclazz is null if class is instanceof Object, is
+ // an interface, a primitive type or void
+ Class superclazz = clazz.getSuperclass();
+ if (superclazz != null) {
+ //add all interfaces of the superclass to the list
+ getInterfaces(interfaces, superclazz);
+ }
+
+ interfaces.addAll(Arrays.asList(clazzInterfaces));
+ }
+
+ private Class[] getInterfaces(final Class clazz) {
+ Set interfaces = new LinkedHashSet();
+ getInterfaces(interfaces, clazz);
+ return (Class[]) interfaces.toArray(new Class[interfaces.size()]);
+ }
+
+ public Map getBlockServletMap() {
+ ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
+ if (this.blockServletCollector == null || applicationContext.getStartupDate() != this.applicationContextStartDate) {
+ this.applicationContextStartDate = applicationContext.getStartupDate();
+ this.blockServletCollector = (Map) applicationContext.getBean("org.apache.cocoon.servletservice.spring.BlockServletMap");
+ }
+ return blockServletCollector;
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/DynamicProxyRequestHandler.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/DynamicProxyRequestHandler.java
new file mode 100644
index 0000000..70bdd99
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/DynamicProxyRequestHandler.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * A dynamic wrapper for servlet requests that overwrites the
+ * getContextPath, getServletPath and getPathInfo methods to
+ * reflect the mount path of the block servlet.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class DynamicProxyRequestHandler implements InvocationHandler {
+ private final HttpServletRequest wrapped;
+ private final String mountPath;
+
+ private static final Method getContextPathMethod;
+ private static final Method getServletPathMethod;
+ private static final Method getPathInfoMethod;
+
+ static {
+ getContextPathMethod = getHttpServletRequestMethod("getContextPath");
+ getServletPathMethod = getHttpServletRequestMethod("getServletPath");
+ getPathInfoMethod = getHttpServletRequestMethod("getPathInfo");
+ }
+
+ /**
+ * Helper method for getting methods of the HttpServletRequest interface
+ *
+ * @param name name of the method
+ * @return the method object
+ */
+ static private Method getHttpServletRequestMethod(String name) {
+ Class[] paramTypes = new Class[]{};
+ try {
+ return HttpServletRequest.class.getMethod(name, paramTypes);
+ } catch (SecurityException e) {
+ throw new RuntimeException("could not get method: " +
+ name + " from class: " + HttpServletRequest.class);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("could not get method: " +
+ name + " from class: " + HttpServletRequest.class);
+ }
+ }
+
+ /**
+ * Creates a new request wrapper from a specified proxied request and
+ * the mount path of the block servlet
+ * @param req the request to proxy
+ * @param mountPath the mount path of the servlet
+ */
+ public DynamicProxyRequestHandler(HttpServletRequest req, String mountPath) {
+ this.wrapped = req;
+ this.mountPath = mountPath;
+ }
+
+ /**
+ * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+ */
+ public Object invoke(Object proxy, Method method, Object[] arguments)
+ throws Throwable {
+ if (method.equals(getContextPathMethod)) {
+ return wrapped.getContextPath() + wrapped.getServletPath();
+ } else if (method.equals(getServletPathMethod)) {
+ return mountPath;
+ } else if (method.equals(getPathInfoMethod)) {
+ String pathInfo = wrapped.getPathInfo().substring(mountPath.length());
+ return pathInfo.length() == 0 ? null : pathInfo;
+ } else {
+ return method.invoke(wrapped, arguments);
+ }
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapper.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapper.java
new file mode 100644
index 0000000..05451dd
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapper.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * <p>THIS IS INTERNAL CLASS OF SERVLET SERVICE FRAMEWORK AND SHOULDN'T BE USED ELSEWHERE!</p>
+ *
+ * <p>This class works in two modes:</p>
+ * <ol>
+ * <li>If status code has been set to value different than <code>404</code> (<code>SC_NOT_FOUND</code>) then this class
+ * acts completely transparently by forwarding all method calls to wrapped response object.</li>
+ * <li>If status code has been set to <code>404</code> then this class acts like a buffer. It buffers all method calls
+ * that would commit wrapped response. Buffering of such calls is being performed in order to assure that wrapped
+ * response can be always reseted if needed. It's worth mentioning the fact that buffer for {@link OutputStream}
+ * returned by {@link #getOutputStream()} is limited to the size specified in
+ * {@link #BUFFER_LIMIT} field.</li>
+ *
+ * <p>Additionally, this class lets the access to statusCode code that has been set through the
+ * {@link #getStatusCode()} method.</p>
+ */
+class HttpServletResponseBufferingWrapper extends HttpServletResponseWrapper {
+
+ /**
+ * Limit for a buffer for output stream returned by {@link #getOutputStream()} method.
+ * This is a hard limit, if exceeded an exception is thrown.
+ */
+ static private int BUFFER_LIMIT = 1024 * 1024; //= 1MB
+ static private String ALREADY_COMMITTED_EXCEPTION = "The response has been already committed.";
+
+ private boolean bufferResponse;
+ private boolean committed;
+ private String message;
+ private int statusCode;
+ private boolean sendError;
+
+ private ForwardingOrLimitingServletOutputStream outputStream;
+ private PrintWriter printWriter;
+
+ public HttpServletResponseBufferingWrapper(HttpServletResponse response) {
+ super(response);
+ resetBufferedResponse();
+ }
+
+ public void addCookie(Cookie cookie) {
+ if (isCommitted())
+ return;
+ super.addCookie(cookie);
+ }
+
+ public void sendError(int sc) throws IOException {
+ if (isCommitted())
+ throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+ if (sc != SC_NOT_FOUND)
+ super.sendError(sc);
+ else {
+ bufferResponse = true;
+ committed = true;
+ sendError = true;
+ }
+ statusCode = sc;
+ }
+
+ public void sendError(int sc, String msg) throws IOException {
+ if (isCommitted())
+ throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+ if (sc != SC_NOT_FOUND)
+ super.sendError(sc, msg);
+ else {
+ bufferResponse = true;
+ committed = true;
+ message = msg;
+ sendError = true;
+ }
+ statusCode = sc;
+ }
+
+ public void sendRedirect(String location) throws IOException {
+ if (isCommitted())
+ throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+ super.sendRedirect(location);
+ statusCode = HttpServletResponse.SC_TEMPORARY_REDIRECT;
+ }
+
+ public void setDateHeader(String name, long date) {
+ if (isCommitted())
+ return;
+ super.setDateHeader(name, date);
+ }
+
+ public void addDateHeader(String name, long date) {
+ if (isCommitted())
+ return;
+ super.addDateHeader(name, date);
+ }
+
+ public void setHeader(String name, String value) {
+ if (isCommitted())
+ return;
+ super.setHeader(name, value);
+ }
+
+ public void addHeader(String name, String value) {
+ if (isCommitted())
+ return;
+ super.addHeader(name, value);
+ }
+
+ public void setIntHeader(String name, int value) {
+ if (isCommitted())
+ return;
+ super.setIntHeader(name, value);
+ }
+
+ public void addIntHeader(String name, int value) {
+ if (isCommitted())
+ return;
+ super.addIntHeader(name, value);
+ }
+
+ public void setStatus(int sc) {
+ if (isCommitted())
+ return;
+ if (sc != SC_NOT_FOUND)
+ super.setStatus(sc);
+ else {
+ bufferResponse = true;
+ }
+ statusCode = sc;
+ }
+
+ public void setStatus(int sc, String sm) {
+ throw new UnsupportedOperationException(
+ "This method has been deprecated.");
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ if (outputStream == null)
+ this.outputStream = new ForwardingOrLimitingServletOutputStream(BUFFER_LIMIT, super.getOutputStream());
+ return outputStream;
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ if (this.outputStream != null)
+ throw new IllegalStateException(
+ "Output buffer has been already obtained. You can use either output buffer or print writer at one time.");
+ if (this.printWriter == null)
+ this.printWriter = new PrintWriter(new OutputStreamWriter(getOutputStream(), getCharacterEncoding()));
+ return printWriter;
+ }
+
+ public void flushBuffer() throws IOException {
+ if (!bufferResponse)
+ super.flushBuffer();
+ else
+ committed = true;
+ }
+
+ public boolean isCommitted() {
+ return committed || super.isCommitted();
+ }
+
+ public void resetBuffer() {
+ if (isCommitted())
+ throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+ if (!bufferResponse)
+ super.resetBuffer();
+ else if (outputStream != null)
+ outputStream.reset();
+ }
+
+ public void reset() {
+ if (isCommitted())
+ throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+ super.reset();
+ bufferResponse = false;
+ message = null;
+ }
+
+ public void flushBufferedResponse() throws IOException {
+ if (bufferResponse) {
+ if (sendError) {
+ try {
+ if (message != null)
+ super.sendError(SC_NOT_FOUND, message);
+ else
+ super.setStatus(SC_NOT_FOUND);
+ } catch (IOException e) {
+ // this should never occur
+ throw new IllegalStateException(
+ "FATAL ERROR! This situation should never occur because it's a job of "
+ + getClass().getName() + " class to "
+ + "prevent such situation.");
+ }
+ } else {
+ if (message != null)
+ super.setStatus(SC_NOT_FOUND, message);
+ else
+ super.setStatus(SC_NOT_FOUND);
+ }
+ }
+
+ //since all data goes through ForwardingOrLimitingServletOutputStream then flushing
+ //must be performed in *every* case now
+ if (this.printWriter != null) {
+ if (this.printWriter.checkError())
+ throw new IOException(
+ "Error occured while writing to printWriter.");
+ this.printWriter.close();
+ } else if (outputStream != null)
+ outputStream.writeTo(super.getOutputStream());
+
+ super.flushBuffer();
+ }
+
+ public void resetBufferedResponse() {
+ if (super.isCommitted())
+ throw new IllegalStateException(ALREADY_COMMITTED_EXCEPTION);
+ if (bufferResponse) {
+ message = null;
+ bufferResponse = false;
+ committed = false;
+ sendError = false;
+ }
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ /**
+ * Simple class acting like a {@link ServletOutputStream} but limiting (if it does not forward) number of bytes that
+ * can be written to the stream.
+ */
+ private class ForwardingOrLimitingServletOutputStream extends ServletOutputStream {
+
+ private Log log = LogFactory.getLog(getClass());
+
+ private int writeLimit;
+ private ByteArrayOutputStream outputStream;
+
+ private OutputStream forwardTo;
+
+ public ForwardingOrLimitingServletOutputStream(int writeLimit, OutputStream forwardTo) {
+ this.writeLimit = writeLimit;
+ this.forwardTo = forwardTo;
+ reset();
+ }
+
+ public void write(int b) throws IOException {
+ HttpServletResponseBufferingWrapper.this.committed = true;
+
+ if (isForwarding())
+ forwardTo.write(b);
+ else {
+ if (this.outputStream.size() < this.writeLimit)
+ this.outputStream.write(b);
+ else {
+ RuntimeException e = new RuntimeException(
+ "The buffering limit (" + writeLimit+ ") has been reached. If you encounter this exception it means that you to "
+ + "write a big response body for response that has error code set as status code. This is always a bad "
+ + "idea and in such case you should reconsider your design.");
+ log.fatal("Fatal error occured in writing to response", e);
+ throw e;
+ }
+ }
+ }
+
+ public void reset() {
+ this.outputStream = new ByteArrayOutputStream(writeLimit);
+ }
+
+ public void writeTo(OutputStream outputStream) throws IOException {
+ if (this.outputStream.size() > 0)
+ this.outputStream.writeTo(outputStream);
+ }
+
+ private boolean isForwarding() {
+ return !HttpServletResponseBufferingWrapper.this.bufferResponse;
+ }
+
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/Mountable.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/Mountable.java
new file mode 100644
index 0000000..4c34278
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/Mountable.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+/**
+ * Interface for servlet services that are mounted on a specific path.
+ * The inteface is not needed for using the {@link DispatcherServlet}
+ * as it mounts all servlet beans that has a property named mountPath.
+ * The main use for the interface is to b e able to create an AOP mixin
+ * with an ordinary Java proxy.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public interface Mountable {
+
+ /**
+ * The mount path of the servlet.
+ *
+ * @return servlet mount path
+ */
+ public String getMountPath();
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/NoCallingServletServiceRequestAvailableException.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/NoCallingServletServiceRequestAvailableException.java
new file mode 100644
index 0000000..9faa7c0
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/NoCallingServletServiceRequestAvailableException.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+public class NoCallingServletServiceRequestAvailableException extends RuntimeException {
+
+ public NoCallingServletServiceRequestAvailableException(String message) {
+ super(message);
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/RelativeServletConnection.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/RelativeServletConnection.java
new file mode 100644
index 0000000..b78ff62
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/RelativeServletConnection.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+
+import org.apache.cocoon.callstack.environment.CallFrameHelper;
+import org.apache.cocoon.servletservice.util.ServletServiceRequest;
+import org.apache.cocoon.servletservice.util.ServletServiceResponse;
+
+/**
+ * Implementation of a {@link ServletConnection} that gets its content by
+ * invoking the servlet service. It works based on the context of the current
+ * servlet context and its connections.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public final class RelativeServletConnection extends AbstractServletConnection {
+
+ private String connectionName;
+
+ public RelativeServletConnection(String connectionName, String path, String queryString) {
+ // path validation
+ if (path == null) {
+ throw new IllegalArgumentException("Path musn't be null.");
+ }
+ if (!path.startsWith("/")) {
+ throw new IllegalArgumentException(
+ "The path has to start with a slash '/' because it is always absolute within this servlet context.");
+ }
+
+ this.connectionName = connectionName;
+
+ // setup URI
+ URI reqUri;
+ try {
+ this.uri = new URI((connectionName != null ? connectionName : "local"), null, path, queryString, null);
+ this.uri = new URI("servlet", this.uri.toASCIIString(), null);
+ reqUri = new URI("servlet", null, path, queryString, null);
+ } catch (URISyntaxException e) {
+ String message = "Invalid relative servlet service URI created.";
+ this.logger.error(message, e);
+ throw new RuntimeException(message, e);
+ }
+
+ // lookup the servlet context
+ if (ServletServiceContext.SUPER.equals(this.connectionName)) {
+ // Super calls are resolved relative the current context and ordinary
+ // calls relative to the last non super call in the call chain
+ this.context = CallStackHelper.getCurrentServletContext();
+ } else {
+ this.context = CallStackHelper.getBaseServletContext();
+ }
+ if (this.context == null) {
+ throw new NoServletContextAvailableException(
+ "A servlet connection can only be used with an available servlet context. [" + this.uri
+ + "]");
+ }
+
+ // prepare request and response objects
+ this.request = new ServletServiceRequest(reqUri, CallFrameHelper.getRequest());
+ this.response = new ServletServiceResponse();
+
+ if(this.logger.isDebugEnabled()) {
+ this.logger.debug("Resolving relative servlet URI " + this.uri.toASCIIString());
+ }
+ }
+
+ /**
+ * Perform the actual connect that invokes the servlet service.
+ */
+ protected void performConnect() throws ServletException, IOException {
+ final RequestDispatcher dispatcher;
+ if (this.connectionName == null) {
+ dispatcher = this.context.getRequestDispatcher(null);
+ } else {
+ dispatcher = this.context.getNamedDispatcher(this.connectionName);
+ }
+
+ if (dispatcher == null) {
+ throw new ServletException("No dispatcher for connection" + this.connectionName);
+ }
+ dispatcher.forward(this.request, this.response);
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/ServletConnection.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/ServletConnection.java
new file mode 100644
index 0000000..8b6209d
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/ServletConnection.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URLConnection;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+
+/**
+ * <p>
+ * Contract to connect to a servlet service. The implementing classes differ from how they look up
+ * the servlet service.
+ * </p>
+ * <p>
+ * This class was designed similar to {@link URLConnection}.
+ * </p>
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public interface ServletConnection {
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~ Pre-connect methods ~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Set the last modification date if you want to make use of caching. This method has to be
+ * called before any of the other methods of this interface is invoked.
+ *
+ * @param ifmodifiedsince The timestamp of the last known resource.
+ */
+ void setIfModifiedSince(long ifmodifiedsince);
+
+ /**
+ * Get an output stream that writes as POST to this connection.
+ *
+ * @return An output stream that writes as POST to this connection.
+ */
+ OutputStream getOutputStream();
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~ connect method ~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Connect to the servlet service. Establishing a connection means that the service is executed
+ * and the response is available.
+ *
+ * @throws IOException The connection to the servlet service can't be established.
+ * @throws ServletException Any other problem when connecting to a servlet service.
+ */
+ void connect() throws IOException, ServletException;
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~ Post-connect methods ~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Read the input stream from the servlet service response.
+ *
+ * @return An input stream on the servlet service response.
+ * @throws IOException The connection to the servlet service can't be established.
+ * @throws ServletException Any other problem when connecting to a servlet service.
+ */
+ InputStream getInputStream() throws IOException, ServletException;
+
+ /**
+ *
+ * @return The HTTP status code returned by the servlet service.
+ * @throws IOException
+ */
+ int getResponseCode() throws IOException;
+
+ /**
+ * Get the last modification date of the servlet service.
+ *
+ * @return The last modification date of the servlet service.
+ */
+ long getLastModified();
+
+ /**
+ * Get the mime-type of the servlet-service.
+ *
+ * @return The mime-type of the servlet-service.
+ */
+ String getContentType();
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~ Idempotent methods ~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Get a URI representing this servlet connection.
+ *
+ * @return a URI representing this servlet connection.
+ */
+ URI getURI();
+
+ /**
+ * Get a response header with the given name.
+ *
+ * @param name The name of the header parameter
+ * @return The value of the header parameter
+ */
+ String getHeaderField(String name);
+
+ /**
+ * Get all response header fields.
+ *
+ * @return A {@link Map} that contains all response headers.
+ */
+ Map<String, String> getHeaders();
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/ServletServiceContext.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/ServletServiceContext.java
new file mode 100644
index 0000000..6b59b6a
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/ServletServiceContext.java
@@ -0,0 +1,508 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.cocoon.servletservice.util.ServletContextWrapper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class ServletServiceContext extends ServletContextWrapper implements Absolutizable {
+
+ public static final String SUPER = "super";
+
+ private final Log logger = LogFactory.getLog(ServletServiceContext.class);
+
+ private Map attributes = new Hashtable();
+ private Servlet servlet;
+ private String mountPath;
+ private String contextPath;
+ private URL contextPathURL;
+ private Map properties;
+ private Map connections;
+ private Map connectionServiceNames;
+ private String serviceName;
+
+ /*
+ * TODO inheritance of attributes from the parent context is only partly implemented: removeAttribute and
+ * getAttributeNames doesn't respect inheritance yet.
+ */
+ public Object getAttribute(String name) {
+ Object value = this.attributes.get(name);
+ return value != null ? value : super.getAttribute(name);
+ }
+
+ public void setAttribute(String name, Object value) {
+ this.attributes.put(name, value);
+ }
+
+ public void removeAttribute(String name) {
+ this.attributes.remove(name);
+ }
+
+ public Enumeration getAttributeNames() {
+ return Collections.enumeration(this.attributes.keySet());
+ }
+
+ /**
+ * @param map the attributes to set
+ */
+ public void setAttributes(Map map) {
+ if (map != null) {
+ this.attributes = map;
+ }
+ }
+
+ public URL getResource(String path) throws MalformedURLException {
+ if (path == null || !path.startsWith("/")) {
+ throw new MalformedURLException("The path must begin with a '/' and is interpreted "
+ + "as relative to the current context root.");
+ }
+
+ // lazy initialization of the base URL
+ synchronized (this) {
+ if (this.contextPathURL == null) {
+ this.contextPathURL = new URL(this.contextPath);
+ }
+ }
+
+ return new URL(this.contextPathURL, path.substring(1));
+ }
+
+ public String getRealPath(String path) {
+ // We better don't assume that blocks are unpacked
+ return null;
+ }
+
+ // FIXME, this should be defined in the config instead
+ public String getInitParameter(String name) {
+ if (this.properties == null) {
+ return null;
+ }
+
+ String value = (String) this.properties.get(name);
+ // Ask the super servlet for the property
+ if (value == null) {
+ ServletContext superContext = this.getNamedContext(SUPER);
+ if (superContext != null) {
+ value = superContext.getInitParameter(name);
+ }
+ }
+
+ // Ask the parent context
+ if (value == null) {
+ value = super.getInitParameter(name);
+ }
+
+ return value;
+ }
+
+ public Enumeration getInitParameterNames() {
+ Vector names = new Vector();
+
+ // add all names of the parent servlet context
+ Enumeration enumeration = super.getInitParameterNames();
+ while (enumeration.hasMoreElements()) {
+ names.add(enumeration.nextElement());
+ }
+
+ // add names of the super servlet
+ ServletContext superContext = this.getNamedContext(SUPER);
+ if (superContext != null) {
+ enumeration = superContext.getInitParameterNames();
+ while (enumeration.hasMoreElements()) {
+ names.add(enumeration.nextElement());
+ }
+ }
+
+ // add property names of this servlet
+ if (this.properties != null) {
+ names.addAll(this.properties.keySet());
+ }
+
+ return names.elements();
+ }
+
+ public InputStream getResourceAsStream(String path) {
+ try {
+ return this.getResource(path).openStream();
+ } catch (IOException e) {
+ this.logger.error("Can't open stream on " + path, e);
+ return null;
+ }
+ }
+
+ public ServletContext getContext(String uripath) {
+ return null;
+ }
+
+ public int getMajorVersion() {
+ return 2;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getMinorVersion()
+ */
+ public int getMinorVersion() {
+ return 3;
+ }
+
+ private Collection getDirectoryList(File file, String pathPrefix) {
+ ArrayList filenames = new ArrayList();
+
+ if (!file.isDirectory()) {
+ filenames.add("/" + file.toString().substring(pathPrefix.length() - 1));
+ return filenames;
+ }
+
+ File[] files = file.listFiles();
+
+ for (int i = 0; i < files.length; i++) {
+ File subfile = files[i];
+ filenames.addAll(this.getDirectoryList(subfile, pathPrefix));
+ }
+
+ return filenames;
+ }
+
+ public Set getResourcePaths(String path) {
+ if (path == null) {
+ return Collections.EMPTY_SET;
+ }
+
+ String pathPrefix;
+ if (this.contextPath.startsWith("file:")) {
+ pathPrefix = this.contextPath.substring("file:".length());
+ } else {
+ pathPrefix = this.contextPath;
+ }
+
+ path = pathPrefix + path;
+
+ File file = new File(path);
+
+ if (!file.exists()) {
+ return Collections.EMPTY_SET;
+ }
+
+ HashSet set = new HashSet();
+ set.addAll(this.getDirectoryList(file, pathPrefix));
+
+ return set;
+ }
+
+ public RequestDispatcher getRequestDispatcher(String path) {
+ PathDispatcher dispatcher = new PathDispatcher(path);
+ return dispatcher.exists() ? dispatcher : null;
+ }
+
+ public RequestDispatcher getNamedDispatcher(String name) {
+ NamedDispatcher dispatcher = new NamedDispatcher(name);
+ return dispatcher.exists() ? dispatcher : null;
+ }
+
+ public String getServerInfo() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getServletContextName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ // Servlet service specific methods
+
+ /**
+ * Set the servlet of the context
+ *
+ * @param servlet
+ */
+ public void setServlet(Servlet servlet) {
+ this.servlet = servlet;
+ }
+
+ /**
+ * Takes the scheme specific part of a servlet service URI (the scheme is the responsibilty of the ServletSource)
+ * and resolve it with respect to the servlets mount point.
+ */
+ public URI absolutizeURI(URI uri) throws URISyntaxException {
+ String servletServiceName = uri.getScheme();
+ ServletServiceContext servletServiceContext;
+ if (servletServiceName == null) {
+ // this servlet service
+ servletServiceContext = this;
+ } else {
+ // another servlet service
+ servletServiceContext = (ServletServiceContext) this.getNamedContext(servletServiceName);
+ if (servletServiceContext == null) {
+ throw new URISyntaxException(uri.toString(), "Unknown servlet service name");
+ }
+ }
+
+ String mountPath = servletServiceContext.getMountPath();
+ if (mountPath == null) {
+ throw new URISyntaxException(uri.toString(), "No mount point for this URI");
+ }
+ if (mountPath.endsWith("/")) {
+ mountPath = mountPath.substring(0, mountPath.length() - 1);
+ }
+
+ String absoluteURI = mountPath + uri.getSchemeSpecificPart();
+ if (this.logger.isInfoEnabled()) {
+ this.logger.info("Resolving " + uri.toString() + " to " + absoluteURI);
+ }
+
+ return new URI(absoluteURI);
+ }
+
+ public String getServiceName(String connectionName) {
+ return (String) this.connectionServiceNames.get(connectionName);
+ }
+
+ public String getServiceName() {
+ return this.serviceName;
+ }
+
+ /**
+ * Get the context of a servlet service with a given name.
+ */
+ // FIXME implement NPE handling
+ public ServletContext getNamedContext(String name) {
+ if (this.connections == null) {
+ return null;
+ }
+
+ Servlet servlet = (Servlet) this.connections.get(name);
+ if (servlet == null && !name.equals(SUPER)) {
+ Servlet _super = (Servlet) this.connections.get(SUPER);
+ if (_super != null) {
+ ServletContext c = _super.getServletConfig().getServletContext();
+ if (c instanceof ServletServiceContext) {
+ return ((ServletServiceContext) c).getNamedContext(name);
+ }
+
+ return null;
+ }
+ }
+ return servlet != null ? servlet.getServletConfig().getServletContext() : null;
+ }
+
+ /**
+ * @param mountPath The mountPath to set.
+ */
+ public void setMountPath(String mountPath) {
+ this.mountPath = mountPath;
+ }
+
+ /**
+ * Get the mount path of the servlet service context
+ */
+ public String getMountPath() {
+ return this.mountPath;
+ }
+
+ /**
+ * @param contextPath
+ */
+ public void setContextPath(String contextPath) {
+ if (contextPath == null) {
+ throw new IllegalArgumentException("Context path must not be null.");
+ }
+
+ if (contextPath.endsWith("/")) {
+ this.contextPath = contextPath;
+ } else {
+ this.contextPath = contextPath + "/";
+ }
+ }
+
+ /**
+ * @param properties The properties to set.
+ */
+ public void setInitParams(Map properties) {
+ this.properties = properties;
+ }
+
+ /**
+ * @param connections the connections to set
+ */
+ public void setConnections(Map connections) {
+ this.connections = connections;
+ }
+
+ /**
+ * @param connections the service names of the connections
+ */
+ public void setConnectionServiceNames(Map connectionServletServiceNames) {
+ this.connectionServiceNames = connectionServletServiceNames;
+ }
+
+ /**
+ * @param serviceName the name of the
+ */
+ public void setServiceName(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ protected class NamedDispatcher implements RequestDispatcher {
+
+ private final String servletServiceName;
+
+ private final boolean superCall;
+
+ private final ServletContext context;
+
+ public NamedDispatcher(String servletServiceName) {
+ this.servletServiceName = servletServiceName;
+ this.superCall = SUPER.equals(this.servletServiceName);
+
+ // Call to a named servlet service that exists in the current
+ // context
+ this.context = ServletServiceContext.this.getNamedContext(this.servletServiceName);
+ }
+
+ protected boolean exists() {
+ return this.context != null;
+ }
+
+ public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+ // Call to named servlet service
+
+ if (ServletServiceContext.this.logger.isInfoEnabled()) {
+ ServletServiceContext.this.logger
+ .info("Enter processing in servlet service " + this.servletServiceName);
+ }
+ RequestDispatcher dispatcher = this.context.getRequestDispatcher(((HttpServletRequest) request)
+ .getPathInfo());
+ if (dispatcher != null && dispatcher instanceof PathDispatcher) {
+ ((PathDispatcher) dispatcher).forward(request, response, this.superCall);
+ } else {
+ // Cannot happen
+ throw new IllegalStateException();
+ }
+ if (ServletServiceContext.this.logger.isInfoEnabled()) {
+ ServletServiceContext.this.logger.info("Leaving processing in servlet service "
+ + this.servletServiceName);
+ }
+ }
+
+ public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Limited functionality, assumes that there is at most one servlet in the context
+ */
+ private class PathDispatcher implements RequestDispatcher {
+
+ // Ignores path, as the assumed only servlet within the context is
+ // implicitly mounted on '/*'
+ private PathDispatcher(String path) {
+ }
+
+ private boolean exists() {
+ return ServletServiceContext.this.servlet != null;
+ }
+
+ public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+ this.forward(request, response, false);
+ }
+
+ protected void forward(ServletRequest request, ServletResponse response, boolean superCall)
+ throws ServletException, IOException {
+ try {
+ HttpServletResponseBufferingWrapper wrappedResponse = new HttpServletResponseBufferingWrapper(
+ (HttpServletResponse) response);
+ // FIXME: I think that Cocoon should always set status code on
+ // its own
+ wrappedResponse.setStatus(HttpServletResponse.SC_OK);
+ if (!superCall) {
+ // It is important to set the current context each time
+ // a new context is entered, this is used for the servlet
+ // protocol
+ CallStackHelper.enterServlet(ServletServiceContext.this, (HttpServletRequest) request,
+ wrappedResponse);
+ } else {
+ // A super servlet service should be called in the context
+ // of the called servlet service to get polymorphic calls
+ // resolved in the right way. We still need to register the
+ // current context for resolving super calls relative it.
+ CallStackHelper.enterSuperServlet(ServletServiceContext.this, (HttpServletRequest) request,
+ wrappedResponse);
+ }
+
+ ServletServiceContext.this.servlet.service(request, wrappedResponse);
+
+ int status = wrappedResponse.getStatusCode();
+ NamedDispatcher _super = (NamedDispatcher) ServletServiceContext.this.getNamedDispatcher(SUPER);
+ if (status == HttpServletResponse.SC_NOT_FOUND && _super != null) {
+ // if servlet returned NOT_FOUND (404) and has super servlet declared let's reset everything and ask
+ // the super servlet
+
+ // wrapping object resets underlying response as well
+ wrappedResponse.resetBufferedResponse();
+ // here we don't need to pass wrappedResponse object because it's not our concern to buffer response
+ // anymore
+ // this avoids many overlapping buffers
+ _super.forward(request, response);
+ } else {
+ wrappedResponse.flushBufferedResponse();
+ }
+ } finally {
+ CallStackHelper.leaveServlet();
+ }
+ }
+
+ public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/BaseServletContextFactoryBean.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/BaseServletContextFactoryBean.java
new file mode 100644
index 0000000..59737e1
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/BaseServletContextFactoryBean.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.spring;
+
+import org.apache.cocoon.servletservice.CallStackHelper;
+import org.apache.cocoon.servletservice.ServletServiceContext;
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * {@link FactoryBean} that exposes the {@link ServletServiceContext} in the current base call frame.
+ * It will typically be used together with the call scope for servlet services.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public final class BaseServletContextFactoryBean implements FactoryBean {
+
+ public Object getObject() throws Exception {
+ return CallStackHelper.getBaseServletContext();
+ }
+
+ public Class getObjectType() {
+ // A ServletServiceContext rather than a ServletContext is needed to make the
+ // Absolutizable interface available through the ScopedProxyFactoryBean
+ return ServletServiceContext.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletDecorator.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletDecorator.java
new file mode 100644
index 0000000..5745ad6
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletDecorator.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.spring;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * <p>
+ * This {@link BeanDefinitionDecorator} deals with the
+ * <code>http://cocoon.apache.org/schema/servlet</code> namespace which
+ * defines following elements: <code>context</code> : with optional attributes
+ * <code>mountPath</code> and <code>contextPath</code>. With optional sub
+ * elements <code>init-params", "context-params" and "connections".</p>
+ * <p>
+ * The actual creation of the servlet service (= a bean) is done by {@link ServletFactoryBean}.</p>
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class ServletDecorator implements BeanDefinitionDecorator {
+
+ public BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder holder, ParserContext ctx) {
+ String embeddedServletBeanName = registerEmbeddedServletBean(holder, ctx);
+ return createServletFactoryBeanDefinition((Element) source, holder, ctx, embeddedServletBeanName);
+ }
+
+ private String registerEmbeddedServletBean(BeanDefinitionHolder holder, ParserContext ctx) {
+ String beanName = holder.getBeanName() + "/embedded";
+
+ AbstractBeanDefinition definition = ((AbstractBeanDefinition) holder.getBeanDefinition());
+ ctx.getRegistry().registerBeanDefinition(beanName, definition);
+
+ return beanName;
+ }
+
+ private BeanDefinitionHolder createServletFactoryBeanDefinition(Element source, BeanDefinitionHolder holder,
+ ParserContext ctx, String embeddedServletBeanName) {
+ String ns = source.getNamespaceURI();
+ if (!source.hasAttribute("mount-path"))
+ throw new RuntimeException("The mount-path attribute is required.");
+ String mountPath = source.getAttribute("mount-path");
+ if (!source.hasAttribute("context-path"))
+ throw new RuntimeException("The context-path attribute is required.");
+ String contextPath = source.getAttribute("context-path");
+
+ Element initParamsElem = (Element) source.getElementsByTagNameNS(ns, "init-params").item(0);
+ Element contextParamsElem = (Element) source.getElementsByTagNameNS(ns, "context-params").item(0);
+ Element connectionsElem = (Element) source.getElementsByTagNameNS(ns, "connections").item(0);
+
+ BeanDefinitionBuilder servletFactoryDefBuilder = BeanDefinitionBuilder
+ .rootBeanDefinition(ServletFactoryBean.class);
+ servletFactoryDefBuilder.setSource(ctx.extractSource(source));
+ servletFactoryDefBuilder.addPropertyReference("embeddedServlet", embeddedServletBeanName);
+ servletFactoryDefBuilder.setInitMethodName("init");
+ servletFactoryDefBuilder.setDestroyMethodName("destroy");
+ servletFactoryDefBuilder.addPropertyValue("serviceName", holder.getBeanName());
+ //this dependency is needed because JNet must be called manually when init() method is called on servlet
+ //see COCOON-2236
+ servletFactoryDefBuilder.addPropertyReference("urlHandlerFactoryCollector", "org.apache.cocoon.jnet.URLHandlerFactoryCollector");
+
+ servletFactoryDefBuilder.addPropertyValue("mountPath", mountPath);
+ servletFactoryDefBuilder.addPropertyValue("contextPath", contextPath);
+ if (initParamsElem != null) {
+ Map initParams = ctx.getDelegate().parseMapElement(initParamsElem, null);
+ servletFactoryDefBuilder.addPropertyValue("initParams", initParams);
+ }
+ if (contextParamsElem != null) {
+ Map contextParams = ctx.getDelegate().parseMapElement(contextParamsElem, null);
+ servletFactoryDefBuilder.addPropertyValue("contextParams", contextParams);
+ }
+ if (connectionsElem != null) {
+ Map connections = ctx.getDelegate().parseMapElement(connectionsElem, null);
+ servletFactoryDefBuilder.addPropertyValue("connections", connections);
+ Map connectionNames = new HashMap();
+ for (Iterator it = connections.keySet().iterator(); it.hasNext();) {
+ TypedStringValue key = (TypedStringValue) it.next();
+ if (key.getValue().endsWith("+")) {
+ throw new InvalidBeanReferenceNameException(
+ "The key of a servlet connection mustn't use '+' as its last character. "
+ + "This is reserved for absolute references in servlet sources.");
+ }
+ RuntimeBeanReference beanNameReference = (RuntimeBeanReference) connections.get(key);
+ connectionNames.put(key.getValue(), beanNameReference.getBeanName());
+ }
+ servletFactoryDefBuilder.addPropertyValue("connectionServiceNames", connectionNames);
+ }
+
+ return new BeanDefinitionHolder(servletFactoryDefBuilder.getBeanDefinition(), holder.getBeanName());
+ }
+
+ private class InvalidBeanReferenceNameException extends RuntimeException {
+
+ public InvalidBeanReferenceNameException(String message) {
+ super(message);
+ }
+
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletFactoryBean.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletFactoryBean.java
new file mode 100644
index 0000000..bfb53c5
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletFactoryBean.java
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.spring;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.cocoon.jnet.URLHandlerFactoryCollector;
+import org.apache.cocoon.servletservice.Mountable;
+import org.apache.cocoon.servletservice.ServletServiceContext;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.aop.support.DefaultIntroductionAdvisor;
+import org.springframework.aop.support.DelegatingIntroductionInterceptor;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.FactoryBeanNotInitializedException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.web.context.ServletContextAware;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.GenericWebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class ServletFactoryBean implements FactoryBean, ApplicationContextAware,
+ ServletContextAware, BeanNameAware {
+
+ private ApplicationContext parentContainer;
+ private ServletContext servletContext;
+ private String beanName;
+
+ private Servlet embeddedServlet;
+
+ private String mountPath;
+ private String contextPath;
+
+ private Map initParams;
+ private Map contextParams;
+ private Map connections;
+ private Map connectionServiceNames;
+ private String serviceName;
+
+ private ServletServiceContext servletServiceContext;
+
+ private URLHandlerFactoryCollector urlHandlerFactoryCollector;
+
+ public ServletFactoryBean() {
+ }
+
+ public void init() throws Exception {
+ this.servletServiceContext = new ServletServiceContext();
+ this.servletServiceContext.setServletContext(this.servletContext);
+
+ this.servletServiceContext.setMountPath(this.mountPath);
+
+ this.servletServiceContext.setInitParams(this.initParams);
+ this.servletServiceContext.setAttributes(this.contextParams);
+ this.servletServiceContext.setConnections(this.connections);
+ this.servletServiceContext.setConnectionServiceNames(this.connectionServiceNames);
+ this.servletServiceContext.setServiceName(this.serviceName);
+
+ // create a sub container that resolves paths relative to the servlet
+ // service context rather than the parent context and make it available
+ // in a context attribute
+ if (this.parentContainer == null) {
+ this.parentContainer = WebApplicationContextUtils.getRequiredWebApplicationContext(this.servletContext);
+ }
+
+ // set the context path
+ this.servletServiceContext.setContextPath(this.contextPath);
+
+ // create servlet-service specific Spring web application context
+ GenericWebApplicationContext container = new GenericWebApplicationContext();
+ container.setParent(this.parentContainer);
+ container.setServletContext(this.servletServiceContext);
+ container.refresh();
+ this.servletServiceContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, container);
+
+ // create a servlet config based on the block servlet context
+ ServletConfig blockServletConfig =
+ new ServletConfig() {
+
+ public String getInitParameter(String name) {
+ return ServletFactoryBean.this.servletServiceContext.getInitParameter(name);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return ServletFactoryBean.this.servletServiceContext.getInitParameterNames();
+ }
+
+ public ServletContext getServletContext() {
+ return ServletFactoryBean.this.servletServiceContext;
+ }
+
+ public String getServletName() {
+ return ServletFactoryBean.this.beanName;
+ }
+ };
+
+ // create and initialize the embedded servlet
+ try {
+ //manual call to pushUrlHandlerFactories is needed due to issue COCOON-2236
+ urlHandlerFactoryCollector.pushUrlHandlerFactories();
+ this.embeddedServlet.init(blockServletConfig);
+ } finally {
+ urlHandlerFactoryCollector.popUrlHandlerFactories();
+ }
+
+ this.servletServiceContext.setServlet(this.embeddedServlet);
+ }
+
+ public void destroy() {
+ this.embeddedServlet.destroy();
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.parentContainer = applicationContext;
+ }
+
+ public void setServletContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ public void setBeanName(String beanName) {
+ this.beanName = beanName;
+ }
+
+ /**
+ * @param embeddedServlet the embeddedServlet to set
+ */
+ public void setEmbeddedServlet(Servlet embeddedServlet) {
+ this.embeddedServlet = embeddedServlet;
+ }
+
+ /**
+ * @param mountPath
+ */
+ public void setMountPath(String mountPath) {
+ this.mountPath = mountPath;
+ }
+
+ /**
+ * The path to the blocks resources relative to the servlet context URL,
+ * must start with an '/'.
+ * @param contextPath
+ */
+ public void setContextPath(String contextPath) {
+ this.contextPath = contextPath;
+ }
+
+ /**
+ * @param initParams
+ */
+ public void setInitParams(Map initParams) {
+ this.initParams = initParams;
+ }
+
+ /**
+ * @param contextParams the contextParams to set
+ */
+ public void setContextParams(Map contextParams) {
+ this.contextParams = contextParams;
+ }
+
+ /**
+ * @param connections
+ */
+ public void setConnections(Map connections) {
+ this.connections = connections;
+ }
+
+ /**
+ * @param connectionServiceNames
+ */
+ public void setConnectionServiceNames(Map connectionServiceNames) {
+ this.connectionServiceNames = connectionServiceNames;
+ }
+
+ public void setServiceName(String name) {
+ this.serviceName = name;
+ }
+
+ public Object getObject() throws Exception {
+ if (this.embeddedServlet == null) {
+ throw new FactoryBeanNotInitializedException("There might be a circular dependency inside the servlet connections.");
+ }
+ ProxyFactory proxyFactory = new ProxyFactory(this.embeddedServlet);
+ proxyFactory.addAdvice(new ServiceInterceptor());
+ if (this.mountPath != null) {
+ proxyFactory.addAdvisor(new MountableMixinAdvisor());
+ }
+ return proxyFactory.getProxy();
+ }
+
+ public Class getObjectType() {
+ if (this.embeddedServlet == null) {
+ //if embeddedServlet is null we don't know exactly what ObjectType this Factory will return but at least
+ //we are sure it is a class implementing Servlet interface so it's better to return it instead of null
+ return Servlet.class;
+ }
+
+ return this.embeddedServlet != null ? this.embeddedServlet.getClass() : null;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+ private class ServiceInterceptor implements MethodInterceptor {
+
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+ if ("service".equals(invocation.getMethod().getName())) {
+ Object[] arguments = invocation.getArguments();
+ HttpServletRequest request = (HttpServletRequest) arguments[0];
+ HttpServletResponse response = (HttpServletResponse) arguments[1];
+ RequestDispatcher dispatcher =
+ ServletFactoryBean.this.servletServiceContext.getRequestDispatcher(request.getPathInfo());
+ dispatcher.forward(request, response);
+ return null;
+ } else if ("init".equals(invocation.getMethod().getName())) {
+ // The embedded servlet is initialized by this factory bean, ignore other containers
+ return null;
+ } else if ("destroy".equals(invocation.getMethod().getName())) {
+ // The embedded servlet is destroyed up by this factory bean, ignore other containers
+ return null;
+ }
+
+ return invocation.proceed();
+ }
+ }
+
+ private class MountableMixin extends DelegatingIntroductionInterceptor
+ implements Mountable {
+
+ public String getMountPath() {
+ return ServletFactoryBean.this.mountPath;
+ }
+ }
+
+ private class MountableMixinAdvisor extends DefaultIntroductionAdvisor {
+
+ public MountableMixinAdvisor() {
+ super(new MountableMixin(), Mountable.class);
+ }
+ }
+
+ public void setUrlHandlerFactoryCollector(URLHandlerFactoryCollector urlHandlerFactoryCollector) {
+ this.urlHandlerFactoryCollector = urlHandlerFactoryCollector;
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletNamespaceHandler.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletNamespaceHandler.java
new file mode 100644
index 0000000..5cbe6a4
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/spring/ServletNamespaceHandler.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.spring;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+/**
+ * <p>
+ * Spring namespace handler for the Cocoon servlet namespace (<code>http://cocoon.apache.org/schema/servlet</code>).
+ * The {@link ServletDecorator} deals with the implementation details.
+ *
+ * @version $Id: ServletNamespaceHandler.java 562806 2007-08-05 02:26:41Z
+ * vgritsenko $
+ * @since 1.0.0
+ */
+public class ServletNamespaceHandler extends NamespaceHandlerSupport {
+
+ public void init() {
+ registerBeanDefinitionDecorator("context", new ServletDecorator());
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLConnection.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLConnection.java
new file mode 100644
index 0000000..b541da6
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLConnection.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cocoon.servletservice.url;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.servlet.ServletException;
+
+import org.apache.cocoon.servletservice.AbsoluteServletConnection;
+import org.apache.cocoon.servletservice.Absolutizable;
+import org.apache.cocoon.servletservice.CallStackHelper;
+import org.apache.cocoon.servletservice.NoCallingServletServiceRequestAvailableException;
+import org.apache.cocoon.servletservice.ServletConnection;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class ServletURLConnection extends HttpURLConnection {
+
+ private final ServletConnection servletConnection;
+
+ private final URL url;
+
+ private final Log logger = LogFactory.getLog(this.getClass());
+
+ protected ServletURLConnection(URL url) throws URISyntaxException {
+ super(url);
+
+ this.url = url;
+
+ URI locationUri = null;
+ String query = url.getQuery();
+ if (query != null) {
+ locationUri = new URI(url.getPath() + "?" + url.getQuery());
+ } else {
+ locationUri = new URI(url.getPath());
+ }
+
+ final String servletReference = locationUri.getScheme();
+ final Absolutizable absolutizable = (Absolutizable) CallStackHelper.getCurrentServletContext();
+ final String servletName;
+
+ // find out the type of the reference and create a service name
+ if (servletReference == null) {
+ // self-reference
+ if (absolutizable == null) {
+ throw new NoCallingServletServiceRequestAvailableException(
+ "A self-reference requires an active servlet request.");
+ }
+
+ servletName = absolutizable.getServiceName();
+ } else if (servletReference.endsWith(AbsoluteServletConnection.ABSOLUTE_SERVLET_SOURCE_POSTFIX)) {
+ // absolute reference
+ servletName = servletReference.substring(0, servletReference.length() - 1);
+ } else {
+ // relative reference
+ if (absolutizable == null) {
+ throw new NoCallingServletServiceRequestAvailableException(
+ "A relative servlet call requires an active servlet request.");
+ }
+
+ servletName = absolutizable.getServiceName(servletReference);
+ }
+
+ this.servletConnection = new AbsoluteServletConnection(servletName, locationUri.getRawPath(), locationUri
+ .getRawQuery());
+ this.servletConnection.setIfModifiedSince(0);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.URLConnection#connect()
+ */
+ @Override
+ public void connect() throws IOException {
+ try {
+ if (this.connected) {
+ return;
+ }
+
+ this.servletConnection.connect();
+
+ this.connected = true;
+ } catch (ServletException e) {
+ IOException ioException = new IOException("Can't connect to servlet URL " + this.url + ".");
+ ioException.initCause(e);
+ throw ioException;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.HttpURLConnection#getErrorStream()
+ */
+ @Override
+ public InputStream getErrorStream() {
+ if (!this.connected) {
+ return null;
+ }
+
+ try {
+ return this.servletConnection.getInputStream();
+ } catch (Exception e) {
+ this.logger.warn(e);
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.URLConnection#getInputStream()
+ */
+ @Override
+ public InputStream getInputStream() throws IOException {
+ try {
+ this.connect();
+
+ return this.servletConnection.getInputStream();
+ } catch (ServletException e) {
+ IOException ioException = new IOException("Can't read from servlet URL " + this.url + ".");
+ ioException.initCause(e);
+ throw ioException;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.URLConnection#getLastModified()
+ */
+ @Override
+ public long getLastModified() {
+ try {
+ this.connect();
+ } catch (IOException e) {
+ return 0;
+ }
+
+ return this.servletConnection.getLastModified();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.URLConnection#getOutputStream()
+ */
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ return this.servletConnection.getOutputStream();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.URLConnection#getContentType()
+ */
+ @Override
+ public String getContentType() {
+ return this.servletConnection.getContentType();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.HttpURLConnection#getResponseCode()
+ */
+ @Override
+ public int getResponseCode() {
+ try {
+ this.connect();
+
+ return this.servletConnection.getResponseCode();
+ } catch (IOException e) {
+ return 500;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.HttpURLConnection#disconnect()
+ */
+ @Override
+ public void disconnect() {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.HttpURLConnection#usingProxy()
+ */
+ @Override
+ public boolean usingProxy() {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.URLConnection#getHeaderFields()
+ */
+ @Override
+ public Map<String, List<String>> getHeaderFields() {
+ Map<String, List<String>> result = new HashMap<String, List<String>>();
+
+ Map<String, String> headers = this.servletConnection.getHeaders();
+ for (Entry<String, String> eachHeader : headers.entrySet()) {
+ List<String> values = new ArrayList<String>();
+ values.add(eachHeader.getValue());
+ result.put(eachHeader.getKey(), Collections.unmodifiableList(values));
+ }
+
+ return Collections.unmodifiableMap(result);
+ }
+}
\ No newline at end of file
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLStreamHandler.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLStreamHandler.java
new file mode 100644
index 0000000..bc86771
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLStreamHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cocoon.servletservice.url;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+public class ServletURLStreamHandler extends URLStreamHandler {
+
+ protected URLConnection openConnection(URL url) throws IOException {
+ try {
+ return new ServletURLConnection(url);
+ } catch (Throwable e) {
+ IOException ioException = new IOException("Can't create ServletURLConnection " + url + ".");
+ ioException.initCause(e);
+ throw ioException;
+ }
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLStreamHandlerFactory.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLStreamHandlerFactory.java
new file mode 100644
index 0000000..091f605
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/url/ServletURLStreamHandlerFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cocoon.servletservice.url;
+
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+
+public class ServletURLStreamHandlerFactory implements URLStreamHandlerFactory {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.net.URLStreamHandlerFactory#createURLStreamHandler(java.lang.String)
+ */
+ public URLStreamHandler createURLStreamHandler(String protocol) {
+ if ("servlet".equalsIgnoreCase(protocol)) {
+ return new ServletURLStreamHandler();
+ }
+
+ return null;
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/NullServletInputStream.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/NullServletInputStream.java
new file mode 100644
index 0000000..2aed563
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/NullServletInputStream.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.util;
+
+import javax.servlet.ServletInputStream;
+
+import java.io.IOException;
+
+/**
+ * Always empty stream.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class NullServletInputStream extends ServletInputStream {
+
+ public static final NullServletInputStream INSTANCE = new NullServletInputStream();
+
+ public int read() throws IOException {
+ return -1;
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/RequestParameters.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/RequestParameters.java
new file mode 100644
index 0000000..88656d4
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/RequestParameters.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.util;
+
+import org.apache.commons.collections.iterators.IteratorEnumeration;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * This class is used by the <code>RequestWrapper</code>. It parses
+ * a query string, decodes request parameters, and creates a parameter map
+ * ready for use by the <code>Request</code> object.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public final class RequestParameters implements Serializable {
+
+ /**
+ * The parameter names are the keys and the value is a String array object
+ */
+ private final Map values;
+
+ /**
+ * Construct a new object from a queryString
+ *
+ * @param queryString request query string
+ */
+ public RequestParameters(String queryString) {
+ this.values = new HashMap(5);
+
+ if (queryString != null) {
+ StringTokenizer st = new StringTokenizer(queryString, "&");
+ while (st.hasMoreTokens()) {
+ String pair = st.nextToken();
+ int pos = pair.indexOf('=');
+ if (pos != -1) {
+ setParameter(decode(pair.substring(0, pos)),
+ decode(pair.substring(pos + 1, pair.length())));
+ } else {
+ setParameter(decode(pair), "");
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a parameter.
+ * The parameter is added with the given value.
+ *
+ * @param name The name of the parameter.
+ * @param value The value of the parameter.
+ */
+ private void setParameter(String name, String value) {
+ String[] values = (String[]) this.values.get(name);
+ if (values == null) {
+ values = new String[] { value };
+ } else {
+ String[] v2 = new String[values.length + 1];
+ System.arraycopy(values, 0, v2, 0, values.length);
+ v2[values.length] = value;
+ values = v2;
+ }
+
+ this.values.put(name, values);
+ }
+
+ /**
+ * Get the value of a parameter.
+ *
+ * @param name The name of the parameter.
+ * @return The value of the first parameter with the name
+ * or <CODE>null</CODE>
+ */
+ public String getParameter(String name) {
+ String[] values = (String[]) this.values.get(name);
+ if (values != null) {
+ return values[0];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the value of a parameter.
+ *
+ * @param name The name of the parameter.
+ * @param defaultValue The default value if the parameter does not exist.
+ * @return The value of the first parameter with the name
+ * or <CODE>defaultValue</CODE>
+ */
+ public String getParameter(String name, String defaultValue) {
+ String[] values = (String[]) this.values.get(name);
+ if (values != null) {
+ return values[0];
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Get all values of a parameter.
+ *
+ * @param name The name of the parameter.
+ * @return Array of the (String) values or null if the parameter
+ * is not defined.
+ */
+ public String[] getParameterValues(String name) {
+ return (String[]) values.get(name);
+ }
+
+ /**
+ * Get all parameter names.
+ *
+ * @return Enumeration for the (String) parameter names.
+ */
+ public Enumeration getParameterNames() {
+ return new IteratorEnumeration(values.keySet().iterator());
+ }
+
+ /**
+ * Get map of parameter names to String array values
+ *
+ * @return map of parameter values
+ */
+ public Map getParameterMap() {
+ return Collections.unmodifiableMap(values);
+ }
+
+ /**
+ * Decodes URL encoded string. Supports decoding of '+', '%XX' encoding,
+ * and '%uXXXX' encoding.
+ *
+ * @param s URL encoded string
+ * @return decoded string
+ */
+ private static String decode(String s) {
+ byte[] decoded = new byte[s.length() / 3 + 1];
+ int decodedLength = 0;
+
+ final int length = s.length();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < length; ) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '+':
+ sb.append(' ');
+ i++;
+ break;
+
+ case '%':
+ // Check if this is a non-standard %u9999 encoding
+ // (see COCOON-1950)
+ try {
+ if (s.charAt(i + 1) == 'u') {
+ sb.append((char) Integer.parseInt(s.substring(i + 2, i + 6), 16));
+ i += 6;
+ break;
+ }
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid query string. " +
+ "Illegal hex characters in pattern %" + s.substring(i + 1, i + 6));
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Invalid query string. " +
+ "% character should be followed by 2 hexadecimal characters.");
+ }
+
+ // Process sequence of %XX encoded bytes in one go since several bytes can represent
+ // single character.
+ while (i < length && s.charAt(i) == '%') {
+ if (i + 2 >= length) {
+ throw new IllegalArgumentException("Invalid query string. " +
+ "% character should be followed by 2 hexadecimal characters.");
+ }
+
+ // If found %u9999, rollback '%' and finish this loop
+ if (s.charAt(i + 1) == 'u') {
+ i--;
+ break;
+ }
+
+ try {
+ decoded[decodedLength++] = (byte) Integer.parseInt(s.substring(i + 1, i + 3), 16);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid query string. " +
+ "Illegal hex characters in pattern %" + s.substring(i + 1, i + 3));
+ }
+ i += 3;
+ }
+ if (decodedLength > 0) {
+ try {
+ sb.append(new String(decoded, 0, decodedLength, "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ // The situation that UTF-8 is not supported is quite theoretical, so throw a runtime exception
+ throw new RuntimeException("Problem in decode: UTF-8 encoding not supported.");
+ }
+ decodedLength = 0;
+ }
+
+ break;
+
+ default:
+ sb.append(c);
+ i++;
+ break;
+ }
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletConfigurationWrapper.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletConfigurationWrapper.java
new file mode 100644
index 0000000..12a4ff5
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletConfigurationWrapper.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.util;
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+/**
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class ServletConfigurationWrapper implements ServletConfig {
+
+ protected ServletConfig servletConfig;
+
+ protected ServletContext servletContext;
+
+ /**
+ * @param servletConfig
+ */
+ public ServletConfigurationWrapper(ServletConfig servletConfig) {
+ this(servletConfig, null);
+ }
+
+ /**
+ * @param servletConfig
+ * @param servletContext
+ */
+ public ServletConfigurationWrapper(ServletConfig servletConfig,
+ ServletContext servletContext) {
+ this.servletConfig = servletConfig;
+ this.servletContext = servletContext;
+ }
+
+ public String getServletName() {
+ return this.servletConfig.getServletName();
+ }
+
+ public ServletContext getServletContext() {
+ return this.servletContext != null ? this.servletContext
+ : this.servletConfig.getServletContext();
+ }
+
+ public String getInitParameter(String name) {
+ return this.servletConfig.getInitParameter(name);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return this.servletConfig.getInitParameterNames();
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletContextWrapper.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletContextWrapper.java
new file mode 100644
index 0000000..ebca9aa
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletContextWrapper.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.util;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Set;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+/**
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class ServletContextWrapper implements ServletContext {
+
+ protected ServletContext servletContext;
+
+ /**
+ * @param servletContext The servletContext to set.
+ */
+ public void setServletContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ public ServletContext getContext(String uripath) {
+ return this.servletContext.getContext(uripath);
+ }
+
+ public int getMajorVersion() {
+ return this.servletContext.getMajorVersion();
+ }
+
+ public int getMinorVersion() {
+ return this.servletContext.getMinorVersion();
+ }
+
+ public String getMimeType(String file) {
+ return this.servletContext.getMimeType(file);
+ }
+
+ public Set getResourcePaths(String paths) {
+ return this.servletContext.getResourcePaths(paths);
+ }
+
+ public URL getResource(String path) throws MalformedURLException {
+ return this.servletContext.getResource(path);
+ }
+
+ public InputStream getResourceAsStream(String path) {
+ return this.servletContext.getResourceAsStream(path);
+ }
+
+ public RequestDispatcher getRequestDispatcher(String path) {
+ return this.servletContext.getRequestDispatcher(path);
+ }
+
+ public RequestDispatcher getNamedDispatcher(String name) {
+ return this.servletContext.getNamedDispatcher(name);
+ }
+
+ public Servlet getServlet(String name) throws ServletException {
+ return this.servletContext.getServlet(name);
+ }
+
+ public Enumeration getServlets() {
+ return this.servletContext.getServlets();
+ }
+
+ public Enumeration getServletNames() {
+ return this.servletContext.getServletNames();
+ }
+
+ public void log(String msg) {
+ this.servletContext.log(msg);
+ }
+
+ public void log(Exception exception, String msg) {
+ this.servletContext.log(exception, msg);
+ }
+
+ public void log(String msg, Throwable throwable) {
+ this.servletContext.log(msg, throwable);
+ }
+
+ public String getRealPath(String path) {
+ return this.servletContext.getRealPath(path);
+ }
+
+ public String getServerInfo() {
+ return this.servletContext.getServerInfo();
+ }
+
+ public String getInitParameter(String path) {
+ return this.servletContext.getInitParameter(path);
+ }
+
+ public Enumeration getInitParameterNames() {
+ return this.servletContext.getInitParameterNames();
+ }
+
+ public Object getAttribute(String name) {
+ return this.servletContext.getAttribute(name);
+ }
+
+ public Enumeration getAttributeNames() {
+ return this.servletContext.getAttributeNames();
+ }
+
+ public void setAttribute(String name, Object value) {
+ this.servletContext.setAttribute(name, value);
+ }
+
+ public void removeAttribute(String name) {
+ this.servletContext.removeAttribute(name);
+ }
+
+ public String getServletContextName() {
+ return this.servletContext.getServletContextName();
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletServiceRequest.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletServiceRequest.java
new file mode 100644
index 0000000..30877d0
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletServiceRequest.java
@@ -0,0 +1,835 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.security.Principal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+import org.apache.cocoon.callstack.CallFrame;
+import org.apache.cocoon.callstack.CallStack;
+import org.apache.cocoon.callstack.environment.CallFrameHelper;
+import org.apache.cocoon.servletservice.ServletServiceContext;
+import org.apache.commons.collections.iterators.IteratorEnumeration;
+
+/**
+ * <p>
+ * Create a {@link HttpServletRequest} from an URL, that is used while calling e.g. a
+ * servlet service. The current implementation forwards headers, attributes and
+ * parameters.
+ * </p>
+ *
+ * @version $Id: BlockCallHttpServletRequest.java 577519 2007-09-20 03:05:26Z
+ * vgritsenko $
+ * @since 1.0.0
+ */
+public class ServletServiceRequest implements HttpServletRequest {
+
+ /**
+ * Protocol of block call requests.
+ */
+ private static final String PROTOCOL = "HTTP/1.1";
+
+ /**
+ * Date header format definied by RFC 822, see
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
+ */
+ private final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);
+
+ /**
+ * The <code>parentRequest</code> holds reference to the request object
+ * that makes a servlet call.
+ */
+ private HttpServletRequest parentRequest;
+
+ /**
+ * Block call request URI
+ */
+ private final URI uri;
+
+ /**
+ * Request method. If not set via {@link #setMethod(String)}, defaults to
+ * <code>GET</code>.
+ */
+ private String method;
+
+ /**
+ * Request headers map
+ */
+ private final Headers headers;
+
+ /**
+ * Request character encoding.
+ */
+ private String encoding;
+
+ /**
+ * Length of the request body.
+ */
+ private int contentLength;
+
+ /**
+ * Request body.
+ */
+ private ServletInputStream content;
+
+ /**
+ * Request attributes map.
+ */
+ private final Attributes attributes;
+
+ private Parameters parameters;
+
+ private ServletServiceContext context;
+
+ /**
+ * @param uri
+ * points to the called servlet
+ * @param parentRequest
+ * reference to the request object that makes a servlet call
+ */
+ public ServletServiceRequest(URI uri, HttpServletRequest parentRequest) {
+ this.parentRequest = parentRequest;
+ this.uri = uri;
+ this.headers = new Headers();
+ this.method = "GET";
+ this.contentLength = -1;
+ this.content = NullServletInputStream.INSTANCE;
+ this.attributes = new Attributes();
+ this.parameters = new Parameters();
+ }
+
+ public String getProtocol() {
+ return PROTOCOL;
+ }
+
+ public String getMethod() {
+ return this.method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ //
+ // Request URI parts
+ //
+
+ public String getScheme() {
+ return this.uri.getScheme();
+ }
+
+ public String getServerName() {
+ return this.parentRequest.getServerName();
+ }
+
+ public int getServerPort() {
+ return this.parentRequest.getServerPort();
+ }
+
+ public String getContextPath() {
+ return this.parentRequest.getContextPath();
+ }
+
+ public String getServletPath() {
+ // TODO Is this right?
+ return "";
+ }
+
+ public String getPathInfo() {
+ return this.uri.getPath();
+ }
+
+ public String getPathTranslated() {
+ // TODO This is legal but more info might be possible
+ return null;
+ }
+
+ public String getQueryString() {
+ return this.uri.getQuery();
+ }
+
+ public String getRequestURI() {
+ // TODO Is this right?
+ return this.getContextPath() + this.getServletPath() + this.getPathInfo();
+ }
+
+ public StringBuffer getRequestURL() {
+ return new StringBuffer(this.getScheme()).append(':').append(this.getRequestURI());
+ }
+
+ //
+ // Request headers
+ //
+
+ public String getHeader(String name) {
+ return (String) this.headers.getValue(name);
+ }
+
+ public Enumeration getHeaders(String name) {
+ return this.headers.getNames();
+ }
+
+ public void setHeader(String name, String value) {
+ this.headers.setValue(name, value);
+ }
+
+ public long getDateHeader(String name) {
+ String header = this.getHeader(name);
+ if (header == null) {
+ return -1;
+ }
+
+ try {
+ return this.dateFormat.parse(header).getTime();
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setDateHeader(String name, long date) {
+ this.setHeader(name, this.dateFormat.format(new Date(date)));
+ }
+
+ public int getIntHeader(String name) {
+ String header = this.getHeader(name);
+ if (header == null) {
+ return -1;
+ }
+
+ return Integer.parseInt(header);
+ }
+
+ public void setIntHeader(String name, int value) {
+ this.setHeader(name, String.valueOf(value));
+ }
+
+ public Enumeration getHeaderNames() {
+ return this.headers.getNames();
+ }
+
+ //
+ // Request parameters
+ //
+
+ public String getParameter(String name) {
+ return (String) this.parameters.getValue(name);
+ }
+
+ public String[] getParameterValues(String name) {
+ return this.parameters.getValues(name);
+ }
+
+ public Enumeration getParameterNames() {
+ return this.parameters.getNames();
+ }
+
+ public Map getParameterMap() {
+ return this.parameters.getValues();
+ }
+
+ //
+ // Request body
+ //
+
+ public String getCharacterEncoding() {
+ return this.encoding;
+ }
+
+ public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException {
+ this.encoding = encoding;
+ }
+
+ public int getContentLength() {
+ return this.contentLength;
+ }
+
+ public void setContentLength(int contentLength) {
+ this.contentLength = contentLength;
+ }
+
+ /*
+ * TODO Doesn't handle input streams yet
+ */
+ public String getContentType() {
+ return null;
+ }
+
+ public ServletInputStream getInputStream() throws IOException {
+ return this.content;
+ }
+
+ public void setInputStream(final InputStream inputStream) {
+ try {
+ this.contentLength = inputStream.available();
+ } catch (IOException e) {
+ this.contentLength = -1;
+ }
+
+ this.content = new ServletInputStream() {
+ public int read() throws IOException {
+ return inputStream.read();
+ }
+ };
+ }
+
+ public BufferedReader getReader() throws IOException {
+ Reader reader;
+ String encoding = this.getCharacterEncoding();
+ if (encoding == null) {
+ reader = new InputStreamReader(this.getInputStream());
+ } else {
+ reader = new InputStreamReader(this.getInputStream(), encoding);
+ }
+
+ return new BufferedReader(reader);
+ }
+
+ //
+ // Request attributes
+ //
+
+ public Object getAttribute(String name) {
+ return this.attributes.getValue(name);
+ }
+
+ public Enumeration getAttributeNames() {
+ return this.attributes.getNames();
+ }
+
+ public void setAttribute(String name, Object value) {
+ if (value != null) {
+ this.attributes.setValue(name, value);
+ } else {
+ this.removeAttribute(name);
+ }
+ }
+
+ public void removeAttribute(String name) {
+ this.attributes.remove(name);
+ }
+
+ public String getAuthType() {
+ return null;
+ }
+
+ public Cookie[] getCookies() {
+ return this.parentRequest.getCookies();
+ }
+
+ public Locale getLocale() {
+ return this.parentRequest.getLocale();
+ }
+
+ public Enumeration getLocales() {
+ return this.parentRequest.getLocales();
+ }
+
+ /**
+ * @deprecated
+ * @see javax.servlet.ServletRequest#getRealPath(java.lang.String)
+ */
+ public String getRealPath(String path) {
+ return null;
+ }
+
+ public String getRemoteAddr() {
+ return this.parentRequest.getRemoteAddr();
+ }
+
+ public String getRemoteHost() {
+ return this.parentRequest.getRemoteHost();
+ }
+
+ public String getRemoteUser() {
+ return this.parentRequest.getRemoteUser();
+ }
+
+ public RequestDispatcher getRequestDispatcher(String path) {
+ return this.context.getRequestDispatcher(path);
+ }
+
+
+ public String getRequestedSessionId() {
+ return this.parentRequest.getRequestedSessionId();
+ }
+
+ public HttpSession getSession() {
+ Session session = (Session) this.getSession(true);
+ session.setRequest(this);
+ return session;
+ }
+
+ public HttpSession getSession(boolean create) {
+
+ HttpServletRequest request = this.parentRequest;
+ while (request != null) {
+ if (request instanceof ServletServiceRequest) {
+ request = ((ServletServiceRequest) request).parentRequest;
+ } else {
+ break;
+ }
+ }
+ HttpSession outestSession = request.getSession();
+ Session session = null;
+ // each block's session object is stored in the outest session as an
+ // attribute
+ if (this != request) {
+ session = (Session) outestSession.getAttribute(HttpSession.class + "_" + this.context.getMountPath());
+ } else {
+ session = (Session) outestSession;
+ }
+
+ if (session == null && create) {
+ session = new Session(this.context);
+ outestSession.setAttribute(HttpSession.class + "_" + this.context.getMountPath(), session);
+ }
+
+ if (session != null) {
+ session.setRequest(this);
+ }
+
+ return session;
+ }
+
+ public void setContext(ServletContext context) {
+ this.context = (ServletServiceContext) context;
+ }
+
+ public Principal getUserPrincipal() {
+ return this.parentRequest.getUserPrincipal();
+ }
+
+ public boolean isRequestedSessionIdFromCookie() {
+ return this.parentRequest.isRequestedSessionIdFromCookie();
+ }
+
+ public boolean isRequestedSessionIdFromUrl() {
+ return this.isRequestedSessionIdFromURL();
+ }
+
+ public boolean isRequestedSessionIdFromURL() {
+ return this.parentRequest.isRequestedSessionIdFromURL();
+ }
+
+ public boolean isRequestedSessionIdValid() {
+ return this.parentRequest.isRequestedSessionIdValid();
+ }
+
+ public boolean isSecure() {
+ return this.parentRequest.isSecure();
+ }
+
+ public boolean isUserInRole(String role) {
+ return this.parentRequest.isUserInRole(role);
+ }
+
+ public String getLocalAddr() {
+ return this.parentRequest.getLocalAddr();
+ }
+
+ public String getLocalName() {
+ return this.parentRequest.getLocalName();
+ }
+
+ public int getLocalPort() {
+ return this.parentRequest.getLocalPort();
+ }
+
+ public int getRemotePort() {
+ return this.parentRequest.getRemotePort();
+ }
+
+ private abstract static class Values implements Serializable {
+
+ /** The parameter names are the keys and the value is a List object */
+ Map values = new HashMap();
+
+ public Values() {
+ }
+
+ protected void setValue(String name, Object value) {
+ List list;
+ if (this.values.containsKey(name)) {
+ list = (List) this.values.get(name);
+ } else {
+ list = new ArrayList();
+ this.values.put(name, list);
+ }
+ list.add(value);
+ }
+
+ public Object getValue(String name) {
+ if (this.values.containsKey(name)) {
+ return ((List) this.values.get(name)).get(0);
+ }
+
+ return this.getValueOfCaller(name);
+ }
+
+ protected abstract Object getValueOfCaller(String name);
+
+ protected abstract Enumeration namesOf(HttpServletRequest request);
+
+ public Enumeration getNames() {
+ Set names = new HashSet();
+ for (int i = 0; i < CallStack.size(); i++) {
+ CallFrame frame = CallStack.frameAt(i);
+ HttpServletRequest request = (HttpServletRequest) frame.getAttribute(CallFrameHelper.REQUEST_OBJECT);
+ if (request instanceof ServletServiceRequest) {
+ names.addAll(this.values.keySet());
+ } else {
+ for (Enumeration enumeration = this.namesOf(request); enumeration.hasMoreElements();) {
+ names.add(enumeration.nextElement());
+ }
+ }
+ if (request.equals(this.getRequest())) {
+ break;
+ }
+ }
+
+ return new EnumerationFromIterator(names.iterator());
+ }
+
+ protected abstract ServletServiceRequest getRequest();
+
+ final class EnumerationFromIterator implements Enumeration {
+
+ private Iterator iterator;
+
+ EnumerationFromIterator(Iterator iter) {
+ this.iterator = iter;
+ }
+
+ public boolean hasMoreElements() {
+ return this.iterator.hasNext();
+ }
+
+ public Object nextElement() {
+ return this.iterator.next();
+ }
+ }
+
+ }
+
+ private class Parameters extends Values {
+
+ public Parameters() {
+ if (this.getRequest().uri.getQuery() != null) {
+ StringTokenizer st = new StringTokenizer(this.getRequest().uri.getQuery(), "&");
+ while (st.hasMoreTokens()) {
+ String pair = st.nextToken();
+ int pos = pair.indexOf('=');
+ if (pos != -1) {
+ this.setValue(this.parseName(pair.substring(0, pos)), this.parseName(pair.substring(pos + 1,
+ pair.length())));
+ }
+ }
+ }
+ }
+
+ private String parseName(String s) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '+':
+ sb.append(' ');
+ break;
+ case '%':
+ try {
+ if (s.charAt(i + 1) == 'u') {
+ // working with multi-byte symbols in format %uXXXX
+ sb.append((char) Integer.parseInt(s.substring(i + 2, i + 6), 16));
+ i += 5; // 4 digits and 1 symbol u
+ } else {
+ // working with single-byte symbols in format %YY
+ sb.append((char) Integer.parseInt(s.substring(i + 1, i + 3), 16));
+ i += 2;
+ }
+ } catch (NumberFormatException e) {
+ IllegalArgumentException iae = new IllegalArgumentException();
+ iae.initCause(e);
+ throw iae;
+ } catch (StringIndexOutOfBoundsException e) {
+ String rest = s.substring(i);
+ sb.append(rest);
+ if (rest.length() == 2) {
+ i++;
+ }
+ }
+
+ break;
+ default:
+ sb.append(c);
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+ public Map getValues() {
+ Map result = new HashMap();
+ for (int i = 0; i < CallStack.size(); i++) {
+ CallFrame frame = CallStack.frameAt(i);
+ HttpServletRequest request = (HttpServletRequest) frame.getAttribute(CallFrameHelper.REQUEST_OBJECT);
+ if (request instanceof ServletServiceRequest) {
+ result.putAll(this.values);
+ } else {
+ result.putAll(request.getParameterMap());
+ }
+ if (request.equals(this.getRequest())) {
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ public String[] getValues(String name) {
+ List list = (List) this.values.get(name);
+ if (list == null) {
+ return null;
+ }
+
+ String[] result = new String[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ result[i] = (String) list.get(i);
+ }
+ return result;
+ }
+
+ protected ServletServiceRequest getRequest() {
+ return ServletServiceRequest.this;
+ }
+
+ protected Object getValueOfCaller(String name) {
+ return this.getRequest().parentRequest.getParameter(name);
+ }
+
+ protected Enumeration namesOf(HttpServletRequest request) {
+ return request.getParameterNames();
+ }
+
+ }
+
+ private class Headers extends Values {
+
+ public Object getValueOfCaller(String name) {
+ return this.getRequest().parentRequest.getHeader(name);
+ }
+
+ public Enumeration getValues(String name) {
+ List list = (List) this.values.get(name);
+ if (list == null) {
+ return new Enumeration() {
+
+ public boolean hasMoreElements() {
+ return false;
+ }
+
+ public Object nextElement() {
+ throw new NoSuchElementException();
+ }
+
+ };
+ }
+
+ return new IteratorEnumeration(list.iterator());
+ }
+
+ protected ServletServiceRequest getRequest() {
+ return ServletServiceRequest.this;
+ }
+
+ protected Enumeration namesOf(HttpServletRequest request) {
+ return request.getHeaderNames();
+ }
+ }
+
+ private class Attributes extends Values {
+
+ public Object getValue(String name) {
+ if (this.values.containsKey(name)) {
+ return this.values.get(name);
+ }
+
+ return this.getValueOfCaller(name);
+ }
+
+ public Object getValueOfCaller(String name) {
+ return this.getRequest().parentRequest.getAttribute(name);
+ }
+
+ protected void setValue(String name, Object value) {
+ this.values.put(name, value);
+ }
+
+ public void remove(String name) {
+ if (this.values.containsKey(name)) {
+ this.values.remove(name);
+ } else {
+ this.getRequest().parentRequest.removeAttribute(name);
+ }
+ }
+
+ protected ServletServiceRequest getRequest() {
+ return ServletServiceRequest.this;
+ }
+
+ protected Enumeration namesOf(HttpServletRequest request) {
+ return request.getAttributeNames();
+ }
+
+ }
+
+ private static class Session extends Values implements HttpSession {
+
+ private ServletServiceContext context;
+
+ private transient ServletServiceRequest request;
+
+ public Session(ServletServiceContext context) {
+ this.context = context;
+ }
+
+ protected Object getValueOfCaller(String name) {
+ return this.getRequest().parentRequest.getSession().getAttribute(name);
+ }
+
+ protected Enumeration namesOf(HttpServletRequest request) {
+ return this.getRequest().parentRequest.getSession().getAttributeNames();
+ }
+
+ protected ServletServiceRequest getRequest() {
+ return this.request;
+ }
+
+ private void setRequest(ServletServiceRequest request) {
+ this.request = request;
+ }
+
+ public Object getAttribute(String name) {
+ if (this.values.containsKey(name)) {
+ return this.values.get(name);
+ }
+
+ return this.getValueOfCaller(name);
+ }
+
+ public Enumeration getAttributeNames() {
+ return this.getNames();
+ }
+
+ public long getCreationTime() {
+ return this.getRequest().parentRequest.getSession().getCreationTime();
+ }
+
+ public String getId() {
+ return this.getRequest().parentRequest.getSession().getId();
+ }
+
+ public long getLastAccessedTime() {
+ return this.getRequest().parentRequest.getSession().getLastAccessedTime();
+ }
+
+ public int getMaxInactiveInterval() {
+ return this.getRequest().parentRequest.getSession().getMaxInactiveInterval();
+ }
+
+ public ServletContext getServletContext() {
+ return this.context;
+ }
+
+ public HttpSessionContext getSessionContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String[] getValueNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void invalidate() {
+ this.getRequest().parentRequest.getSession().invalidate();
+ }
+
+ public boolean isNew() {
+ return this.getRequest().parentRequest.getSession().isNew();
+ }
+
+ public void putValue(String name, Object value) {
+ this.setValue(name, value);
+ }
+
+ public void removeAttribute(String name) {
+ this.removeValue(name);
+ }
+
+ public void removeValue(String name) {
+ this.removeAttribute(name);
+ }
+
+ public void setAttribute(String name, Object value) {
+ this.setValue(name, value);
+ }
+
+ public void setMaxInactiveInterval(int interval) {
+ this.getRequest().parentRequest.getSession().setMaxInactiveInterval(interval);
+ }
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Override the setValue and getValue methods because session attributes
+ // are not arrays
+ protected void setValue(String name, Object value) {
+ this.values.put(name, value);
+ }
+
+ public Object getValue(String name) {
+ return this.values.get(name);
+ }
+
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletServiceResponse.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletServiceResponse.java
new file mode 100644
index 0000000..da8f2f4
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/java/org/apache/cocoon/servletservice/util/ServletServiceResponse.java
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Creates a {@link HttpServletResponse} object that is usable for internal block calls.
+ *
+ * @version $Id$
+ * @since 1.0.0
+ */
+public class ServletServiceResponse implements HttpServletResponse {
+
+ private OutputStream outputStream;
+ private ServletOutputStream servletStream;
+ private PrintWriter writer;
+ private boolean committed;
+ private Locale locale;
+ private int statusCode;
+
+ private Map<String, String> headers;
+
+ /**
+ * format definied by RFC 822, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
+ */
+ final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);
+
+ public ServletServiceResponse() {
+ this.headers = new HashMap<String, String>();
+ this.statusCode = HttpServletResponse.SC_OK;
+ }
+
+ public void addCookie(Cookie cookie) {
+ // Ignore
+ }
+
+ public void addDateHeader(String name, long date) {
+ // this class does not support multivalue headers
+ this.setDateHeader(name, date);
+ }
+
+ public void addHeader(String name, String value) {
+ // this class does not support multivalue headers
+ this.setHeader(name, value);
+ }
+
+ public void addIntHeader(String name, int value) {
+ // this class does not support multivalue headers
+ this.setIntHeader(name, value);
+ }
+
+ public boolean containsHeader(String name) {
+ return this.headers.containsKey(name);
+ }
+
+ public String encodeRedirectUrl(String url) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String encodeRedirectURL(String url) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String encodeUrl(String url) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String encodeURL(String url) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void flushBuffer() throws IOException {
+ this.committed = true;
+ }
+
+ public int getBufferSize() {
+ return 0;
+ }
+
+ public String getCharacterEncoding() {
+ // TODO Let it depend on the actual response body
+ return "ISO-8859-1";
+ }
+
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ if (this.writer != null) {
+ throw new IllegalStateException("Tried to create output stream; writer already exists");
+ }
+
+ if (this.servletStream == null) {
+ this.servletStream = new ServletOutputStream() {
+
+ @Override
+ public void flush() throws IOException {
+ ServletServiceResponse.this.outputStream.flush();
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ ServletServiceResponse.this.outputStream.write(b);
+ ServletServiceResponse.this.committed = true;
+ }
+
+ /*
+ * This method is probably never called, the close will be initiated directly on
+ * this.outputStream by the one who set it via
+ * BlockCallHttpServletResponse.setOutputStream()
+ */
+ @Override
+ public void close() throws IOException {
+ ServletServiceResponse.this.outputStream.close();
+ }
+
+ };
+ }
+
+ return this.servletStream;
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ if (this.servletStream != null) {
+ throw new IllegalStateException("Tried to create writer; output stream already exists");
+ }
+
+ if (this.writer == null) {
+ this.writer = new PrintWriter(new OutputStreamWriter(this.outputStream, this.getCharacterEncoding()));
+ }
+
+ return this.writer;
+ }
+
+ public boolean isCommitted() {
+ return this.committed;
+ }
+
+ public void reset() {
+ this.resetBuffer();
+ }
+
+ public void resetBuffer() {
+ // this class does not buffer anything so if first byte is written to output stream
+ // there is no way to reset anything
+ // Servlet Service Framework uses for buffering a wrapper called
+ // HttpServletResponseBufferingWrapper
+ if (this.committed) {
+ throw new IllegalStateException("May not resetBuffer after response is committed");
+ }
+ }
+
+ public void sendError(int sc) throws IOException {
+ // TODO Auto-generated method stub
+ }
+
+ public void sendError(int sc, String msg) throws IOException {
+ // TODO Auto-generated method stub
+ }
+
+ public void sendRedirect(String location) throws IOException {
+ // TODO Auto-generated method stub
+ }
+
+ public void setBufferSize(int size) {
+ // TODO Implement buffering, for the moment ignore.
+ }
+
+ public void setContentLength(int len) {
+ // Ignore
+ }
+
+ public void setContentType(String type) {
+ this.setHeader("Content-Type", type);
+ }
+
+ public void setDateHeader(String name, long date) {
+ this.setHeader(name, this.dateFormat.format(new Date(date)));
+ }
+
+ public long getDateHeader(String name) {
+ String header = this.getHeader(name);
+ if (header == null) {
+ return -1;
+ }
+
+ try {
+ return this.dateFormat.parse(header).getTime();
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setHeader(String name, String value) {
+ this.headers.put(name, value);
+ }
+
+ public String getHeader(String name) {
+ return this.headers.get(name);
+ }
+
+ public Map<String, String> getHeaders() {
+ return this.headers;
+ }
+
+ public void setIntHeader(String name, int value) {
+ this.setHeader(name, String.valueOf(value));
+ }
+
+ public int getIntHeader(String name) {
+ String header = this.getHeader(name);
+ if (header == null) {
+ return -1;
+ }
+
+ return Integer.parseInt(header);
+ }
+
+ public void setLocale(Locale loc) {
+ this.locale = loc;
+ }
+
+ public void setOutputStream(OutputStream outputStream) {
+ this.outputStream = outputStream;
+ }
+
+ public void setStatus(int sc) {
+ this.statusCode = sc;
+ }
+
+ public int getStatus() {
+ return this.statusCode;
+ }
+
+ public void setStatus(int sc, String sm) {
+ throw new UnsupportedOperationException("This method has been deprecated");
+ }
+
+ public String getContentType() {
+ return this.getHeader("Content-Type");
+ }
+
+ public void setCharacterEncoding(String arg0) {
+ // TODO Auto-generated method stub
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-callstack.xml b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-callstack.xml
new file mode 100644
index 0000000..a9ad6bb
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-callstack.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- SVN $Id$ -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+ <!-- Install the call stack custom scope -->
+ <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
+ <property name="scopes">
+ <map>
+ <entry key="call">
+ <bean class="org.apache.cocoon.callstack.CallScope"/>
+ </entry>
+ <entry key="servlet">
+ <bean class="org.apache.cocoon.servletscope.ServletScope">
+ <property name="servletContext" ref="javax.servlet.ServletContext/callstack"/>
+ </bean>
+ </entry>
+ </map>
+ </property>
+ </bean>
+
+ <!-- The request object of the current call frame -->
+ <bean name="javax.servlet.http.HttpServletRequest/callstack"
+ class="org.apache.cocoon.callstack.environment.HttpServletRequestFactoryBean"
+ scope="call">
+ <aop:scoped-proxy proxy-target-class="false"/>
+ </bean>
+
+ <!-- The response object of the current call frame -->
+ <bean name="javax.servlet.http.HttpServletResponse/callstack"
+ class="org.apache.cocoon.callstack.environment.HttpServletResponseFactoryBean"
+ scope="call">
+ <aop:scoped-proxy proxy-target-class="false"/>
+ </bean>
+
+ <!-- The context object of the current call frame -->
+ <bean name="javax.servlet.ServletContext/callstack"
+ class="org.apache.cocoon.callstack.environment.ServletContextFactoryBean"
+ scope="call">
+ <aop:scoped-proxy proxy-target-class="false"/>
+ </bean>
+
+</beans>
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-context.xml b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-context.xml
new file mode 100644
index 0000000..d4089c8
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-context.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- SVN $Id$ -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+ <!-- The context object of the current base call frame -->
+ <bean name="javax.servlet.ServletContext/baseframe"
+ class="org.apache.cocoon.servletservice.spring.BaseServletContextFactoryBean" scope="call">
+ <aop:scoped-proxy proxy-target-class="false" />
+ </bean>
+
+</beans>
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-protocol.xml b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-protocol.xml
new file mode 100644
index 0000000..7449697
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-protocol.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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$ -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+ <bean id="org.apache.cocoon.servletservice.url.ServletURLStreamHandlerFactory" class="org.apache.cocoon.servletservice.url.ServletURLStreamHandlerFactory"/>
+
+</beans>
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-servlet-map.xml b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-servlet-map.xml
new file mode 100644
index 0000000..5c0233e
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-servlet-map.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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$ -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:configurator="http://cocoon.apache.org/schema/configurator"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://cocoon.apache.org/schema/configurator http://cocoon.apache.org/schema/configurator/cocoon-configurator-1.0.1.xsd">
+
+ <configurator:bean-map id="org.apache.cocoon.servletservice.spring.BlockServletMap" type="javax.servlet.Servlet"
+ has-properties="mountPath" key-property="mountPath" />
+
+</beans>
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-urlhandler-advice.xml b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-urlhandler-advice.xml
new file mode 100644
index 0000000..080352d
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/cocoon/spring/cocoon-ssf-urlhandler-advice.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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$ -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+ <aop:config>
+ <aop:aspect id="installURL" ref="org.apache.cocoon.jnet.URLHandlerFactoryCollector">
+ <aop:pointcut id="servletServiceInvocation"
+ expression="execution(* javax.servlet.Servlet.service(..)) and bean(*/embedded)" />
+
+ <aop:around pointcut-ref="servletServiceInvocation" method="installURLHandlers" />
+ </aop:aspect>
+ </aop:config>
+
+</beans>
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/spring.handlers b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..88b23e9
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+http\://cocoon.apache.org/schema/servlet=org.apache.cocoon.servletservice.spring.ServletNamespaceHandler
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/spring.schemas b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..19f7e9f
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+http\://cocoon.apache.org/schema/servlet/cocoon-servlet-1.0.xsd=org/apache/cocoon/servletservice/schema/cocoon-servlet-1.0.xsd
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/org/apache/cocoon/servletservice/schema/cocoon-servlet-1.0.xsd b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/org/apache/cocoon/servletservice/schema/cocoon-servlet-1.0.xsd
new file mode 100644
index 0000000..cf11b45
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/main/resources/org/apache/cocoon/servletservice/schema/cocoon-servlet-1.0.xsd
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- @version $Id$ -->
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ targetNamespace="http://cocoon.apache.org/schema/servlet"
+ xmlns:tns="http://cocoon.apache.org/schema/servlet"
+ xmlns="http://cocoon.apache.org/schema/servlet">
+
+ <xsd:import namespace="http://www.springframework.org/schema/beans"
+ schemaLocation="http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"/>
+
+ <xsd:annotation>
+ <xsd:documentation>
+ Schema for decorating servlet beans so that they are managed
+ by Spring and used in the servlet context configured by the
+ elements in this scheme.
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:element name="context">
+ <xsd:annotation>
+ <xsd:documentation>
+ The top level element.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element ref="init-params" minOccurs="0" maxOccurs="1" />
+ <xsd:element ref="context-params" minOccurs="0" maxOccurs="1" />
+ <xsd:element ref="connections" minOccurs="0" maxOccurs="1" />
+ </xsd:sequence>
+ <xsd:attribute name="mount-path" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation>
+ Where the servlet should be mounted relative to
+ the context path of the embeding servlet
+ container. Use an empty string for mounting
+ directly at the context path. Non empty mount
+ paths must start with '/'. If the mountPath
+ attribute not is used, the servlet will not be
+ available though an URI, it will only be
+ avaliable through its bean name for servlet
+ beans connected to this servlet.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="context-path" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation>
+ The root path for the servlet context.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="init-params">
+ <xsd:annotation>
+ <xsd:documentation>
+ The init params for the servlet configuration used for
+ the servlet.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="beans:mapType" />
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="context-params">
+ <xsd:annotation>
+ <xsd:documentation>
+ Context parameters for the servlet context.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="beans:mapType" />
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="connections">
+ <xsd:annotation>
+ <xsd:documentation>
+ Connections to other servlet services that are made
+ available through the named dispatcher of the servlet
+ context. Consist of mappings between a named connection
+ to another servlet and a reference to it.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="beans:mapType" />
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+</xsd:schema>
\ No newline at end of file
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapperTestCase.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapperTestCase.java
new file mode 100644
index 0000000..1435ab0
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/HttpServletResponseBufferingWrapperTestCase.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.easymock.MockControl;
+
+public class HttpServletResponseBufferingWrapperTestCase extends TestCase {
+
+ public void testHeadersPassing() {
+ MockControl control = MockControl.createControl(HttpServletResponse.class);
+ HttpServletResponse response = (HttpServletResponse)control.getMock();
+ response.setHeader("test", "foo");
+ response.isCommitted();
+ control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+ control.replay();
+ HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+ responseWrapper.setHeader("test", "foo");
+ control.verify();
+ }
+
+ public void testStatusCodePassing() {
+ MockControl control = MockControl.createControl(HttpServletResponse.class);
+ HttpServletResponse response = (HttpServletResponse)control.getMock();
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.isCommitted();
+ control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+ control.replay();
+ HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+ responseWrapper.setStatus(HttpServletResponse.SC_OK);
+ control.verify();
+ }
+
+ public void testNoBuffering() throws IOException {
+ CountingServletOutputStream countingStream = new CountingServletOutputStream();
+
+ MockControl control = MockControl.createControl(HttpServletResponse.class);
+ HttpServletResponse response = (HttpServletResponse)control.getMock();
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.isCommitted();
+ control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+ response.getOutputStream();
+ control.setReturnValue(countingStream);
+ control.replay();
+
+ HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+ responseWrapper.setStatus(HttpServletResponse.SC_OK);
+ OutputStream outputStream = responseWrapper.getOutputStream();
+ outputStream.write(0);
+
+ assertEquals(1, countingStream.getCounter());
+ control.verify();
+ }
+
+ public void testBuffering() throws IOException {
+ CountingServletOutputStream countingStream = new CountingServletOutputStream();
+
+ MockControl control = MockControl.createControl(HttpServletResponse.class);
+ HttpServletResponse response = (HttpServletResponse)control.getMock();
+ response.isCommitted();
+ control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+ response.getOutputStream();
+ control.setReturnValue(countingStream);
+ control.replay();
+
+ HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+ responseWrapper.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ OutputStream outputStream = responseWrapper.getOutputStream();
+ outputStream.write(0);
+
+ assertEquals(0, countingStream.getCounter());
+ control.verify();
+ }
+
+ public void testBufferOverflow() throws IOException {
+ MockControl control = MockControl.createControl(HttpServletResponse.class);
+ HttpServletResponse response = (HttpServletResponse)control.getMock();
+ response.isCommitted();
+ control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+ response.getOutputStream();
+ control.setReturnValue(new CountingServletOutputStream());
+ control.replay();
+ HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+ responseWrapper.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ OutputStream outputStream = responseWrapper.getOutputStream();
+ boolean catchedException = false;
+ byte[] b = new byte[1024*1024];
+ for (int i = 0; i < 2; i++) {
+ try {
+ outputStream.write(b);
+ } catch (RuntimeException e) {
+ //here we check whether we caught right exception. Is there any better method?
+ if (e.getMessage().indexOf("limit") != -1)
+ catchedException = true;
+ else
+ throw e;
+ }
+ }
+ control.verify();
+ assertTrue("Did not catch exception of overflowed buffer.", catchedException);
+ }
+
+ public void testFlushBufferedResponseWhenBufferIsEmpty() throws IOException {
+ MockControl control = MockControl.createControl(HttpServletResponse.class);
+ HttpServletResponse response = (HttpServletResponse)control.getMock();
+ response.isCommitted();
+ control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ response.flushBuffer();
+ control.replay();
+ HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+ responseWrapper.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ responseWrapper.flushBufferedResponse();
+ control.verify();
+ }
+
+ /**
+ * This method tests late setting of status code to 404. Late here means <b>after</b> getOutputStream method was called.
+ * @throws IOException
+ */
+ public void testLateSettingStatusCodeTo404() throws IOException {
+ CountingServletOutputStream countingStream = new CountingServletOutputStream();
+
+ MockControl control = MockControl.createControl(HttpServletResponse.class);
+ HttpServletResponse response = (HttpServletResponse)control.getMock();
+ response.isCommitted();
+ control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+ response.getOutputStream();
+ control.setReturnValue(countingStream);
+ control.replay();
+
+ HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+ responseWrapper.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ OutputStream outputStream = responseWrapper.getOutputStream();
+ outputStream.write(0);
+
+ assertEquals(0, countingStream.getCounter());
+ responseWrapper.resetBufferedResponse();
+ assertEquals(0, countingStream.getCounter());
+ control.verify();
+ }
+
+ /**
+ * This method tests if flushing of obtained writer is performed correctly in flushBufferedResponse.
+ * @throws Exception
+ */
+ public void testWriterFlushing() throws Exception {
+ CountingServletOutputStream countingStream = new CountingServletOutputStream();
+
+ MockControl control = MockControl.createControl(HttpServletResponse.class);
+ HttpServletResponse response = (HttpServletResponse)control.getMock();
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.getCharacterEncoding();
+ control.setReturnValue("ISO-8859-1");
+ response.isCommitted();
+ control.setReturnValue(false, MockControl.ZERO_OR_MORE);
+ response.getOutputStream();
+ control.setReturnValue(countingStream, MockControl.ONE_OR_MORE);
+ response.flushBuffer();
+ control.replay();
+
+ HttpServletResponseBufferingWrapper responseWrapper = new HttpServletResponseBufferingWrapper(response);
+ responseWrapper.setStatus(HttpServletResponse.SC_OK);
+ PrintWriter writer = responseWrapper.getWriter();
+ writer.write("0");
+ responseWrapper.flushBufferedResponse();
+
+ assertEquals(1, countingStream.getCounter());
+ }
+
+ /**
+ * Simple ServletOutputStream that counts how many bytes were written.
+ *
+ */
+ private class CountingServletOutputStream extends ServletOutputStream {
+
+ private int counter = 0;
+
+ public void write(int arg0) throws IOException {
+ counter++;
+ }
+
+ public int getCounter() {
+ return counter;
+ }
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/ServletServiceContextTestCase.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/ServletServiceContextTestCase.java
new file mode 100644
index 0000000..08e814f
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/ServletServiceContextTestCase.java
@@ -0,0 +1,358 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.cocoon.servletservice;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.apache.cocoon.servletservice.util.ServletServiceRequest;
+import org.apache.cocoon.servletservice.util.ServletServiceResponse;
+
+/**
+ * @version $Id$
+ */
+public class ServletServiceContextTestCase extends TestCase {
+
+ private ServletServiceContext mainContext;
+ private HttpServlet servletA;
+ private HttpServlet servletB;
+ private HttpServlet servletC;
+
+ private ServletServiceContext servletAContext;
+ private ServletServiceContext servletBContext;
+ private ServletServiceContext servletCContext;
+
+ ServletServiceRequest request;
+ ServletServiceResponse response;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ this.mainContext = new ServletServiceContext();
+
+ request = new ServletServiceRequest(new URI("dummy"), null);
+ response = new ServletServiceResponse();
+
+ //creating ServletContexts
+ servletAContext = new ServletServiceContext();
+ servletBContext = new ServletServiceContext();
+ servletCContext = new ServletServiceContext();
+ }
+
+ /**
+ * Tests basic connection to the servlet.
+ *
+ * @throws Exception if test fails
+ */
+ public void testBasicConnection() throws Exception {
+ servletA = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletAContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.flushBuffer();
+ }
+ };
+ servletAContext.setServlet(servletA);
+
+ setMainConnection(servletA, "servletA");
+
+ RequestDispatcher dispatcher = mainContext.getNamedDispatcher("servletA");
+ dispatcher.forward(request, response);
+
+ assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+ assertTrue("Check if response has been committed", response.isCommitted());
+ }
+
+ /**
+ * <p>Tests if explicit super call works and if ServletContexts are properly set up when super call is performed.</p>
+ *
+ * <p>Servlets are connected that way:</p>
+ * <pre>
+ * ServletB
+ * ^
+ * |
+ * ServletA
+ * </pre>
+ * @throws Exception if test fails
+ */
+ public void testExplicitSuperCall() throws Exception {
+ servletA = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletAContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ ServletContext context = CallStackHelper.getCurrentServletContext();
+ RequestDispatcher dispatcher = context.getNamedDispatcher("super");
+ dispatcher.forward(request, response);
+ }
+
+ };
+
+ servletB = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletBContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ assertEquals(servletAContext, CallStackHelper.getBaseServletContext());
+ assertEquals(servletBContext, CallStackHelper.getCurrentServletContext());
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.flushBuffer();
+ }
+ };
+
+ servletAContext.setServlet(servletA);
+ servletBContext.setServlet(servletB);
+
+ //connecting servlets
+ setMainConnection(servletA, "servletA");
+ connectServlets(servletA, servletB, "super");
+
+ RequestDispatcher dispatcher = mainContext.getNamedDispatcher("servletA");
+ dispatcher.forward(request, response);
+ assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+ assertTrue("Check if response has been committed", response.isCommitted());
+ }
+
+ /**
+ * <p>Tests if ServletContext is properly set up in a servlet (C) that is called from super servlet (B) for another one(A).</p>
+ *
+ * <p>Servlets are connected that way:</p>
+ * <pre>
+ * ServletB --> ServletC
+ * ^
+ * |
+ * ServletA
+ * </pre>
+ *
+ * @throws Exception if test fails
+ */
+ public void testContextInServletCalledFromExplicitSuperCall() throws Exception {
+ servletA = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletAContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ ServletContext context = CallStackHelper.getCurrentServletContext();
+ RequestDispatcher dispatcher = context.getNamedDispatcher("super");
+ dispatcher.forward(request, response);
+ }
+ };
+
+ servletB = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletBContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ ServletContext context = CallStackHelper.getBaseServletContext();
+ RequestDispatcher dispatcher = context.getNamedDispatcher("servletC");
+ dispatcher.forward(request, response);
+ }
+ };
+
+ servletC = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletCContext); }
+
+ protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
+ super.service(req, response);
+ assertEquals(servletCContext, CallStackHelper.getBaseServletContext());
+ assertEquals(servletCContext, CallStackHelper.getCurrentServletContext());
+ res.setStatus(HttpServletResponse.SC_OK);
+ res.flushBuffer();
+ }
+ };
+ servletAContext.setServlet(servletA);
+ servletBContext.setServlet(servletB);
+ servletCContext.setServlet(servletC);
+
+ //connecting servlets
+ setMainConnection(servletA, "servletA");
+ connectServlets(servletA, servletB, "super");
+ connectServlets(servletB, servletC, "servletC");
+
+ RequestDispatcher dispatcher = mainContext.getNamedDispatcher("servletA");
+ dispatcher.forward(request, response);
+ assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+ assertTrue("Check if response has been committed", response.isCommitted());
+ }
+
+ /**
+ * <p>This test is very similar to the {@link #testContextInServletCalledFromExplicitSuperCall()} but tries to use true OO approach so there is
+ * no explicit super call. See COCOON-2038.</p>
+ *
+ * <p>Servlets are connected that way:</p>
+ * <pre>
+ * ServletB --> ServletC
+ * ^
+ * |
+ * ServletA
+ * </pre>
+ *
+ * @throws Exception if test fails
+ */
+ public void testTrueObjectOrientedBehaviour() throws Exception {
+ servletA = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletAContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ super.service(request, response);
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ response.flushBuffer();
+ }
+
+ };
+
+ servletB = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletBContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ ServletContext context = CallStackHelper.getBaseServletContext();
+ RequestDispatcher dispatcher = context.getNamedDispatcher("servletC");
+ dispatcher.forward(request, response);
+ }
+ };
+
+ servletC = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletCContext); }
+
+ protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
+ super.service(req, response);
+ assertEquals(servletCContext, CallStackHelper.getBaseServletContext());
+ assertEquals(servletCContext, CallStackHelper.getCurrentServletContext());
+ res.setStatus(HttpServletResponse.SC_OK);
+ res.flushBuffer();
+ }
+ };
+ servletAContext.setServlet(servletA);
+ servletBContext.setServlet(servletB);
+ servletCContext.setServlet(servletC);
+
+ //connecting servlets
+ setMainConnection(servletA, "servletA");
+ connectServlets(servletA, servletB, "super");
+ connectServlets(servletB, servletC, "servletC");
+
+ RequestDispatcher dispatcher = mainContext.getNamedDispatcher("servletA");
+ dispatcher.forward(request, response);
+ assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+ assertTrue("Check if response has been committed", response.isCommitted());
+ }
+
+ /**
+ * <p>This test is for bug COCOON-1939 for more than 2 level of inheritance.
+ *
+ * <p>Servlets are connected that way:</p>
+ * <pre>
+ * SerlvetC
+ * ^
+ * |
+ * ServletB
+ * ^
+ * |
+ * ServletA
+ * </pre>
+ *
+ * @throws Exception if test fails
+ */
+ public void testThreeLevelInheritance() throws Exception {
+ servletA = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletAContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ super.service(request, response);
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ response.flushBuffer();
+ }
+
+ };
+
+ servletB = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletBContext); }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ super.service(request, response);
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ response.flushBuffer();
+ }
+ };
+
+ servletC = new HttpServlet() {
+ public ServletConfig getServletConfig() { return new ServletConfigWithContext(servletCContext); }
+
+ protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
+ super.service(req, response);
+ assertEquals(servletAContext, CallStackHelper.getBaseServletContext());
+ assertEquals(servletCContext, CallStackHelper.getCurrentServletContext());
+ res.setStatus(HttpServletResponse.SC_OK);
+ res.flushBuffer();
+ }
+ };
+ servletAContext.setServlet(servletA);
+ servletBContext.setServlet(servletB);
+ servletCContext.setServlet(servletC);
+
+ //connecting servlets
+ setMainConnection(servletA, "servletA");
+ connectServlets(servletA, servletB, "super");
+ connectServlets(servletB, servletC, "super");
+
+ RequestDispatcher dispatcher = mainContext.getNamedDispatcher("servletA");
+ dispatcher.forward(request, response);
+ assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+ assertTrue("Check if response has been committed", response.isCommitted());
+ }
+
+ //-----------------------------------------------------------------------------------------
+
+ private void connectServlets(HttpServlet connectFrom, HttpServlet connectTo, String connectionName) {
+ ServletServiceContext context = (ServletServiceContext)connectFrom.getServletConfig().getServletContext();
+ Map connections = new HashMap();
+ connections.put(connectionName, connectTo);
+ context.setConnections(connections);
+ }
+
+ private void setMainConnection(HttpServlet connectTo, String connectionName) {
+ Map connections = new HashMap();
+ connections.put(connectionName, connectTo);
+ mainContext.setConnections(connections);
+ }
+
+ private class ServletConfigWithContext implements ServletConfig {
+ private ServletContext context;
+
+ public ServletConfigWithContext(ServletContext context) {
+ this.context = context;
+ }
+
+ public String getInitParameter(String arg0) { return null; }
+ public Enumeration getInitParameterNames() { return null; }
+ public String getServletName() { return null; }
+
+ public ServletContext getServletContext() {
+ return context;
+ }
+ }
+
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java
new file mode 100644
index 0000000..282c3d7
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.servletservice.util;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Id$
+ */
+public class RequestParametersTestCase extends TestCase {
+
+ public void testParseMultiple() {
+ RequestParameters p = new RequestParameters("a=1&a=2&a=3&b=11&b=22");
+ String[] a = p.getParameterValues("a");
+ assertEquals(3, a.length);
+ assertEquals("1", a[0]);
+ assertEquals("2", a[1]);
+ assertEquals("3", a[2]);
+
+ String[] b = p.getParameterValues("b");
+ assertEquals(2, b.length);
+ assertEquals("11", b[0]);
+ assertEquals("22", b[1]);
+ }
+
+ public void testParseEncoded() {
+ RequestParameters p = new RequestParameters("a=one+two&b=one%3Ftwo&c=");
+ assertEquals("one two", p.getParameter("a"));
+ assertEquals("one?two", p.getParameter("b"));
+ }
+
+ public void testParseEncodedWrong() {
+ try {
+ new RequestParameters("a=one%");
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ new RequestParameters("a=one%3");
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ new RequestParameters("a=one%u");
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ new RequestParameters("a=one%u0");
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ new RequestParameters("a=one%u00");
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ new RequestParameters("a=one%u003");
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+
+ public void testParseUnicode() {
+ final char u = '\u11F3'; // Hangul character
+
+ RequestParameters p = new RequestParameters("a=a%E1%87%B3b&b=a%u11f3b");
+ assertEquals("a" + u + "b", p.getParameter("a"));
+ assertEquals("a" + u + "b", p.getParameter("b"));
+ }
+
+ public void testParseWSDL() {
+ assertEquals("", new RequestParameters("foo").getParameter("foo"));
+ }
+}
diff --git a/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/util/ServletServiceResponseTestCase.java b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/util/ServletServiceResponseTestCase.java
new file mode 100644
index 0000000..5bb105b
--- /dev/null
+++ b/cocoon-servlet-service-impl/cocoon-servlet-service-impl-1.2.0/src/test/java/org/apache/cocoon/servletservice/util/ServletServiceResponseTestCase.java
@@ -0,0 +1,45 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.cocoon.servletservice.util;
+
+import org.apache.commons.io.output.ByteArrayOutputStream;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Id$
+ */
+public class ServletServiceResponseTestCase extends TestCase {
+
+ /**
+ * Tests if its possible to write to outputStream after
+ * calling resetBuffer() method.
+ * @throws Exception
+ */
+ public void testResetBuffer() throws Exception {
+ ServletServiceResponse response = new ServletServiceResponse();
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ response.setOutputStream(outputStream);
+
+ response.getOutputStream();
+ response.resetBuffer();
+ response.getOutputStream().write(0);
+
+ assertEquals(1, outputStream.size());
+ }
+
+}