/*
* ========================================================================== *
* Copyright (C) 2006, 2007 TAO Consulting Pte <http://www.taoconsulting.sg/> *
* based on work of * Copyright (C) 2004-2005 Pier Fumagalli
* <http://www.betaversion.org/~pier/> * All rights reserved. *
* ========================================================================== *
* * Licensed under the Apache License, Version 2.0 (the "License"). You may *
* not use this file except in compliance with the License. You may obtain a *
* copy of the License at <http://www.apache.org/licenses/LICENSE-2.0>. * *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT *
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the *
* License for the specific language governing permissions and limitations *
* under the License. * *
* ==========================================================================
*/
package biz.taoconsulting.dominodav.methods;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Stack;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import biz.taoconsulting.dominodav.LockManager;
import biz.taoconsulting.dominodav.interfaces.IDAVAddressInformation;
import biz.taoconsulting.dominodav.interfaces.IDAVProcessable;
import biz.taoconsulting.dominodav.interfaces.IDAVRepository;
import biz.taoconsulting.dominodav.interfaces.IDAVResource;
/**
* @author Bastian Buch (TAO Consulting), Stephan H. Wissel
*/
public abstract class AbstractDAVMethod implements IDAVProcessable {
/**
* The logger object for event logging
*/
private static final Log LOGGER = LogFactory
.getLog(AbstractDAVMethod.class);
/**
*
*/
private LockManager lockManager;
/**
*
*/
private IDAVRepository repository;
/**
*
*/
private HttpServletRequest req;
/**
*
*/
private HttpServletResponse resp;
/**
* The servlet context, only used for the MIME Type
*/
private ServletContext context = null;
/**
* What was the last httpStatus set?
*/
private Stack<String> lastHttpStatus = new Stack<String>();
/**
*
*/
private HashMap<String, String> headerValues;
/**
* Parameters we pass from one to the other methos
*/
private HashMap<String, String> headerAttributes;
/**
* Has the HTTPServletResponse.OutputStream been used
*/
private boolean streamUsedFlag = false;
/**
* This the DAV Method succeed or throw an error
*/
private boolean methodSuccess = true; // Innocent until proven guilty
/**
* The last error message to be retrieved by the servlet
*/
private String lastErrorMessage = null;
/**
* The original resource
*/
protected IDAVResource resource;
/**
* @see biz.taoconsulting.dominodav.interfaces.IDAVProcessable#process(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse,
* biz.taoconsulting.dominodav.interfaces.IDAVRepository,
* biz.taoconsulting.dominodav.LockManager)
*/
public void process(HttpServletRequest req, HttpServletResponse resp,
IDAVRepository repository, LockManager lockmanager)
throws IOException {
this.repository = repository;
this.req = req;
this.resp = resp;
this.lockManager = lockmanager;
// extracts values from the requestheader
this.interpretRequestHeader();
try {
this.writeInitialHeader();
} catch (Exception e) {
LOGGER.error("WriteInitialHeader failed", e);
}
try {
// LOGGER.info("Start action for method "+this.getClass());
this.action();
} catch (Exception e) {
LOGGER.error("Executing action failed:" + e.getMessage(), e);
// Status auf 500 setzne..
this.resp.setStatus(500);
PrintWriter pw = this.getOutputWriter();
pw.write("<h3>Error:" + e.getMessage() + "</h3>");
}
}
/**
*
*/
private void interpretRequestHeader() {
if (this.headerValues == null) {
this.headerValues = new HashMap<String, String>();
}
if (this.headerAttributes == null) {
this.headerAttributes = new HashMap<String, String>();
}
@SuppressWarnings("rawtypes")
Enumeration enumHeaders = this.req.getHeaderNames();
@SuppressWarnings("rawtypes")
Enumeration enumAttribute = this.req.getAttributeNames();
// extract all headers as key/value pairs
if (enumHeaders != null) {
while (enumHeaders.hasMoreElements()) {
String key = (String) (enumHeaders.nextElement());
String value = (String) this.req.getHeader(key);
this.headerValues.put(key, value);
// LOGGER.info("Header [" + key + "]:" + value);
}
}
try {
// extract additional standard header parameters
String contextpath = this.req.getContextPath();
String pathtranslated = this.req.getPathTranslated();
String contenttype = this.req.getContentType();
String characterencoding = this.req.getCharacterEncoding();
String querystring = this.req.getQueryString();
String uri = this.req.getRequestURI();
if (contextpath != null) {
this.headerValues.put("context-path", contextpath);
}
if (pathtranslated != null) {
this.headerValues.put("path-translated", pathtranslated);
}
if (contenttype != null) {
this.headerValues.put("content-type", contenttype);
}
if (characterencoding != null) {
this.headerValues.put("character-encoding", characterencoding);
}
if (querystring != null) {
this.headerValues.put("query-string", querystring);
}
if (uri != null) {
this.headerValues.put("uri", uri);
}
// We record the Path-Info (includes the repository) and the
// resource location which is the path-info minus the repository
// name
String pathInfo = this.req.getPathInfo();
if (pathInfo == null) {
LOGGER.debug("No Pathinfo found, defaulting to / for repository "
+ this.getRepositoryName());
pathInfo = "/";
} else {
// We translate the pathinfo from URL Encoded back into the
// normal form
try {
pathInfo = URLDecoder.decode(pathInfo, "UTF-8");
} catch (UnsupportedEncodingException e) {
LOGGER.error("Decoding pathInfo failed with UTF-8", e);
pathInfo = this.req.getPathInfo(); // Reset to the old value
}
}
LOGGER.debug("Path-info: " + pathInfo);
this.headerValues.put("path-info", pathInfo);
/*
* Now the resource-path, this is a bit more tricky Example: the
* servlet is "dav", the repository "rep" Both URLs must yield the
* same result: "/dav/rep" and "/dav/rep/"
*/
String resourcePath = this.req.getPathInfo();
String curName = this.getRepositoryName();
if (resourcePath == null || resourcePath.equals("")) {
resourcePath = "/";
} else {
try {
resourcePath = URLDecoder.decode(
resourcePath.replaceFirst("/" + curName, ""),
"UTF-8");
} catch (UnsupportedEncodingException e) {
LOGGER.error("Decoding resourcePath failed with UTF-8", e);
// Reset to the old value
resourcePath = this.req.getPathInfo().replaceFirst(
"/" + curName, "");
}
}
this.headerValues.put("resource-path", resourcePath);
LOGGER.debug("resource-path: " + resourcePath);
// List the attributes
if (enumAttribute != null) {
while (enumAttribute.hasMoreElements()) {
String key = (String) (enumAttribute.nextElement());
String value = (String) this.req.getAttribute(key);
this.headerAttributes.put(key, value);
// LOGGER.info("Attribute [" + key + "]:" + value);
}
}
} catch (Error e) {
LOGGER.error("Interpret request header failed:", e);
}
}
/**
* The interprete request header might get called without a repository
*
* @return
*/
private String getRepositoryName() {
if (this.repository == null) {
return "NullRepository";
}
return ((IDAVAddressInformation) this.repository).getName();
}
/**
* @throws Exception
* Thrown if action cannot run
*/
protected abstract void action() throws Exception;
/**
*
*/
protected abstract void writeInitialHeader();
/**
* @return ...
*/
protected HashMap<String, String> getHeaderAttributes() {
return this.headerAttributes;
}
/**
* @param headerAttributes
* ...
*/
protected void setHeaderAttributes(HashMap<String, String> headerAttributes) {
this.headerAttributes = headerAttributes;
}
/**
* @return ...
*/
protected HashMap<String, String> getHeaderValues() {
return this.headerValues;
}
/**
* @param headerValues
* ...
*/
protected void setHeaderValues(HashMap<String, String> headerValues) {
this.headerValues = headerValues;
}
/**
* @return the writer
*/
protected PrintWriter getOutputWriter() {
try {
return this.getResp().getWriter();
} catch (IOException exc) {
LOGGER.error("getOutput failed", exc);
return null;
}
}
/**
* @return the stream
*/
protected ServletOutputStream getOutputStream() {
try {
this.streamUsedFlag = true;
return this.getResp().getOutputStream();
} catch (IOException exc) {
LOGGER.error("getOutput failed", exc);
return null;
}
}
/**
* @return ...
*/
protected IDAVRepository getRepository() {
return this.repository;
}
/**
* @param repository
* ...
*/
protected void setRepository(IDAVRepository repository) {
this.repository = repository;
}
/**
* @return ...
*/
protected HttpServletRequest getReq() {
return this.req;
}
/**
* @param req
* ...
*/
protected void setReq(HttpServletRequest req) {
this.req = req;
}
/**
* @return ...
*/
protected HttpServletResponse getResp() {
return this.resp;
}
/**
* @param resp
* ...
*/
protected void setResp(HttpServletResponse resp) {
this.resp = resp;
}
/**
* @return Reference to the LockManager class
*/
public LockManager getLockManager() {
return this.lockManager;
}
/**
* @param lockManager
* Instance of the lock Manager
*/
public void setLockManager(LockManager lockManager) {
this.lockManager = lockManager;
}
/**
* @return ServletContext context : used to retrieve the mime-type
*/
// ToDo: Remove dependency to ServletContext
public ServletContext getContext() {
return this.context;
}
/**
* @param con
* ServletContext
*/
public void setContext(ServletContext con) {
this.context = con;
}
/**
* Last HTTP Status set in the method
*/
public String getLastHttpStatus() {
if (this.lastHttpStatus.isEmpty()) {
return IDAVProcessable.NO_STATUS_SET;
}
return this.lastHttpStatus.peek();
}
protected void setHTTPStatus(int newStatusInt) {
String newStatus = String.valueOf(newStatusInt);
if (!this.lastHttpStatus.isEmpty()) {
String oldStatus = this.lastHttpStatus.peek();
if (!oldStatus.equals(newStatus)) {
LOGGER.error("HTTP Status change from: " + oldStatus + " to "
+ newStatus);
}
}
this.resp.setStatus(newStatusInt);
this.lastHttpStatus.push(newStatus);
}
/**
* Did the execution of this message succeed
*/
public boolean didMethodSucceed() {
return this.methodSuccess;
}
/**
* The last error that had occured
*/
public String getErrorMessage() {
return this.lastErrorMessage;
}
/**
* If we have an error message we know it didn't work
*
* @param theMessage
* @param ErrorStatus
* - Http Error Code
*/
protected void setErrorMessage(String theMessage, int ErrorStatus) {
if (theMessage != null) {
this.methodSuccess = false;
this.lastErrorMessage = theMessage;
this.setHTTPStatus(ErrorStatus);
}
}
/**
* Has the Outputstream been used - prohibits the use of the PrintWriter
* then
*/
public boolean streamUsed() {
return this.streamUsedFlag;
}
/**
* Set the usageflag for the stream!
*/
protected void streamHasBeenUsed() {
this.streamUsedFlag = true;
}
}