ozeigermann 2004/12/07 09:03:03
Modified: src/stores/org/apache/slide/store/impl/rdbms
MySqlRDBMSAdapter.java
src/webdav/server/org/apache/slide/webdav/method
AbstractWebdavMethod.java
src/stores/org/apache/slide/store/txfile
AbstractTxFileStoreService.java
Added: src/share/org/apache/slide/store
ConcurrencyConflictException.java
Log:
Added rudimentary support for internal and transparent repeat
of conflicting transactions.
Revision Changes Path
1.11 +7 -5
jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/MySqlRDBMSAdapter.java
Index: MySqlRDBMSAdapter.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/MySqlRDBMSAdapter.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- MySqlRDBMSAdapter.java 11 Nov 2004 17:15:35 -0000 1.10
+++ MySqlRDBMSAdapter.java 7 Dec 2004 17:03:02 -0000 1.11
@@ -35,6 +35,7 @@
import org.apache.slide.content.*;
import org.apache.slide.common.*;
import org.apache.slide.macro.ConflictException;
+import org.apache.slide.store.ConcurrencyConflictException;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.logger.Logger;
@@ -61,7 +62,8 @@
switch (e.getErrorCode()) {
case 1213 : // thread was deadlock victim
getLogger().log(e.getErrorCode() + ": Deadlock resolved on "
+ uri, LOG_CHANNEL, Logger.WARNING);
- return new ServiceAccessException(service, new
ConflictException(uri));
+ throw new ConcurrencyConflictException(e, uri);
+// throw new ServiceAccessException(this, new
ConflictException(uri));
default :
getLogger().log(
1.64 +112 -45
jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/AbstractWebdavMethod.java
Index: AbstractWebdavMethod.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/AbstractWebdavMethod.java,v
retrieving revision 1.63
retrieving revision 1.64
diff -u -r1.63 -r1.64
--- AbstractWebdavMethod.java 6 Dec 2004 08:54:13 -0000 1.63
+++ AbstractWebdavMethod.java 7 Dec 2004 17:03:02 -0000 1.64
@@ -61,12 +61,19 @@
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.event.VetoException;
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.AccessDeniedException;
import org.apache.slide.security.Security;
+import org.apache.slide.store.ConcurrencyConflictException;
+import org.apache.slide.structure.LinkedObjectNotFoundException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.Structure;
@@ -185,6 +192,9 @@
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
@@ -413,53 +423,32 @@
} catch (ObjectNotFoundException e) {
// ignore, it can't have locks
}
+
+ boolean done = false;
+ boolean retryUponConflict = isRepeatUponConflict() &&
!slideToken.isExternalTransaction();
+ int retries = getMaxRetryRepeats();
- boolean responseIsRedirected = false;
- if (!requestHeaders.getApplyToRedirectRef(false)) {
- try {
- NodeRevisionDescriptors revisionDescriptors
- = content.retrieve(slideToken, requestUri);
- NodeRevisionDescriptor revisionDescriptor
- = content.retrieve(slideToken,
revisionDescriptors);
- if (WebdavUtils.isRedirectref(revisionDescriptor)) {
- NodeProperty refTarget
- =
revisionDescriptor.getProperty(P_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);
- }
+ ConcurrencyConflictException finalCce = null;
- // Set this flag so that we don't attempt to execute
the
- // request on the redirect reference itself.
- responseIsRedirected = true;
+ while (!done && retries != 0) {
+ try {
+ executeRedirect();
+ done = true;
+ } catch (ConcurrencyConflictException cce) {
+ finalCce = cce;
+ if (retryUponConflict) {
+ retries--;
+ token.getLogger().log(cce.getMessage(), LOG_CHANNEL,
Logger.WARNING);
+ token.rollback();
+ token.begin();
+ } else {
+ break;
}
}
- catch (ObjectNotFoundException notFound) {
- token.getLogger().log("Error while trying to send
redirect",
- notFound, LOG_CHANNEL, Logger.ERROR);
- }
}
-
- if (!responseIsRedirected) {
- executeRequest();
+ if (!done) {
+ // rethrow it as an ordinary conflict exception
+ throw new ConflictException(finalCce.getUri(), finalCce);
}
if (!slideToken.isExternalTransaction() && transactionIsStarted)
{
@@ -512,6 +501,57 @@
}
}
+ protected void executeRedirect() throws AccessDeniedException,
LinkedObjectNotFoundException,
+ ObjectLockedException, RevisionDescriptorNotFoundException,
ServiceAccessException,
+ VetoException, WebdavException, IOException {
+ boolean responseIsRedirected = false;
+ if (!requestHeaders.getApplyToRedirectRef(false)) {
+ try {
+ NodeRevisionDescriptors revisionDescriptors
+ = content.retrieve(slideToken, requestUri);
+ NodeRevisionDescriptor revisionDescriptor
+ = content.retrieve(slideToken, revisionDescriptors);
+ if (WebdavUtils.isRedirectref(revisionDescriptor)) {
+ NodeProperty refTarget
+ = revisionDescriptor.getProperty(P_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) {
+ token.getLogger().log("Error while trying to send redirect",
+ notFound, LOG_CHANNEL, Logger.ERROR);
+ }
+ }
+ if (!responseIsRedirected) {
+ executeRequest();
+ }
+ }
+
// --------------------------------------------------------- Public
Methods
@@ -921,6 +961,33 @@
"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() {
if (this instanceof FineGrainedLockingMethod &&
isFineGrainSequential()) {
synchronized (ATOMIC_LOCKING_MONITOR) {
1.17 +7 -4
jakarta-slide/src/stores/org/apache/slide/store/txfile/AbstractTxFileStoreService.java
Index: AbstractTxFileStoreService.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/AbstractTxFileStoreService.java,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- AbstractTxFileStoreService.java 9 Aug 2004 09:35:20 -0000 1.16
+++ AbstractTxFileStoreService.java 7 Dec 2004 17:03:02 -0000 1.17
@@ -38,6 +38,7 @@
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
+import org.apache.slide.store.ConcurrencyConflictException;
import org.apache.slide.util.logger.TxLogger;
import org.apache.slide.util.logger.Logger;
@@ -408,7 +409,9 @@
getLogChannel(),
Logger.INFO);
- throw new ServiceAccessException(this, new
ConflictException(uri));
+// throw new ServiceAccessException(this, new
ConflictException(uri));
+ throw new
ConcurrencyConflictException(((ResourceManagerException) cause).getStatus(),
+ ((ResourceManagerException) cause).getMessage(), uri);
} else {
1.1
jakarta-slide/src/share/org/apache/slide/store/ConcurrencyConflictException.java
Index: ConcurrencyConflictException.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-slide/src/share/org/apache/slide/store/ConcurrencyConflictException.java,v
1.1 2004/12/07 17:03:03 ozeigermann Exp $
* $Revision: 1.1 $
* $Date: 2004/12/07 17:03:03 $
*
* ====================================================================
*
* 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.store;
import java.sql.SQLException;
/**
* Exception gets thrown from the store layer upon failure caused by
* other concurrent requests. Such failures include deadlocks, unserializable
* transactions and other spurious behaviour caused by insufficient isolation.
*
* @version $Revision: 1.1 $
*/
public class ConcurrencyConflictException extends Error {
protected int errorCode;
protected String uri;
public ConcurrencyConflictException(int errorCode, String message, String
uri) {
super(message);
this.errorCode = errorCode;
this.uri = uri;
}
public ConcurrencyConflictException(SQLException e, String uri) {
this(e.getErrorCode(), e.getMessage(), uri);
}
public String toString() {
StringBuffer buf = new StringBuffer();
if (uri != null) {
buf.append(uri).append(": ");
}
if (getMessage() != null) {
buf.append(getMessage());
}
if (errorCode != -1) {
buf.append("(").append(errorCode).append(")");
}
return buf.toString();
}
/**
* @return Returns the errorCode.
*/
public int getErrorCode() {
return errorCode;
}
/**
* @return Returns the uri.
*/
public String getUri() {
return uri;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]