/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sling.engine.impl;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.engine.impl.request.RequestData;
public class SlingHttpServletResponseImpl extends HttpServletResponseWrapper implements SlingHttpServletResponse {
public static class WriterAlreadyClosedException extends IllegalStateException {
// just a marker class.
}
private final RequestData requestData;
private final boolean firstSlingResponse;
public SlingHttpServletResponseImpl(RequestData requestData,
HttpServletResponse response) {
super(response);
this.requestData = requestData;
this.firstSlingResponse = !(response instanceof SlingHttpServletResponse);
if (firstSlingResponse && RequestData.getAdditionalResponseHeaders() != null) {
for (StaticResponseHeader mapping: RequestData.getAdditionalResponseHeaders()) {
response.addHeader(mapping.getResponseHeaderName(), mapping.getResponseHeaderValue());
}
}
}
protected final RequestData getRequestData() {
return requestData;
}
//---------- Adaptable interface
public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
return getRequestData().adaptTo(this, type);
}
// ---------- Redirection support through PathResolver --------------------
@Override
public String encodeURL(final String url) {
// remove context path
String path = removeContextPath(url);
// make the path absolute
path = makeAbsolutePath(path);
// resolve the url to as if it would be a resource path
path = map(path);
// have the servlet container to further encodings
return super.encodeURL(path);
}
@Override
public String encodeRedirectURL(final String url) {
// remove context path
String path = removeContextPath(url);
// make the path absolute
path = makeAbsolutePath(path);
// resolve the url to as if it would be a resource path
path = map(path);
// have the servlet container to further encodings
return super.encodeRedirectURL(path);
}
@Override
@Deprecated
public String encodeUrl(final String url) {
return encodeURL(url);
}
@Override
@Deprecated
public String encodeRedirectUrl(final String url) {
return encodeRedirectURL(url);
}
// ---------- Error handling through Sling Error Resolver -----------------
@Override
public void sendError(int status) throws IOException {
sendError(status, null);
}
@Override
public void sendError(int status, String message) throws IOException {
checkCommitted();
SlingRequestProcessorImpl eh = getRequestData().getSlingRequestProcessor();
eh.handleError(status, message, requestData.getSlingRequest(), this);
}
// ---------- Internal helper ---------------------------------------------
@Override
public PrintWriter getWriter() throws IOException {
PrintWriter result = super.getWriter();
if ( firstSlingResponse ) {
final PrintWriter delegatee = result;
result = new PrintWriter(result) {
private boolean isClosed = false;
private void checkClosed() {
if ( this.isClosed ) {
throw new WriterAlreadyClosedException();
}
}
@Override
public PrintWriter append(final char arg0) {
this.checkClosed();
return delegatee.append(arg0);
}
@Override
public PrintWriter append(final CharSequence arg0, final int arg1, final int arg2) {
this.checkClosed();
return delegatee.append(arg0, arg1, arg2);
}
@Override
public PrintWriter append(final CharSequence arg0) {
this.checkClosed();
return delegatee.append(arg0);
}
@Override
public boolean checkError() {
this.checkClosed();
return delegatee.checkError();
}
@Override
public void close() {
this.checkClosed();
this.isClosed = true;
delegatee.close();
}
@Override
public void flush() {
this.checkClosed();
delegatee.flush();
}
@Override
public PrintWriter format(final Locale arg0, final String arg1,
final Object... arg2) {
this.checkClosed();
return delegatee.format(arg0, arg1, arg2);
}
@Override
public PrintWriter format(final String arg0, final Object... arg1) {
this.checkClosed();
return delegatee.format(arg0, arg1);
}
@Override
public void print(final boolean arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public void print(final char arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public void print(final char[] arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public void print(final double arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public void print(final float arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public void print(final int arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public void print(final long arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public void print(final Object arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public void print(final String arg0) {
this.checkClosed();
delegatee.print(arg0);
}
@Override
public PrintWriter printf(final Locale arg0, final String arg1,
final Object... arg2) {
this.checkClosed();
return delegatee.printf(arg0, arg1, arg2);
}
@Override
public PrintWriter printf(final String arg0, final Object... arg1) {
this.checkClosed();
return delegatee.printf(arg0, arg1);
}
@Override
public void println() {
this.checkClosed();
delegatee.println();
}
@Override
public void println(final boolean arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void println(final char arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void println(final char[] arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void println(final double arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void println(final float arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void println(final int arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void println(final long arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void println(final Object arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void println(final String arg0) {
this.checkClosed();
delegatee.println(arg0);
}
@Override
public void write(final char[] arg0, final int arg1, final int arg2) {
this.checkClosed();
delegatee.write(arg0, arg1, arg2);
}
@Override
public void write(final char[] arg0) {
this.checkClosed();
delegatee.write(arg0);
}
@Override
public void write(final int arg0) {
this.checkClosed();
delegatee.write(arg0);
}
@Override
public void write(final String arg0, final int arg1, final int arg2) {
this.checkClosed();
delegatee.write(arg0, arg1, arg2);
}
@Override
public void write(final String arg0) {
this.checkClosed();
delegatee.write(arg0);
}
};
}
return result;
}
private void checkCommitted() {
if (isCommitted()) {
throw new IllegalStateException(
"Response has already been committed");
}
}
private String makeAbsolutePath(String path) {
if (path.startsWith("/")) {
return path;
}
String base = getRequestData().getContentData().getResource().getPath();
int lastSlash = base.lastIndexOf('/');
if (lastSlash >= 0) {
path = base.substring(0, lastSlash+1) + path;
} else {
path = "/" + path;
}
return path;
}
private String map(String url) {
return getRequestData().getResourceResolver().map(getRequestData().getServletRequest(), url);
}
private String removeContextPath(final String path) {
final String contextPath = this.getRequestData().getSlingRequest().getContextPath().concat("/");
if ( contextPath.length() > 1 && path.startsWith(contextPath) ) {
return path.substring(contextPath.length() - 1);
}
return path;
}
}