AbstractWebdavMethod.java

Index Score
org.apache.slide.webdav.method
Jakarta Slide

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
LINE_COMMENTNumber of line comments
EXEC_COMMENTSComments in executable code
SIZESize of the file in bytes
BLOCKSNumber of blocks
CYCLOMATICCyclomatic complexity
DECL_COMMENTSComments in declarations
RETURNSNumber of return points from functions
LINESNumber of lines in the source file
ELOCEffective lines of code
LOGICAL_LINESNumber of statements
LOCLines of code
EXITSProcedure exits
OPERATORSNumber of operators
PROGRAM_LENGTHHalstead program length
OPERANDSNumber of operands
COMPARISONSNumber of comparison operators
INTERFACE_COMPLEXITYInterface complexity
UNIQUE_OPERANDSNumber of unique operands
PROGRAM_VOCABHalstead program vocabulary
FUNCTIONSNumber of function declarations
COMMENTSComment lines
DOC_COMMENTNumber of javadoc comment lines
JAVA0117JAVA0117 Missing javadoc: method 'method'
JAVA0166JAVA0166 Generic exception caught
JAVA0108JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0110JAVA0110 Incorrect javadoc: no @return tag
WHITESPACENumber of whitespace lines
PARAMSNumber of formal parameter declarations
JAVA0034JAVA0034 Missing braces in if statement
JAVA0163JAVA0163 Empty statement
LOOPSNumber of loops
JAVA0177JAVA0177 Variable declaration missing initializer
JAVA0170JAVA0170 Caught exception not derived from java.lang.Exception
JAVA0115JAVA0115 Incorrect javadoc: no @throws or @exception tag for 'exception'
JAVA0008JAVA0008 Empty catch block
JAVA0116JAVA0116 Missing javadoc: field 'field'
JAVA0075JAVA0075 Method parameter hides field
JAVA0254JAVA0254 Use enhanced for loop construct instead of Iterator
JAVA0007JAVA0007 Should not declare public field
JAVA0179JAVA0179 Local variable hides visible field
JAVA0049JAVA0049 Nested block at depth N (maximum: M)
JAVA0128JAVA0128 Public constructor in non-public class
JAVA0266JAVA0266 Use of System.out
JAVA0136JAVA0136 N methods defined in class (maximum: M)
JAVA0160JAVA0160 Method does not throw specified exception
JAVA0285JAVA0285 Dereference of potentially null variable
UNIQUE_OPERATORSNumber of unique operators
JAVA0100JAVA0100 Class contains N non-final fields (maximum: M)
NEST_DEPTHMaximum nesting depth
JAVA0126JAVA0126 Method declares unchecked exception in throws
JAVA0111JAVA0111 Incorrect javadoc: @return tag for void method
JAVA0119JAVA0119 Control variable changed within body of for loop
PROGRAM_VOLUMEHalstead program volume
JAVA0259JAVA0259 Return of collection/array field
JAVA0278JAVA0278 Unnecessary use of Boolean constructor
JAVA0132JAVA0132 Method overload with compatible signature
JAVA0043JAVA0043 Inner class does not use outer class
JAVA0109JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA0173JAVA0173 Unused method parameter
JAVA0067JAVA0067 Array descriptor on identifier name
JAVA0130JAVA0130 Non-static method does not use instance fields
JAVA0145JAVA0145 Tab character used in source file
/* * $Header$ * $Revision: 529868 $ * $Date: 2007-04-18 01:18:19 -0400 (Wed, 18 Apr 2007) $ * * ==================================================================== * * Copyright 1999-2002 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.slide.webdav.method; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.StringTokenizer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import org.apache.commons.transaction.locking.GenericLock; import org.apache.commons.transaction.locking.GenericLockManager; import org.apache.commons.transaction.locking.LockManager; import org.apache.commons.transaction.locking.MultiLevelLock; import org.apache.commons.transaction.util.PrintWriterLogger; import org.apache.slide.authenticate.CredentialsToken; import org.apache.slide.common.Domain; import org.apache.slide.common.NamespaceAccessToken; import org.apache.slide.common.NestedSlideException; import org.apache.slide.common.ServiceAccessException; import org.apache.slide.common.SlideException; import org.apache.slide.common.SlideToken; import org.apache.slide.content.Content; import org.apache.slide.content.NodeProperty; import org.apache.slide.content.NodeRevisionContent; import org.apache.slide.content.NodeRevisionDescriptor; import org.apache.slide.content.NodeRevisionDescriptors; import org.apache.slide.content.RevisionDescriptorNotFoundException; import org.apache.slide.content.NodeProperty.NamespaceCache; import org.apache.slide.lock.Lock; import org.apache.slide.lock.NodeLock; import org.apache.slide.lock.ObjectLockedException; import org.apache.slide.macro.ConflictException; import org.apache.slide.macro.Macro; import org.apache.slide.search.Search; import org.apache.slide.security.Security; import org.apache.slide.store.ConcurrencyConflictError; import org.apache.slide.structure.ObjectNode; import org.apache.slide.structure.ObjectNotFoundException; import org.apache.slide.structure.Structure; import org.apache.slide.transaction.ExternalTransactionContext; import org.apache.slide.util.Configuration; import org.apache.slide.util.Messages; import org.apache.slide.util.XMLValue; import org.apache.slide.util.logger.Logger; import org.apache.slide.webdav.WebdavException; import org.apache.slide.webdav.WebdavMethod; import org.apache.slide.webdav.WebdavServletConfig; import org.apache.slide.webdav.util.BindConstants; import org.apache.slide.webdav.util.DeltavConstants; import org.apache.slide.webdav.util.NotificationConstants; import org.apache.slide.webdav.util.PreconditionViolationException; import org.apache.slide.webdav.util.TransactionConstants; import org.apache.slide.webdav.util.UnlockListenerImpl; import org.apache.slide.webdav.util.UriHandler; import org.apache.slide.webdav.util.VersioningHelper; import org.apache.slide.webdav.util.ViolatedPrecondition; import org.apache.slide.webdav.util.WebdavConstants; import org.apache.slide.webdav.util.WebdavStatus; import org.apache.slide.webdav.util.WebdavUtils; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import org.jdom.output.XMLOutputter; /** * WebDAV method. * */ public abstract class AbstractWebdavMethod implements WebdavMethod, WebdavConstants, DeltavConstants, BindConstants, NotificationConstants, TransactionConstants { // -------------------------------------------------------------- Constants /** * String constant for <code>no-cache</code>. */ protected static final String NO_CACHE = "no-cache"; /** * String constant for <code>private</code>. */ protected static final String PRIVATE_CACHE = "private"; /** * String constant for <code>http://</code>. */ public static final String HTTP_PROTOCOL = "http://"; /** * String constant for <code>HTTP/1.1</code>. */ public static final String HTTP_VERSION = "HTTP/1.1"; /** * String constant for <code>text/xml</code>. */ public static final String TEXT_XML = "text/xml"; /** * String constant for <code>text/xml; charset="UTF-8"</code>. */ public static final String TEXT_XML_UTF_8 = "text/xml; charset=UTF-8"; /** * The indent to use in the XML response. */ public static final String XML_RESPONSE_INDENT = " "; private static final String LOG_CHANNEL = AbstractWebdavMethod.class.getName(); private static final String LOG_CHANNEL_LOCKING = LOG_CHANNEL + ".locking"; // public static final String PRINCIPAL = // "org.apache.slide.webdav.method.principal"; public static final int INFINITY = Integer.MAX_VALUE; protected static final Namespace DNSP = NamespaceCache.DEFAULT_NAMESPACE; /** * The set of SimpleDateFormat formats to use in getDateHeader(). */ protected static final SimpleDateFormat formats[] = { new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) }; private static final int LOCK_LOG_LEVEL = Logger.DEBUG; protected static final int READ_LOCK = 1; protected static final int WRITE_LOCK = 2; private static final int SHARED_LOCK = 1; private static final int EXCLUSIVE_LOCK = 2; // global read/write lock to allow for deadlock-free access private static final MultiLevelLock GLOBAL_LOCK = new GenericLock("global", WRITE_LOCK, new PrintWriterLogger(new PrintWriter(System.out), LOG_CHANNEL, false)); private static final LockManager FINE_LOCK_MANAGER = new GenericLockManager(WRITE_LOCK, new PrintWriterLogger(new PrintWriter(System.out), LOG_CHANNEL, false)); private static final GenericLock GLOBAL_READ_LOCK = new GenericLock("GLOBAL READ", EXCLUSIVE_LOCK, new PrintWriterLogger(new PrintWriter(System.out), LOG_CHANNEL, false)); private static final GenericLock GLOBAL_WRITE_LOCK = new GenericLock("GLOBAL WRITE", EXCLUSIVE_LOCK, new PrintWriterLogger(new PrintWriter(System.out), LOG_CHANNEL, false)); private static final int DEFAULT_MAX_RETRY_REPEATS = 1; // ----------------------------------------------------- Instance Variables /** * Requested Slide-Uri. */ protected String requestUri; /** * Servlet request. */ protected HttpServletRequest req; /** * Servlet response. */ protected HttpServletResponse resp; /** * Configuration. */ protected WebdavServletConfig config; protected String slideContextPath; /** * Request body. */ protected String requestBody; /** * Namespace access token. */ protected NamespaceAccessToken token; /** * Structure helper. */ protected Structure structure; /** * Content helper. */ protected Content content; /** * Security helper. */ protected Security security; /** * Lock helper. */ protected Lock lock; /** wam * Search helper. */ protected Search search; /** * Macro helper. */ protected Macro macro; /** * Versioning helper. */ protected VersioningHelper versioningHelper; /** * Slide token. */ protected SlideToken slideToken; /** * Fine grain locks acquired by this method. */ protected Set locks = new HashSet(); /** * The request content (XML) Document. */ private Document requestContentDocument = null; /** * Indicates if the request content has already been parsed. */ private boolean isRequestContentParsed = false; /** * Request headers */ protected RequestHeaders requestHeaders = new RequestHeaders(); /** * Indicates if the request will be repeated upon concurrency failure. */ protected boolean retryUponConflict; // -------------------------------------------------- Static Initialization // ----------------------------------------------------------- Constructors /** * Constructor. * * @param token the token for accessing the namespace * @param config configuration of the WebDAV servlet */ public AbstractWebdavMethod(NamespaceAccessToken token, WebdavServletConfig config) { this.config = config; this.token = token; // initialize helpers structure = token.getStructureHelper(); content = token.getContentHelper(); security = token.getSecurityHelper(); lock = token.getLockHelper(); macro = token.getMacroHelper(); } // -------------------------------------------- WebdavMethod Implementation /** * Exceute method. * * @exception WebdavException */ public void run(HttpServletRequest req, HttpServletResponse resp) throws WebdavException { // XXX this is a pretty ugly spot and way to set this // TODO find a better solution UriHandler.setGloballyUseHistoryCollectionHack(useHistoryCollectionHack()); this.req = req; this.resp = resp; this.slideToken = WebdavUtils.getSlideToken(req); String forceLowercaseLogin = token.getNamespaceConfig().getParameter("force-lowercase-login"); if ("true".equals(forceLowercaseLogin)) { String name = slideToken.getCredentialsToken().getPrincipal().getName().toLowerCase(); slideToken.setCredentialsToken(new CredentialsToken(name)); } this.requestUri = WebdavUtils.getRelativePath(req, config); this.slideContextPath = req.getContextPath(); if (!this.config.isDefaultServlet()) { this.slideContextPath += req.getServletPath(); } // TODO this is a workaround to pass the slideContextPath to the search // implementation slideToken.addParameter("slideContextPath", this.slideContextPath); this.versioningHelper = VersioningHelper.getVersioningHelper( slideToken, token, req, resp, getConfig(), this); parseRequestHeaders(); boolean transactionIsStarted = false; boolean globalLockObtained = false; String txId = null; try { parseRequest(); ExternalTransactionContext externalTransaction = null; txId = requestHeaders.getTxId(); if (txId != null) { externalTransaction = ExternalTransactionContext.lookupContext(txId); if (externalTransaction != null) { Domain.log("Using external transaction " + txId, LOG_CHANNEL, Logger.INFO); slideToken.setExternalTx(); // pure reads must be guaranteed to be inside transaction as well slideToken.setForceStoreEnlistment(true); Transaction tx = externalTransaction.getTransaction(); token.getTransactionManager().resume(tx); transactionIsStarted = true; } } if (!slideToken.isExternalTransaction()) { token.begin(); transactionIsStarted = true; if (txForAllRequests()) { slideToken.setForceStoreEnlistment(true); } assureGlobalLocks(); globalLockObtained = true; } boolean done = false; retryUponConflict = isRepeatUponConflict() && !slideToken.isExternalTransaction(); int retries = getMaxRetryRepeats(); ConcurrencyConflictError finalCce = null; while (!done && retries != 0) { try { executeRedirect(); done = true; } catch (ConcurrencyConflictError cce) { finalCce = cce; if (retryUponConflict) { retries--; token.getLogger().log(cce.getMessage(), LOG_CHANNEL, Logger.WARNING); token.rollback(); token.begin(); } else { break; } } } if (!done) { // rethrow it as an ordinary conflict exception throw new ConflictException(finalCce.getUri(), finalCce); } if (!slideToken.isExternalTransaction() && transactionIsStarted) { token.commit(); transactionIsStarted = false; } } catch (WebdavException ex) { // Ignore the WebDav Exception and assume that the response code // is already set. } catch (SlideException ex) { int statusCode = getErrorCode( ex ); if (statusCode == WebdavStatus.SC_UNAUTHORIZED) { // This means the user is required to authenticate (correctly), // so add the specific header and invalidate session to force // new authentication String authentication = config.getAuthenticationHeader(); resp.setHeader("WWW-Authenticate", authentication); req.getSession().invalidate(); } sendError( statusCode, ex ); // do not throw exception as the response code has already been set, // otherwise the servlet will log this as an error and issue a stack trace // throw new WebdavException( statusCode ); } catch (Exception ex) { token.getLogger().log(ex,LOG_CHANNEL,Logger.ERROR); int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR; sendError( statusCode, ex ); throw new WebdavException( statusCode ); } finally { try { if (!slideToken.isExternalTransaction() && transactionIsStarted) { // Something went wrong, we are here and the TA is still // open try { token.rollback(); } catch (Exception e) { token.getLogger().log(e,LOG_CHANNEL,Logger.ERROR); } } if (slideToken.isExternalTransaction()) { Transaction transaction; try { if (token.getStatus() == javax.transaction.Status.STATUS_ACTIVE) { transaction = token.getTransactionManager().suspend(); if (transaction != null) { ExternalTransactionContext.registerContext(txId, transaction); } } } catch (SystemException e) { token.getLogger().log(e,LOG_CHANNEL,Logger.ERROR); } } } finally { if (globalLockObtained) { releaseGlobalLocks(); } } } } protected void executeRedirect() throws IOException, SlideException, IllegalStateException, SecurityException, HeuristicMixedException, HeuristicRollbackException, RollbackException, NotSupportedException, SystemException { /* * Check for object existence and cleanup locks but only if this is * not a request to finalize an external transaction. Otherwise we are * making calls to the store that require a transaction to be in process * while we're trying to commit or abort the current transaction. */ if (!isEndofTransactionRequest()) { try { // retrive to check it exists, otherwise it can't have locks structure.retrieve(slideToken, requestUri); // clear expired lock-tokens UnlockListenerImpl listener = new UnlockListenerImpl(slideToken, token, versioningHelper); lock.clearExpiredLocks(slideToken, requestUri, listener); if (listener.getUnlockCount() > 0) { // If we have have cleared any lock or any lock-null resource in // the previous step we commit this changes, otherwise they will // be lost if executeRequest() exits with an exception (e.g. // because of Not Found 404) token.commit(); token.begin(); } } catch (ObjectNotFoundException e) { // ignore, it can't have locks } } boolean responseIsRedirected = false; /* * Check whether to apply to redirect ref but only if this is not an * external transaction request to commit or abort the current transaction. * Otherwise db deadlock can occur. */ if (!requestHeaders.getApplyToRedirectRef(false) && !isEndofTransactionRequest()) { try { NodeRevisionDescriptors revisionDescriptors = content.retrieve(slideToken, requestUri); NodeRevisionDescriptor revisionDescriptor = content.retrieve(slideToken, revisionDescriptors); if (WebdavUtils.isRedirectref(revisionDescriptor)) { NodeProperty refTarget = revisionDescriptor.getProperty(PN_REFTARGET); // Add the header indicating that this redirect is the // result of a redirect reference. resp.addHeader(H_REDIRECT_REF, refTarget.getValue().toString()); // Set the location header for the redirect. resp.addHeader("Location", refTarget.getValue().toString()); // Determine the appropriate status code. boolean permanent = revisionDescriptor.propertyValueContains( P_REDIRECT_LIFETIME, "permanent"); if (permanent) { resp.setStatus(WebdavStatus.SC_MOVED_PERMANENTLY); resp.addHeader(H_CACHE_CONTROL, PRIVATE_CACHE); } else { resp.setStatus(WebdavStatus.SC_MOVED_TEMPORARILY); resp.addHeader(H_CACHE_CONTROL, NO_CACHE); } // Set this flag so that we don't attempt to execute the // request on the redirect reference itself. responseIsRedirected = true; } } catch (ObjectNotFoundException notFound) { // TODO is this really an error? // token.getLogger().log("Error while trying to send redirect", // notFound, LOG_CHANNEL, Logger.ERROR); } catch (RevisionDescriptorNotFoundException e) { // TODO not sure, but this makes VcPutVHR testcase passing } catch (ObjectLockedException e) { // TODO not sure, but this makes nonOwnerUsesLocktoken testcase passing } } if (!responseIsRedirected) { executeRequest(); } } // --------------------------------------------------------- Public Methods /** * Returns a boolean indicating whether this request is an * external transaction request to commit or abort the * current transaction. These requests must be given a * short path to UnlockMethod.execute() that avoids db * interaction -- even reads. * * @return true if the current request is an external transaction commit or abort request. */ public boolean isEndofTransactionRequest() { /* * if this is an UnlockMethod and has a transaction * command set in the request then this request is * trying to commit or abort an external transaction. */ if (this instanceof UnlockMethod) { UnlockMethod meth = (UnlockMethod)this; if (meth.getCommand() != UnlockMethod.NO_TRANSACTION) return true; } return false; } /** * Returns the configuration of the WebdavServlet. * * @return WebdavServletConfig */ public WebdavServletConfig getConfig() { return config; } /** * Return an absolute URL path (absolute in the HTTP sense) based on a Slide * path. */ public String getFullPath(String slidePath) { return WebdavUtils.getAbsolutePath(slidePath, req, getConfig()); } /** * Returns a Slide path based on an absolute URL * (absolute in the HTTP sense) */ public String getSlidePath(String fullpath) { return WebdavUtils.getSlidePath(fullpath, getSlideContextPath()); } public String getSlideContextPath() { return this.slideContextPath; } /** * Returns the slide uri requested. */ public String getRequestUri() { return this.requestUri; } public SlideToken getSlideToken() { return this.slideToken; } public NamespaceAccessToken getNamespaceAccessToken() { return this.token; } public Content getContent() { return content; } public Lock getLock() { return lock; } public Macro getMacro() { return macro; } public Search getSearch() { return search; } public Security getSecurity() { return security; } public Structure getStructure() { return structure; } public VersioningHelper getVersioning() { return versioningHelper; } public HttpServletRequest getRequest() { return this.req; } public HttpServletResponse getResponse() { return this.resp; } // ------------------------------------------------------ Protected Methods /** * Read request contents. * * @param req Request object handed out by the servlet container * @return char[] Array of char which contains the body of the request */ protected void readRequestContent() { if (req.getContentLength() == 0) return; // TODO : Modify this and make it chunking aware try { requestBody = new String(NodeRevisionContent.read(req.getInputStream()), getEncodingString(req.getCharacterEncoding())); } catch (Exception e) { token.getLogger().log(e,LOG_CHANNEL,Logger.ERROR); } } /** * Translate the encoding string into a proper Java encoding String. */ public static String getEncodingString(String httpEncoding) { String result = httpEncoding; if (result == null) result = System.getProperty("file.encoding"); if (result.startsWith("\"")) result = result.substring(1, result.length()); if (result.endsWith("\"")) result = result.substring(0, result.length()-1); return result; } /** * Method parseHeaders * */ private void parseRequestHeaders() throws WebdavException { requestHeaders.parse(); } /** * Test if a resource given by a path is a collection */ protected boolean isCollection(String path) { return WebdavUtils.isCollection(token, slideToken, path); } /** * Test whether the resource given by lowerNode is a descendant of the * resource given by upperNode * * @param lowerNode an ObjectNode * @param upperNode an ObjectNode * * @return true, if lowerNode is below upperNode in the namespace * @throws ServiceAccessException * */ protected boolean isDescendant( ObjectNode lowerNode, ObjectNode upperNode ) throws ServiceAccessException { if (lowerNode.getUuri().equals(upperNode.getUuri())) { return true; } if (upperNode.hasBinding(lowerNode)) { return true; } NodeRevisionDescriptors lowerNrds = null; NodeRevisionDescriptor lowerNrd = null; try { lowerNrds = content.retrieve(slideToken, lowerNode.getUri()); lowerNrd = content.retrieve(slideToken, lowerNrds); NodeProperty psProp = lowerNrd.getProperty(PN_PARENT_SET); // FIXME psProp may be null XMLValue xv = new XMLValue( String.valueOf(psProp.getValue()) ); Iterator i = xv.getList().iterator(); while (i.hasNext()) { // iterate over parent elements Element pElm = (Element)i.next(); String hrPath = pElm.getChild( E_HREF, DNSP ).getText(); ObjectNode hrNode = structure.retrieve( slideToken, hrPath ); return isDescendant( hrNode, upperNode ); } } catch (ServiceAccessException e) { throw e; } catch (Exception e) {} return false; } protected boolean isRequestChunked() { String te = req.getHeader("Transfer-Encoding"); if (te == null) return false; return te.indexOf("chunked") != -1; } /** * Parse WebDAV XML query. * * @exception WebdavException */ protected abstract void parseRequest() throws WebdavException; /** * Returns the request content (XML) Document. * * @return the request content (XML) Document. */ protected Document getRequestContent() { return requestContentDocument; } //-- /** * precondition: sourceUri != null */ protected String parseUri(String uri) throws WebdavException { // TODO: better name int protocolIndex = uri.indexOf("://"); if (protocolIndex >= 0) { // if the Destination URL contains the protocol, we can safely // trim everything upto the first "/" character after "://" int firstSeparator = uri.indexOf("/", protocolIndex + 4); if (firstSeparator < 0) { uri = "/"; } else { uri = uri.substring(firstSeparator); } } else { String hostName = req.getServerName(); if ((hostName != null) && (uri.startsWith(hostName))) { uri = uri.substring(hostName.length()); } int portIndex = uri.indexOf(":"); if (portIndex >= 0) { uri = uri.substring(portIndex); } if (uri.startsWith(":")) { int firstSeparator = uri.indexOf("/"); if (firstSeparator < 0) { uri = "/"; } else { uri = uri.substring(firstSeparator); } } } // headers are "ISO-8859-1" encoded [not any more with TC 4.1.18 uri = WebdavUtils.normalizeURL(WebdavUtils.fixTomcatURL(uri)); String contextPath = req.getContextPath(); if ((contextPath != null) && (uri.startsWith(contextPath))) { uri = uri.substring(contextPath.length()); } String pathInfo = req.getPathInfo(); if (pathInfo != null) { String servletPath = req.getServletPath(); if ((servletPath != null) && (uri.startsWith(servletPath))) { uri = uri.substring(servletPath.length()); } } uri = getConfig().getScope() + uri; return uri; } protected Element parseRequestContent(String rootName) throws JDOMException, IOException { Document document; Element root; document = parseRequestContent(); if (document == null) { throw new JDOMException("Request content missing"); } root = document.getRootElement(); if( root == null || !root.getName().equals(rootName) ) { Domain.warn( "Root element must be "+rootName ); throw new JDOMException("Root element must be <"+rootName+">"); } return root; } /** * Parses the request content (XML) Document. * * @return the request content (XML) Document. * * @throws IOException if an I/O error occurred. * @throws JDOMException if parsing the document failed. */ protected Document parseRequestContent() throws JDOMException, IOException { if (isRequestContentParsed) { return requestContentDocument; } if (req.getContentLength() == 0 || req.getContentLength() == -1) { return requestContentDocument; } try { requestContentDocument = new SAXBuilder().build(req.getInputStream()); isRequestContentParsed = true; } catch (JDOMException e) { if (e.getCause() instanceof IOException) { throw (IOException)e.getCause(); } else { throw e; } } return requestContentDocument; } /** * Generate XML response. * * @exception WebdavException */ protected abstract void executeRequest() throws WebdavException, IOException; /** * Simulate MS IIS5 ? * * @return boolean */ protected boolean isMsProprietarySupport() { return (token.getNamespaceConfig().getParameter("ms") != null); } /** * Sends a precondition vilolation response. * * @param pve the ProconditionViolationException that describes the violated * precondition. */ protected void sendPreconditionViolation(PreconditionViolationException pve) throws IOException { if (pve != null) { ViolatedPrecondition violatedPrecondition = pve.getViolatedPrecondition(); int statusCode = violatedPrecondition.getStatusCode(); printStackTrace( pve, statusCode ); String statusText = WebdavStatus.getStatusText(statusCode); if (violatedPrecondition.getExplanation() != null) { statusText = statusText+": "+violatedPrecondition.getExplanation(); } resp.setStatus(statusCode, statusText); resp.setContentType(TEXT_XML_UTF_8); org.jdom.output.Format format = org.jdom.output.Format.getPrettyFormat(); format.setIndent(XML_RESPONSE_INDENT); new XMLOutputter(format). output(new Document(MethodUtil.getPreconditionViolationError(pve.getViolatedPrecondition())), resp.getWriter()); } } // -------------------------------------------------------- Private Methods /** * Get return status based on exception type. */ protected int getErrorCode(Throwable ex) { return WebdavUtils.getErrorCode(ex); } /** * Get return status based on exception type. */ protected int getErrorCode(SlideException ex) { return WebdavUtils.getErrorCode(ex); } /** * Get return status based on exception type. */ protected int getErrorCode(ServiceAccessException ex) { return WebdavUtils.getErrorCode(ex); } /** * Returns the value of a boolean init parameter of the servlet. * Default value: false. */ protected boolean getBooleanInitParameter( String name ) { return "true".equalsIgnoreCase( getConfig().getInitParameter(name) ); } protected boolean isAutoCreateUsers() { return ("true".equalsIgnoreCase(token.getNamespaceConfig().getParameter("auto-create-users"))); } protected String getUsersPath() { return token.getNamespaceConfig().getUsersPath(); } protected String getRolesPath() { return token.getNamespaceConfig().getRolesPath(); } protected String getHistoryPath() { return Domain.getParameter(I_HISTORYPATH, I_HISTORYPATH_DEFAULT); } /** * Checks whether the hack that restricts the size of collections in the * history collection is configured to be used. */ protected boolean useHistoryCollectionHack() { return "true".equalsIgnoreCase(token.getNamespaceConfig().getParameter("history-collection-hack")); } /** * Checks whether all requests shall be done inside of transactions. */ protected boolean txForAllRequests() { return "true".equalsIgnoreCase(token.getNamespaceConfig().getParameter("all-methods-in-transactions")); } /** * Checks if Slide is configured to allow at most a single write request at a time. * @return <code>true</code> if there can be at most one write request at a time */ protected boolean isSequentialWrite() { String sm = token.getNamespaceConfig().getParameter("sequential-mode"); return ("write".equalsIgnoreCase(sm) || "full".equalsIgnoreCase(sm)); } /** * Checks if Slide is configured to allow reads while write requests are being executed. * @return <code>true</code> if reads are disallowed during writes */ protected boolean isSequentialRead() { return "full".equalsIgnoreCase(token.getNamespaceConfig().getParameter("sequential-mode")); } /** * Checks if Slide is configured to restrict access using fine grained * locks. * * @return <code>true</code> if Slide is configured to lock in a fain * grained way */ protected boolean isFineGrainSequential() { return "fine-grain".equalsIgnoreCase(token.getNamespaceConfig().getParameter( "sequential-mode")); } protected boolean isFailFastSequential() { return "fail-fast".equalsIgnoreCase(token.getNamespaceConfig().getParameter( "sequential-mode")); } /** * Checks if Slide is configured to internally repeat a request if it conflicts with other * concurrent ones. */ protected boolean isRepeatUponConflict() { return "true".equalsIgnoreCase(token.getNamespaceConfig().getParameter( "repeat-upon-conflict")); } /** * Checks if Slide is configured to internally repeat a request if it conflicts with other * concurrent ones. */ protected int getMaxRetryRepeats() { String maxRepeat = token.getNamespaceConfig().getParameter("max-retry-repeat"); if (maxRepeat != null) { try { int repeats = Integer.parseInt(maxRepeat); return repeats; } catch (NumberFormatException nfe) { token.getLogger().log("max-retry-repeat must be an integer, using default", LOG_CHANNEL_LOCKING, Logger.WARNING); } } return DEFAULT_MAX_RETRY_REPEATS; } protected void assureGlobalLocks() throws ConflictException { if (isFailFastSequential()) { boolean locked = true; if (this instanceof ReadMethod) { try { // NON-BLOCKING locked = GLOBAL_LOCK.acquire(this, 1, false, true, Long.MAX_VALUE); } catch (InterruptedException e) { } } else if (this instanceof WriteMethod) { try { // NON-BLOCKING locked = GLOBAL_LOCK.acquire(this, 2, false, true, Long.MAX_VALUE); } catch (InterruptedException e) { } } if (!locked) throw new ConflictException("/"); } else if (this instanceof FineGrainedLockingMethod && isFineGrainSequential()) { ((FineGrainedLockingMethod) this).acquireFineGrainLocks(); } else if (this instanceof ReadMethod) { if (isSequentialRead()) { try { GLOBAL_LOCK.acquire(this, 1, true, true, Long.MAX_VALUE); } catch (InterruptedException e) { } } } else if (this instanceof WriteMethod) { if (isSequentialWrite()) { try { GLOBAL_LOCK.acquire(this, 2, true, true, Long.MAX_VALUE); } catch (InterruptedException e) { } } } } protected void releaseGlobalLocks() { GLOBAL_LOCK.release(this); if (this instanceof FineGrainedLockingMethod && isFineGrainSequential()) { // no need to do this atomically releaseLocks(); } } protected void acquireHistoryLocks(String path) { // some writing request have an additional request to the history // folder if auto versioning is turned on if (Configuration.useVersionControl() && isAutoVersionControl(path) && !isExcludedForVersionControl(path)) { acquireLock(getHistoryPath(), this instanceof WriteMethod ? WRITE_LOCK : READ_LOCK); } } /** * Acquires a global read lock to disallow any writing. * Should be used by methods that do not allow any writing while running. */ protected void acquireGlobalReadLock() { // we have a shared global read lock which means we are compatible with // any other read acquireLock(GLOBAL_READ_LOCK, SHARED_LOCK); // this means no other write request may run in parallel, but if another global read // has the lock, we will *not* be blocked blockWithLock(GLOBAL_WRITE_LOCK, EXCLUSIVE_LOCK); } /** * Acquires a global write lock to disallow any concurrent access. */ protected void acquireGlobalWriteLock() { // we really want to read and write alone: acquireLock(GLOBAL_READ_LOCK, EXCLUSIVE_LOCK); acquireLock(GLOBAL_WRITE_LOCK, EXCLUSIVE_LOCK); } /** * Inhibits a global read lock. Should be called by all fine grain writing methods. */ protected void inhibitGlobalReadLock() { // we have a shared global write lock which means other fine grained write // methods are compatible acquireLock(GLOBAL_WRITE_LOCK, SHARED_LOCK); // do not allow global read as long as we write to any resource, but // if another global write has the lock, we will *not* be blocked blockWithLock(GLOBAL_READ_LOCK, EXCLUSIVE_LOCK); } /** * Acquires the standard locks applicable to most methods. Those are * <ol> * <li>READ locks for the accessed path and all ancestors on all levels up * to the root * <li>recursive READ locks to the users and roles folder and an additional * write lock to the users folder if auto creation of users is enabled * <li>upgrade requests to descendants of the history folder to the full * history folder, effectively making all requests to the history folder * global * <li>assures no writing requests run concurrently with a search request * as it is unpredictable which resources a search will touch * </ol> * * @param path * the path to acquire standard locks for */ protected void acquireStandardLocks(String path) { // (1) all requests need read access from uri to root List paths = getPathsToRoot(path); acquireLock(paths, READ_LOCK); // (2) all requests need read access to users and roles and even write // when users are auto created String usersPath = getUsersPath(); String rolesPath = getRolesPath(); acquireLock(usersPath, isAutoCreateUsers() ? WRITE_LOCK : READ_LOCK); acquireLock(rolesPath, READ_LOCK); // (2a) adapt explicite calls to users and roles to general lock // above // this effectively gives a *single* lock for the users and roles folder // each if (path.startsWith(getUsersPath())) { acquireLock(usersPath, this instanceof WriteMethod ? WRITE_LOCK : READ_LOCK); } else if (path.startsWith(rolesPath)) { acquireLock(rolesPath, this instanceof WriteMethod ? WRITE_LOCK : READ_LOCK); } // (3) adapt explicite read to history folder to general lock // above // this effectively gives a *single* lock for history folder if (Configuration.useVersionControl() && path.startsWith(getHistoryPath())) { acquireLock(getHistoryPath(), READ_LOCK); } // (4) be sure to write only when no search or other global read currently takes place // this is the counter part to acquireSearchLock() if (this instanceof WriteMethod) { inhibitGlobalReadLock(); } } /** * Locks the parent of a path with either a read or a write lock. * * @param path * the child of the path to lock * @param level * either {@link #READ_LOCK} or {@link #WRITE_LOCK} * * @see #acquireLock(String, int) */ protected void acquireParentLock(String path, int level) { int lastSlash = path.lastIndexOf('/'); if (lastSlash > 0) { String parentPath = path.substring(0, lastSlash); acquireLock(parentPath, level); } } /** * Locks a path with either a read or a write lock. <br> * <br> * <em>Caution #1:</em> All methods need read locks to all ancestors on * all levels. Effectively this means a write lock is always recursive and * applies to all descendants on all levels. * * <br> * <br> * <em>Caution #2:</em> A write lock implies a read lock. * * @param path * the path to lock * @param level * either {@link #READ_LOCK}or {@link #WRITE_LOCK} */ protected void acquireLock(String path, int level) { GenericLock lock = (GenericLock) FINE_LOCK_MANAGER.atomicGetOrCreateLock(path); acquireLock(lock, level); } /** * Acquires a lock. * * @param lock * the lock to acquire * @param level * either {@link #READ_LOCK} or {@link #WRITE_LOCK} * * @see #acquireLock(String, int) */ protected void acquireLock(GenericLock lock, int level) { try { lock.acquire(this, level, true, true, Long.MAX_VALUE); } catch (InterruptedException e) { } locks.add(lock); token.getLogger().log(this + (level == READ_LOCK ? " read" : " write") + " locking " + lock.getResourceId().toString(), LOG_CHANNEL_LOCKING, LOCK_LOG_LEVEL); } /** * Acquires a supporting block lock. * * @param lock * the lock to acquire * @param level * either {@link #READ_LOCK} or {@link #WRITE_LOCK} * * @see #acquireLock(String, int) */ protected void blockWithLock(GenericLock lock, int level) { try { lock.acquire(this, level, true, GenericLock.COMPATIBILITY_REENTRANT_AND_SUPPORT, Long.MAX_VALUE); } catch (InterruptedException e) { } locks.add(lock); token.getLogger().log( this + (level == READ_LOCK ? " read" : " write") + " locking " + lock.getResourceId().toString(), LOG_CHANNEL_LOCKING, LOCK_LOG_LEVEL); } /** * Locks a list of paths with either a read or a write lock. * * @param paths * the paths to lock * @param level * either {@link #READ_LOCK} or {@link #WRITE_LOCK} * * @see #acquireLock(String, int) */ protected void acquireLock(List paths, int level) { for (Iterator i = paths.iterator(); i.hasNext();) { String path = (String) i.next(); acquireLock(path, level); } } protected void releaseLocks() { GenericLock lock; for (Iterator i = locks.iterator(); i.hasNext();) { lock = (GenericLock) i.next(); lock.release(this); token.getLogger().log( this + " releasing " + lock.getResourceId().toString(), LOG_CHANNEL_LOCKING, LOCK_LOG_LEVEL); } } protected List getPathsToRoot(String uri) { StringTokenizer tokenizer = new StringTokenizer(uri, "/"); List paths = new ArrayList(tokenizer.countTokens() + 1); paths.add("/"); String path = ""; while (tokenizer.hasMoreTokens()) { path += "/" + tokenizer.nextToken(); paths.add(path); } return paths; } /** * Returns the value of an integer init parameter of the servlet. * Default value: -1. */ protected int getIntInitParameter( String name ) { int result = -1; try { result = Integer.parseInt( getConfig().getInitParameter(name) ); } catch( NumberFormatException x ) {}; return result; } /** * Error handling routine */ protected void sendError( int statusCode ) { try { resp.sendError( statusCode ); } catch( Throwable x ) {}; } /** * Error handling routine */ protected void sendError( int statusCode, String message ) { String statusText = WebdavStatus.getStatusText(statusCode)+ (message != null ? ": "+Messages.format( message, (Object)null ) : ""); try { resp.sendError( statusCode, statusText ); } catch( Throwable x ) {}; } /** * Error handling routine */ protected void sendError( int statusCode, String message, Object[] args ) { String statusText = WebdavStatus.getStatusText(statusCode)+": "+ Messages.format( message, args ); try { resp.sendError( statusCode, statusText ); } catch( Throwable x ) {}; } /** * Error handling routine */ protected void sendError( int statusCode, Throwable t ) { printStackTrace( t, statusCode ); String explanation = (t == null || t.getMessage() == null || "".equals(t.getMessage()) ? Messages.format(t.getClass().getName(), (Object)null) : t.getMessage() ); String statusText = WebdavStatus.getStatusText(statusCode)+": "+explanation; try { resp.sendError( statusCode, statusText ); } catch( Throwable x ) {}; } /** * Prints the stack trace of the given exception if the specified status code * is greater-or-equal the value of the servlet init-parameter printStackTrace. * If the init-parameter is not specified, stack traces are printed for status * codes >= 500. */ protected void printStackTrace( Throwable x, int statusCode ) { int printStackTraceFrom = getIntInitParameter( "printStackTrace" ); if( printStackTraceFrom < 0 ) printStackTraceFrom = 500; if( statusCode >= printStackTraceFrom ) x.printStackTrace(); } /** * Generate status text. * * @param parentElement the parent Element to append to. * @param href Slide-Uri of the object * @param statusCode HTTP status code of the error */ protected void generateStatusText(Element parentElement, String href, int statusCode) { Element hrefElement = new Element(E_HREF, DNSP); parentElement.addContent(hrefElement); hrefElement.setText(getFullPath(href)); Element statusElement = new Element(E_STATUS, DNSP); parentElement.addContent(statusElement); statusElement.setText("HTTP/1.1 " + statusCode + " " + WebdavStatus.getStatusText(statusCode)); } /** * Generate an XML error message. * * @param macroException Nested exception * @return String XML message */ protected String generateErrorMessage (NestedSlideException nestedException) { Element multistatus = new Element(E_MULTISTATUS, DNSP); Enumeration nestedExceptionsList = nestedException.enumerateExceptions(); while (nestedExceptionsList.hasMoreElements()) { Element response = new Element(E_RESPONSE, DNSP); multistatus.addContent(response); SlideException ex = (SlideException) nestedExceptionsList.nextElement(); generateStatusText(response, MethodUtil.getErrorMessage(ex), getErrorCode(ex)); if (ex instanceof PreconditionViolationException) { response.addContent(MethodUtil.getPreconditionViolationResponseDescription((PreconditionViolationException)ex)); } } StringWriter stringWriter = new StringWriter(); try { new XMLOutputter().output(multistatus, stringWriter); } catch (IOException e) { Domain.log(e); } return stringWriter.toString(); } protected boolean exists( String uriStr ) throws SlideException { boolean destinationExists = true; try { content.retrieve(slideToken, uriStr); } catch (ObjectNotFoundException x) { destinationExists = false; } return destinationExists; } protected boolean isLocked( String uriStr ) throws ServiceAccessException { // use a non-blocking slide token. boolean isLocked = false; try { Enumeration locks = lock.enumerateLocks (slideToken, uriStr, false); while (locks.hasMoreElements()) { if (lock.isLocked(slideToken,(NodeLock) locks.nextElement(),false)) { isLocked = true; } } } catch (ServiceAccessException x) { throw x; } catch (SlideException x) { // ignore silently } return isLocked; } protected boolean isLockNull( String uriStr ) throws ServiceAccessException { boolean isLockNull = false; try { NodeRevisionDescriptor nrd = content.retrieve(slideToken, content.retrieve(slideToken, uriStr)); isLockNull = isLockNull( nrd ); } catch (ServiceAccessException x) { throw x; } catch (SlideException x) { // ignore silently } return isLockNull; } protected boolean isLockNull( NodeRevisionDescriptor nrd ) { return nrd.propertyValueContains(P_RESOURCETYPE, E_LOCKNULL); } protected boolean isAutoVersionControl(String resourcePath) { return new Boolean(Domain.getParameter(I_AUTO_VERSION_CONTROL, I_AUTO_VERSION_CONTROL_DEFAULT, token.getUri(slideToken, resourcePath).getStore())) .booleanValue(); } protected boolean isExcludedForVersionControl(String resourcePath) { String versionControlExcludePaths = Domain.getParameter(I_VERSIONCONTROL_EXCLUDEPATH, I_VERSIONCONTROL_EXCLUDEPATH_DEFAULT, token.getUri(slideToken, resourcePath).getStore()); if (versionControlExcludePaths != null && versionControlExcludePaths.length() > 0) { StringTokenizer st = new StringTokenizer(versionControlExcludePaths, ";"); while (st.hasMoreTokens()) { if (isExcluded(resourcePath, st.nextToken())) { return true; } } } return false; } private boolean isExcluded(String resourcePath, String excludePath) { UriHandler uh = UriHandler.getUriHandler(resourcePath); if (excludePath != null && excludePath.length() > 0) { UriHandler exUh = UriHandler.getUriHandler(excludePath); if (exUh.isAncestorOf(uh)) { return true; } } return false; } /** * Check if the conditions specified in the optional If headers are * satisfied. * * @param request The servlet request we are processing * @param response The servlet response we are creating * @param resourceInfo File object * @return boolean true if the resource meets all the specified conditions, * and false if any of the conditions is not satisfied, in which case * request processing is stopped */ protected boolean checkIfHeaders(HttpServletRequest request, HttpServletResponse response, ResourceInfo resourceInfo) throws IOException { // the ETag without apostrophes ("), we use apostrophes as delimiters // to because some clients provide If header with out apostrophes String eTag = getETagValue(resourceInfo, true); long lastModified = resourceInfo.date; StringTokenizer commaTokenizer; String headerValue; // Checking If-Match headerValue = request.getHeader("If-Match"); if (headerValue != null) { if (headerValue.indexOf("*") == -1) { commaTokenizer = new StringTokenizer(headerValue, ", \""); boolean matchingTagFound = false; while (!matchingTagFound && commaTokenizer.hasMoreTokens()) { matchingTagFound = commaTokenizer.nextToken().equals(eTag); } // If none of the given ETags match, 412 Precodition failed is // sent back if (!matchingTagFound) { response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } else { if (!resourceInfo.exists()) { response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } } // Checking If-Modified-Since headerValue = request.getHeader("If-Modified-Since"); if (headerValue != null) { // If an If-None-Match header has been specified, if modified since // is ignored. if (request.getHeader("If-None-Match") == null) { Date date = parseHttpDate(headerValue); if ((date != null) && (lastModified <= (date.getTime() + 1000)) ) { // The entity has not been modified since the date // specified by the client. This is not an error case. response.sendError (HttpServletResponse.SC_NOT_MODIFIED); return false; } } } // Checking If-None-Match headerValue = request.getHeader("If-None-Match"); if (headerValue != null) { if (headerValue.indexOf("*") == -1) { commaTokenizer = new StringTokenizer(headerValue, ", \""); while (commaTokenizer.hasMoreTokens()) { if (commaTokenizer.nextToken().equals(eTag)) { // For GET and HEAD, we respond with 304 Not Modified. // For every other method, 412 Precondition Failed if ( ("GET".equals(request.getMethod())) || ("HEAD".equals(request.getMethod())) ) { response.sendError( HttpServletResponse.SC_NOT_MODIFIED); return false; } else { response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } } } else { if (resourceInfo.exists()) { response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } } // Checking If-Unmodified-Since headerValue = request.getHeader("If-Unmodified-Since"); if (headerValue != null) { Date date = parseHttpDate(headerValue); if ( (date != null) && (lastModified > date.getTime()) ) { // The entity has not been modified since the date // specified by the client. This is not an error case. response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } return true; } /** * Parses the date string given as one of the {@link #formats}. * If the date does not fit any of these formats it returns <code>null</code>. * * @param headerValue date string from and HTTP header (e.g. If-Modified) * @return a Date representing the date given of <code>null</code> if * the string has no valid format. */ protected Date parseHttpDate(String headerValue) { Date date = null; // Parsing the HTTP Date for (int i = 0; (date == null) && (i < formats.length); i++) { try { synchronized (formats[i]) { date = formats[i].parse(headerValue); } } catch (ParseException e) { // ignore the invalid format and try the next } } return date; } /** * Get the ETag value associated with a file. * * @param resourceInfo File object * @param strong True if we want a strong ETag, in which case a checksum * of the file has to be calculated */ protected String getETagValue(ResourceInfo resourceInfo, boolean strong) { // FIXME : Compute a strong ETag if requested, using an MD5 digest // of the file contents if (resourceInfo.exists()) { return resourceInfo.etag; } else { return resourceInfo.length + "-" + resourceInfo.date; } } /** * Get the ETag associated with a file. * * @param resourceInfo File object * @param strong True if we want a strong ETag, in which case a checksum * of the file has to be calculated */ protected String getETag(ResourceInfo resourceInfo, boolean strong) { if (strong) return "\"" + getETagValue(resourceInfo, strong) + "\""; else return "W/\"" + getETagValue(resourceInfo, strong) + "\""; } protected class ResourceInfo { /** * Constructor. * * @param path Path name of the resource */ public ResourceInfo(String path, NodeRevisionDescriptor properties) { this.path = path; this.exists = true; this.creationDate = properties.getCreationDateAsDate().getTime(); this.date = properties.getLastModifiedAsDate().getTime(); this.httpDate = properties.getLastModified(); this.length = properties.getContentLength(); this.etag = properties.getETag(); } /** * Creates a ResourceInfo for a non existing resource. * @param path Path of the resource */ public ResourceInfo(String path) { this.path = path; this.exists = false; this.length = 0; this.date = System.currentTimeMillis(); } public String path; public long creationDate; public String httpDate; public long date; public long length; public String etag; //public boolean collection; public boolean exists; /** * Test if the associated resource exists. */ public boolean exists() { return exists; } /** * String representation. */ public String toString() { return path; } } protected class RequestHeaders { private static final int ST_UNDEFINED = 0, ST_INVALID = 1, ST_DEFINED = 2; // raw headers private String hIfStr; private String hLockTokenStr; private String hDepthStr; private String hDestinationStr; private String hOverwriteStr; private String hTimeoutStr; private String hLabelStr; private String hNotificationTypeStr; private String hCallbackStr; private String hSubscriptionIdStr; private String hNotificationDelayStr; private String hSubscriptionLifetimeStr; private String hTxIdStr; private String hTxMethodStr; private String hContentTypeStr; private String hApplyToRedirectRefStr; // parsed headers private List hIf; private String hLockToken; private int hDepth; private String hDestination; private boolean hOverwrite; private int hTimeout; private String hLabel; private String hNotificationType; private String hCallback; private int []hSubscriptionId; private int hNotificationDelay; private int hSubscriptionLifetime; private String hTxId; private String hTxMethod; private String hContentType; private boolean hApplyToRedirectRef; // states private int stIf = ST_UNDEFINED; private int stLockToken = ST_UNDEFINED; private int stDepth = ST_UNDEFINED; private int stDestination = ST_UNDEFINED; private int stOverwrite = ST_UNDEFINED; private int stTimeout = ST_UNDEFINED; private int stLabel = ST_UNDEFINED; private int stNotificationType = ST_UNDEFINED; private int stCallback = ST_UNDEFINED; private int stSubscriptionId = ST_UNDEFINED; private int stNotificationDelay = ST_UNDEFINED; private int stSubscriptionLifetime = ST_UNDEFINED; private int stTxId = ST_UNDEFINED; private int stTxMethod= ST_UNDEFINED; private int stContentType = ST_UNDEFINED; private int stApplyToRedirectRef = ST_UNDEFINED; protected RequestHeaders() { } protected boolean isDefined( String header ) { return req.getHeader(header) != null; } protected List getIf() throws WebdavException { if (stIf == ST_UNDEFINED) { return Collections.EMPTY_LIST; } else if (stIf == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header If: "+hIfStr ); throw new WebdavException( sc ); } else { return hIf; } } protected String getLockToken() throws WebdavException { if (stLockToken == ST_UNDEFINED) { return null; } else if (stLockToken == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header LockToken: "+hLockTokenStr ); throw new WebdavException( sc ); } else { return hLockToken; } } protected int getDepth( int defaultValue ) throws WebdavException { if (stDepth == ST_UNDEFINED) { return defaultValue; } else if (stDepth == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header Depth: "+hDepthStr ); throw new WebdavException( sc ); } else { return hDepth; } } protected String getDestination() throws WebdavException { if (stDestination == ST_UNDEFINED) { return null; } else if (stDestination == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header Destination: "+hDestinationStr ); throw new WebdavException( sc ); } else { return hDestination; } } protected boolean getOverwrite( boolean defaultValue ) throws WebdavException { if (stOverwrite == ST_UNDEFINED) { return defaultValue; } else if (stOverwrite == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header Overwrite: "+hOverwriteStr ); throw new WebdavException( sc ); } else { return hOverwrite; } } protected String getLabel() throws WebdavException { if (stLabel == ST_UNDEFINED) { return null; } else if (stLabel == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header Label: "+hLabelStr ); throw new WebdavException( sc ); } else { return hLabel; } } protected int getTimeout( int defaultValue) throws WebdavException { if (stTimeout == ST_UNDEFINED) { return defaultValue; } else if (stTimeout == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header Timeout: "+hTimeoutStr ); throw new WebdavException( sc ); } else { return hTimeout; } } protected String getNotificationType() throws WebdavException { if (stNotificationType == ST_UNDEFINED) { return null; } else if (stNotificationType == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid notification type: "+hNotificationTypeStr ); throw new WebdavException( sc ); } else { return hNotificationType; } } protected int []getSubscriptionId() throws WebdavException { if (stSubscriptionId == ST_UNDEFINED) { return new int[0]; } else if (stSubscriptionId == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid subscription ID: "+hSubscriptionIdStr ); throw new WebdavException( sc ); } else { return hSubscriptionId; } } protected String getCallback() throws WebdavException { if (stCallback == ST_UNDEFINED) { return null; } else if (stCallback == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid callback: "+hCallbackStr ); throw new WebdavException( sc ); } else { return hCallback; } } protected int getNotificationDelay( int defaultValue ) throws WebdavException { if (stNotificationDelay == ST_UNDEFINED) { return defaultValue; } else if (stNotificationDelay == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid notification delay: "+hNotificationDelayStr ); throw new WebdavException( sc ); } else { return hNotificationDelay; } } protected int getSubscriptionLifetime( int defaultValue ) throws WebdavException { if (stSubscriptionLifetime == ST_UNDEFINED) { return defaultValue; } else if (stSubscriptionLifetime == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid subscription lifetime: "+hSubscriptionLifetimeStr ); throw new WebdavException( sc ); } else { return hSubscriptionLifetime; } } protected String getTxId() throws WebdavException { if (stTxId == ST_UNDEFINED) { return null; } else if (stTxId == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid transaction id: "+hTxIdStr ); throw new WebdavException( sc ); } else { return hTxId; } } protected String getTxMethod() throws WebdavException { if (stTxMethod == ST_UNDEFINED) { return null; } else if (stTxMethod == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid transaction method: "+hTxMethodStr ); throw new WebdavException( sc ); } else { return hTxMethod; } } protected String getContentType() { if (stContentType == ST_UNDEFINED) { return null; } else { return hContentType; } } protected boolean getApplyToRedirectRef( boolean defaultValue ) throws WebdavException { if (stApplyToRedirectRef == ST_UNDEFINED) { return defaultValue; } else if (stApplyToRedirectRef == ST_INVALID) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header Apply-To-Redirect-Ref: " + hApplyToRedirectRefStr ); throw new WebdavException( sc ); } else { return hApplyToRedirectRef; } } protected void parse() { // TransactionId header hTxIdStr = req.getHeader(H_TRANSACTION); if (hTxIdStr != null) { stTxId = ST_DEFINED; try { hTxId = hTxIdStr; } catch (Exception e) { stTxId = ST_INVALID; } } // TransactionMethod header hTxMethodStr = req.getHeader(H_TRANSACTION_METHOD); if (hTxMethodStr != null) { stTxMethod = ST_DEFINED; try { hTxMethod = hTxMethodStr; } catch (Exception e) { stTxMethod = ST_INVALID; } } // NotificationType header hNotificationTypeStr = req.getHeader(H_NOTIFICATION_TYPE); if (hNotificationTypeStr != null) { stNotificationType = ST_DEFINED; try { hNotificationType = hNotificationTypeStr; } catch (Exception e) { stNotificationType = ST_INVALID; } } // NotificationDelay header hNotificationDelayStr = req.getHeader(H_NOTIFICATION_DELAY); if (hNotificationDelayStr != null) { stNotificationDelay = ST_DEFINED; try { hNotificationDelay = Integer.parseInt(hNotificationDelayStr); } catch (Exception e) { stNotificationDelay = ST_INVALID; } } // SubscriptionLifetime header hSubscriptionLifetimeStr = req.getHeader(H_SUBSCRIPTION_LIFETIME); if (hSubscriptionLifetimeStr != null) { stSubscriptionLifetime = ST_DEFINED; try { hSubscriptionLifetime = Integer.parseInt(hSubscriptionLifetimeStr); } catch (Exception e) { stSubscriptionLifetime = ST_INVALID; } } // SubscriptionID header hSubscriptionIdStr = req.getHeader(H_SUBSCRIPTION_ID); if (hSubscriptionIdStr != null) { stSubscriptionId = ST_DEFINED; try { StringTokenizer tokenizer = new StringTokenizer(hSubscriptionIdStr, ","); hSubscriptionId = new int[tokenizer.countTokens()]; int i = 0; while ( tokenizer.hasMoreTokens() ) { hSubscriptionId[i] = Integer.parseInt(tokenizer.nextToken().trim()); i++; } } catch (Exception e) { stSubscriptionId = ST_INVALID; } } // Call back header hCallbackStr = req.getHeader(H_CALL_BACK); if (hCallbackStr != null) { stCallback = ST_DEFINED; try { hCallback = hCallbackStr; } catch (Exception e) { stCallback = ST_INVALID; } } // If header hIfStr = req.getHeader(H_IF); if (hIfStr != null) { stIf = ST_DEFINED; try { hIf = extractLockTokens(hIfStr); } catch (Exception e) { stIf = ST_INVALID; } } // Lock-Token header hLockTokenStr = req.getHeader(H_LOCK_TOKEN); if (hLockTokenStr != null) { stLockToken = ST_DEFINED; try { List tl = extractLockTokens(hLockTokenStr); hLockToken = (String)tl.get(0); } catch (Exception e) { stLockToken = ST_INVALID; } } // Depth header hDepthStr = req.getHeader(H_DEPTH); if (hDepthStr != null) { stDepth = ST_DEFINED; if ("0".equals(hDepthStr)) { hDepth = 0; } else if ("1".equals(hDepthStr)) { hDepth = 1; } else if ("infinity".equalsIgnoreCase(hDepthStr)) { hDepth = INFINITY; } else { stDepth = ST_INVALID; hDepth = Integer.parseInt(hDepthStr); } } // Destination header hDestinationStr = req.getHeader(H_DESTINATION); if (hDestinationStr != null) { stDestination = ST_DEFINED; hDestination = hDestinationStr; } // Overwrite header hOverwriteStr = req.getHeader(H_OVERWRITE); if (hOverwriteStr != null) { stOverwrite = ST_DEFINED; if ("T".equalsIgnoreCase(hOverwriteStr)) { hOverwrite = true; } else if ("F".equalsIgnoreCase(hOverwriteStr)) { hOverwrite = false; } else { stOverwrite = ST_INVALID; } } // Timeout header hTimeoutStr = req.getHeader(H_TIMEOUT); if (hTimeoutStr != null) { stTimeout = ST_DEFINED; try { hTimeout = extractLockDuration( hTimeoutStr ); } catch (Exception e) { stTimeout = ST_INVALID; } } // Label header hLabelStr = req.getHeader(H_LABEL); if (hLabelStr != null) { stLabel = ST_DEFINED; hLabel = hLabelStr; } // Apply-To-Redirect-Ref header hApplyToRedirectRefStr = req.getHeader(H_OVERWRITE); if (hApplyToRedirectRefStr != null) { stApplyToRedirectRef = ST_DEFINED; if ("T".equalsIgnoreCase(hApplyToRedirectRefStr)) { hApplyToRedirectRef = true; } else if ("F".equalsIgnoreCase(hApplyToRedirectRefStr)) { hApplyToRedirectRef = false; } else { stApplyToRedirectRef = ST_INVALID; } } // Content-Type header hContentTypeStr = req.getHeader(H_CONTENT_TYPE); if (hContentTypeStr != null) { stContentType = ST_DEFINED; hContentType = hContentTypeStr; } } private List extractLockTokens(String hStr) { List result = new ArrayList(); int pos = hStr.indexOf(S_LOCK_TOKEN); int endPos = -1; int offset = S_LOCK_TOKEN.length(); String lockToken = null; while (pos != -1) { endPos = hStr.indexOf('>', pos + offset); if (endPos == -1) { lockToken = hStr; endPos = hStr.length(); } else { lockToken = hStr.substring(pos + offset, endPos); } //System.out.println("Lock Token found :-" + lockToken + "-"); slideToken.addLockToken(lockToken); result.add( lockToken ); pos = hStr.indexOf(S_LOCK_TOKEN, endPos); } return result; } private int extractLockDuration(String hStr) { int result; int firstCommaPos = hStr.indexOf(','); if (firstCommaPos != -1) { hStr = hStr.substring(0, firstCommaPos); } if (hStr.startsWith("Second-")) { result = Integer.parseInt(hStr.substring("Second-".length())); } else { if (hStr.equalsIgnoreCase("Infinite")) { result = INFINITY; } else { result = Integer.parseInt(hStr); } } return result; } } }

The table below shows all metrics for AbstractWebdavMethod.java.

MetricValueDescription
BLOCKS369.00Number of blocks
BLOCK_COMMENT38.00Number of block comment lines
COMMENTS555.00Comment lines
COMMENT_DENSITY 0.48Comment density
COMPARISONS189.00Number of comparison operators
CYCLOMATIC339.00Cyclomatic complexity
DECL_COMMENTS99.00Comments in declarations
DOC_COMMENT394.00Number of javadoc comment lines
ELOC1154.00Effective lines of code
EXEC_COMMENTS78.00Comments in executable code
EXITS218.00Procedure exits
FUNCTIONS108.00Number of function declarations
HALSTEAD_DIFFICULTY98.63Halstead difficulty
HALSTEAD_EFFORT 0.00Halstead effort
INTERFACE_COMPLEXITY280.00Interface complexity
JAVA0001 0.00JAVA0001 Package name does not contain only lower case letters
JAVA0002 0.00JAVA0002 Package name does not begin with a top level domain name or country code
JAVA0003 0.00JAVA0003 Minimize use of on-demand (.*) imports
JAVA0004 0.00JAVA0004 Unnecessary import from java.lang
JAVA0005 0.00JAVA0005 Imports not in specified order
JAVA0006 0.00JAVA0006 Empty finally block
JAVA0007 7.00JAVA0007 Should not declare public field
JAVA0008 6.00JAVA0008 Empty catch block
JAVA0009 0.00JAVA0009 Protected member in final class
JAVA0010 0.00JAVA0010 Non-instantiable class does not contain a non-private static member
JAVA0011 0.00JAVA0011 Abstract class does not contain an abstract method
JAVA0012 0.00JAVA0012 Non-constructor method with same name as declaring class
JAVA0013 0.00JAVA0013 Non-blank final field is not static
JAVA0014 0.00JAVA0014 Class with only static members has non-private constructor
JAVA0015 0.00JAVA0015 Package class contains public nested type
JAVA0016 1.00JAVA0016 Abstract class contains public constructor
JAVA0017 0.00JAVA0017 Class name does not have required form
JAVA0018 0.00JAVA0018 Method name does not have required form
JAVA0019 0.00JAVA0019 Interface name does not have required form
JAVA0020 0.00JAVA0020 Field name does not have required form
JAVA0021 0.00JAVA0021 Interface method name does not have required form
JAVA0022 0.00JAVA0022 Static final field name does not have required form
JAVA0023 0.00JAVA0023 Empty finalize method
JAVA0024 0.00JAVA0024 Empty class
JAVA0025 0.00JAVA0025 Method override is empty
JAVA0026 0.00JAVA0026 Finalize method with parameters
JAVA0029 0.00JAVA0029 Private method not used
JAVA0030 0.00JAVA0030 Private field not used
JAVA0031 0.00JAVA0031 Case statement not properly closed
JAVA0032 0.00JAVA0032 Switch statement missing default
JAVA0033 0.00JAVA0033 default: not last case in switch statement
JAVA003411.00JAVA0034 Missing braces in if statement
JAVA0035 0.00JAVA0035 Missing braces in for statement
JAVA0036 0.00JAVA0036 Missing braces in while statement
JAVA0038 0.00JAVA0038 Non-case label in switch statement
JAVA0039 0.00JAVA0039 Break statement with label
JAVA0040 0.00JAVA0040 Switch statement contains N cases (maximum: M)
JAVA0041 0.00JAVA0041 Nested synchronized block
JAVA0042 0.00JAVA0042 Empty synchronized statement
JAVA0043 1.00JAVA0043 Inner class does not use outer class
JAVA0044 0.00JAVA0044 Serializable class with no instance variables
JAVA0045 0.00JAVA0045 Serializable class with only transient fields
JAVA0046 0.00JAVA0046 Name of class not derived from Exception ends with 'Exception'
JAVA0047 0.00JAVA0047 Serializable class derives from invalid base class
JAVA0048 0.00JAVA0048 Name of class derived from Exception does not end with 'Exception'
JAVA0049 3.00JAVA0049 Nested block at depth N (maximum: M)
JAVA0050 0.00JAVA0050 Class derives from java.lang.Error
JAVA0051 0.00JAVA0051 Class derives from java.lang.RuntimeException
JAVA0052 0.00JAVA0052 Class derives from java.lang.Throwable
JAVA0053 0.00JAVA0053 Unused label
JAVA0054 0.00JAVA0054 Inheritance depth N exceeds maximum M
JAVA0055 0.00JAVA0055 Class should be interface
JAVA0056 0.00JAVA0056 Unnecessary abstract modifier for interface or annotation
JAVA0057 1.00JAVA0057 Unnecessary default constructor
JAVA0058 0.00JAVA0058 Constructor calls super()
JAVA0059 0.00JAVA0059 Method override only calls super()
JAVA0061 0.00JAVA0061 Inaccessible member in anonymous class
JAVA0062 0.00JAVA0062 Public class missing public member or protected constructor
JAVA0063 0.00JAVA0063 Identifier name should not contain '$'
JAVA0064 0.00JAVA0064 N variations of identifier name (maximum: M)
JAVA0065 0.00JAVA0065 Unnecessary final modifier for method in final class
JAVA0066 0.00JAVA0066 Unnecessary modifier for interface nested type
JAVA0067 1.00JAVA0067 Array descriptor on identifier name
JAVA0068 0.00JAVA0068 Modifiers not declared in recommended order
JAVA0071 0.00JAVA0071 Strings compared with ==
JAVA0073 0.00JAVA0073 Integer division in floating-point context
JAVA0074 0.00JAVA0074 Use of Object.notify()
JAVA0075 4.00JAVA0075 Method parameter hides field
JAVA0076 0.00JAVA0076 Use of magic number
JAVA0077 0.00JAVA0077 Private field not used in declaring class
JAVA0078 0.00JAVA0078 Floating point values compared with ==
JAVA0079 0.00JAVA0079 Use of instance to reference static member
JAVA0080 0.00JAVA0080 Import declaration not used
JAVA0081 0.00JAVA0081 Boolean literal in comparison
JAVA0082 0.00JAVA0082 Unnecessary widening cast
JAVA0083 0.00JAVA0083 Unnecessary instanceof test
JAVA0084 1.00JAVA0084 Should use compound assignment operator
JAVA0085 0.00JAVA0085 Use of sun.* class
JAVA0087 0.00JAVA0087 Use of Thread.sleep()
JAVA0089 0.00JAVA0089 Use of restricted package
JAVA0092 0.00JAVA0092 Use of restricted type
JAVA0093 0.00JAVA0093 Redundant assignment
JAVA0094 0.00JAVA0094 Field hides a superclass field