Deadlock Retry with JDK Dynamic Proxy
Here are the key steps in writing a dead lock retry service with JDK Dynamic Proxy.
Step 1: Define the custom deadlock retry annotation.
package com.myapp.deadlock; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited public @interface DeadlockRetry { int maxTries() default 10; int tryIntervalMillis() default 1000; }
Step 2: Define the JDK dynamic proxy class.
package com.myapp.deadlock; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DeadlockRetryHandler implements InvocationHandler { private static final Logger LOG = LoggerFactory.getLogger(DeadlockRetryHandler.class); private Object target; public DeadlockRetryHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Annotation[] annotations = method.getAnnotations(); DeadlockRetry deadlockRetry = (DeadlockRetry) annotations[0]; final Integer maxTries = deadlockRetry.maxTries(); long tryIntervalMillis = deadlockRetry.tryIntervalMillis(); int count = 0; do { try { count++; LOG.info("Attempting to invoke method " + method.getName() + " on " + target.getClass() + " count " + count); Object result = method.invoke(target, args); // retry LOG.info("Completed invocation of method " + method.getName() + " on " + target.getClass()); return result; } catch (Throwable e) { if (!DeadlockUtil.isDeadLock(e)) { throw new RuntimeException(e); } if (tryIntervalMillis > 0) { try { Thread.sleep(tryIntervalMillis); } catch (InterruptedException ie) { LOG.warn("Deadlock retry thread interrupted", ie); } } } } while (count <= maxTries); //gets here only when all attempts have failed throw new RuntimeException("DeadlockRetryMethodInterceptor failed to successfully execute target " + " due to deadlock in all retry attempts", new DeadlockDataAccessException("Created by DeadlockRetryMethodInterceptor", null)); } }
Step 3: The utility class used by the dynamic proxy to determine if the exception indicates deadlock.
package com.myapp.deadlock; import org.apache.commons.lang.exception.ExceptionUtils; import org.springframework.dao.CannotAcquireLockException; public final class DeadlockUtil { public static final String DEADLOCK_MSG = "encountered a deadlock situation. Please re-run your command."; static boolean isDeadLock(Throwable throwable) { boolean isDeadLock = false; Throwable[] causes = ExceptionUtils.getThrowables(throwable); for (Throwable cause : causes) { if (cause instanceof CannotAcquireLockException || (cause.getMessage() != null && (cause.getMessage().contains("LockAcquisitionException") || cause.getMessage().contains( DEADLOCK_MSG)))) { isDeadLock = true; return isDeadLock; } } return isDeadLock; } }
Step 4: Define the target object interface with the annotation.
package com.myapp.engine; import com.myapp.DeadlockRetry; public interface AccountServicePersistenceDelegate { @DeadlockRetry(maxTries = 10, tryIntervalMillis = 5000) abstract Account getAccount(String accountNumber); }
Step 5: Define the target object implementaion.
package com.myapp.engine; import com.myapp.dao.AccountDAO; ... import javax.annotation.Resource; import org.springframework.stereotype.Repository; @Repository public class AccountServicePersistenceDelegateImpl implements AccountServicePersistenceDelegate { @Resource(name = "accountDao") private AccountDAO accountDAO; public Account getStatementOfNetAsset(String accountNumber) { Account account = accountDAO.getAccount(String accountNumber); return account; } }
Step 6: Invoke the target via the proxy.
... @Component("accountService") @ThreadSafe public class AccountServiceImpl implements AccountService { @Resource private AccountServicePersistenceDelegate asServicePersistenceDelegate; private AccountServicePersistenceDelegate proxyAsPersistenceDelegate; @PostConstruct public void init() { this.proxyAsPersistenceDelegate = (AccountServicePersistenceDelegate) Proxy .newProxyInstance(AccountServicePersistenceDelegate.class.getClassLoader(), new Class<?>[] {AccountServicePersistenceDelegate.class}, new DeadlockRetryHandler(asServicePersistenceDelegate)); } public void processAccount(String accountNumber) { //... Account account = proxyAsPersistenceDelegate.getAccount(accountNumber); // invokes the target via the proxy //............ } }
Other design patterns - real life examples
- Sharing and reusing objects with the flyweight design pattern in Java
- When to use a builder design pattern? real life tips
- Proxy design pattern implementing thread safe wrappers
- Java I/O -- the Decorator and proxy design pattern interview questions and answers
- Java design pattern interview questions and answers: strategy and factory pattern
- Sharing and reusing objects with the flyweight design pattern in Java
- Observer design pattern for publish/Subscribe model
Labels: design pattern, Multi-threading
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home