Folks,

everyone who has watched some of my evil commits knows my style:
Simple and dirty. This one is no different. I had to make
ConcurrencyConflictException an error to let it come through untouched
to the abstract request. A cleaner solution would be to add the
exception to all store methods and pass it up properly, but this would
cause so many API incompatibilites that it should be deferred to 3.0
or so.

For now, any better ideas?

Oliver

On 7 Dec 2004 17:03:03 -0000, [EMAIL PROTECTED]
<[EMAIL PROTECTED]> wrote:
> 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]
> 
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to