001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.util.db; 020 021import com.google.common.collect.Sets; 022import org.apache.oozie.executor.jpa.JPAExecutorException; 023import org.apache.oozie.util.XLog; 024 025import javax.persistence.EntityExistsException; 026import javax.persistence.EntityNotFoundException; 027import javax.persistence.LockTimeoutException; 028import javax.persistence.NoResultException; 029import javax.persistence.NonUniqueResultException; 030import javax.persistence.OptimisticLockException; 031import javax.persistence.PersistenceException; 032import javax.persistence.PessimisticLockException; 033import javax.persistence.QueryTimeoutException; 034import javax.persistence.TransactionRequiredException; 035import java.util.Set; 036 037/** 038 * A {@link DatabaseRetryPredicate} which applies when a given {@link Exception} (or its causes) are NOT blacklisted. 039 * <p> 040 * Blacklisted exceptions in this class do not indicate a network failure, therefore no retry should take place. 041 */ 042public class PersistenceExceptionSubclassFilterRetryPredicate extends DatabaseRetryPredicate { 043 private static final XLog LOG = XLog.getLog(PersistenceExceptionSubclassFilterRetryPredicate.class); 044 045 /** 046 * If the {@code Throwable} to be checked has a cause chain, these {@code Exception} classes are used as blacklist: if one of 047 * them appear either at the top level, or down the cause chain, no retry will happen. 048 */ 049 @SuppressWarnings("unchecked") 050 private static final Set<Class<? extends PersistenceException>> BLACKLIST_WITH_CAUSE = Sets.newHashSet( 051 EntityExistsException.class, 052 EntityNotFoundException.class, 053 LockTimeoutException.class, 054 NoResultException.class, 055 NonUniqueResultException.class, 056 OptimisticLockException.class, 057 PessimisticLockException.class, 058 QueryTimeoutException.class, 059 TransactionRequiredException.class 060 ); 061 062 /** 063 * If the {@code Throwable} to be checked doesn't have a cause, these {@code Exception} classes are used as blacklist: if one of 064 * them is assignable from the one to be checked, no retry will happen. 065 * <p> 066 * Note that this blacklist is different from {@link #BLACKLIST_WITH_CAUSE} because this handles the use case where 067 * {@code Exception}s are inserted by a failure injection framework or piece of code rather than the database layer that is 068 * failing. 069 */ 070 @SuppressWarnings("unchecked") 071 private static final Set<Class<? extends Exception>> BLACKLIST_WITHOUT_CAUSE = Sets.newHashSet( 072 JPAExecutorException.class, 073 RuntimeException.class 074 ); 075 076 @Override 077 public boolean apply(final Throwable throwable) { 078 LOG.trace("Retry predicate investigation started. [throwable.class={0}]", throwable.getClass().getName()); 079 080 boolean applies = true; 081 082 if ((throwable.getCause() == null) && BLACKLIST_WITHOUT_CAUSE.contains(throwable.getClass())) { 083 applies = false; 084 } 085 else { 086 for (final Class<?> classDownTheStackTrace : getAllExceptions(throwable)) { 087 for (final Class<? extends PersistenceException> blacklistElement : BLACKLIST_WITH_CAUSE) { 088 if (blacklistElement.isAssignableFrom(classDownTheStackTrace)) { 089 applies = false; 090 } 091 } 092 } 093 } 094 095 LOG.trace("Retry predicate investigation finished. [applies={0}]", applies); 096 097 return applies; 098 } 099}