001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (C) 2012 Paul Ferraro 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package net.sf.hajdbc.durability.fine; 019 020import java.util.Map; 021 022import net.sf.hajdbc.Database; 023import net.sf.hajdbc.DatabaseCluster; 024import net.sf.hajdbc.ExceptionFactory; 025import net.sf.hajdbc.balancer.Balancer; 026import net.sf.hajdbc.durability.DurabilityListener; 027import net.sf.hajdbc.durability.InvocationEvent; 028import net.sf.hajdbc.durability.InvokerEvent; 029import net.sf.hajdbc.durability.InvokerEventImpl; 030import net.sf.hajdbc.durability.InvokerResult; 031import net.sf.hajdbc.durability.InvokerResultImpl; 032import net.sf.hajdbc.durability.coarse.CoarseDurability; 033import net.sf.hajdbc.invocation.Invoker; 034import net.sf.hajdbc.state.StateManager; 035import net.sf.hajdbc.util.Objects; 036 037/** 038 * {@link net.sf.hajdbc.durability.Durability} implementation that tracks invocations as well as per-database invokers. 039 * This durability level can both detect and recover from mid-commit crashes. 040 * @author Paul Ferraro 041 */ 042public class FineDurability<Z, D extends Database<Z>> extends CoarseDurability<Z, D> 043{ 044 public FineDurability(DatabaseCluster<Z, D> cluster) 045 { 046 super(cluster); 047 } 048 049 /** 050 * {@inheritDoc} 051 * @see net.sf.hajdbc.durability.none.NoDurability#getInvoker(net.sf.hajdbc.invocation.Invoker, net.sf.hajdbc.durability.Durability.Phase, java.lang.Object, net.sf.hajdbc.ExceptionFactory) 052 */ 053 @Override 054 public <T, R, E extends Exception> Invoker<Z, D, T, R, E> getInvoker(final Invoker<Z, D, T, R, E> invoker, final Phase phase, final Object transactionId, final ExceptionFactory<E> exceptionFactory) 055 { 056 final DurabilityListener listener = this.cluster.getStateManager(); 057 058 return new Invoker<Z, D, T, R, E>() 059 { 060 @Override 061 public R invoke(D database, T object) throws E 062 { 063 InvokerEvent event = new InvokerEventImpl(transactionId, phase, database.getId()); 064 065 listener.beforeInvoker(event); 066 067 try 068 { 069 R result = invoker.invoke(database, object); 070 071 event.setResult(new InvokerResultImpl(result)); 072 073 return result; 074 } 075 catch (Exception e) 076 { 077 event.setResult(new InvokerResultImpl(e)); 078 079 throw exceptionFactory.createException(e); 080 } 081 finally 082 { 083 listener.afterInvoker(event); 084 } 085 } 086 }; 087 } 088 089 /** 090 * {@inheritDoc} 091 * @see net.sf.hajdbc.durability.coarse.CoarseDurability#recover(java.util.Map) 092 */ 093 @Override 094 public void recover(Map<InvocationEvent, Map<String, InvokerEvent>> map) 095 { 096 StateManager stateManager = this.cluster.getStateManager(); 097 Balancer<Z, D> balancer = this.cluster.getBalancer(); 098 D primary = balancer.primary(); 099 100 for (Map.Entry<InvocationEvent, Map<String, InvokerEvent>> entry: map.entrySet()) 101 { 102 InvocationEvent invocation = entry.getKey(); 103 Map<String, InvokerEvent> invokers = entry.getValue(); 104 105 if (!invokers.isEmpty()) 106 { 107 for (D backup: balancer.backups()) 108 { 109 if (this.deactivateSlave(primary, backup, invocation, invokers)) 110 { 111 this.cluster.deactivate(backup, stateManager); 112 } 113 } 114 } 115 116 stateManager.afterInvocation(invocation); 117 } 118 } 119 120 private boolean deactivateSlave(D primary, D backup, InvocationEvent invocation, Map<String, InvokerEvent> invokers) 121 { 122 InvokerEvent primaryEvent = invokers.get(primary.getId()); 123 124 if (primaryEvent != null) 125 { 126 InvokerResult result = primaryEvent.getResult(); 127 128 if (result != null) 129 { 130 Object primaryValue = result.getValue(); 131 Exception primaryException = result.getException(); 132 133 InvokerEvent backupEvent = invokers.get(backup.getId()); 134 135 if (backupEvent != null) 136 { 137 InvokerResult backupResult = backupEvent.getResult(); 138 139 if (backupResult != null) 140 { 141 Object backupValue = backupResult.getValue(); 142 Exception backupException = backupResult.getException(); 143 144 if (primaryException != null) 145 { 146 if ((backupException == null) || !invocation.getExceptionType().getExceptionFactory().equals(primaryException, backupException)) 147 { 148 return true; 149 } 150 } 151 else if ((backupException != null) || !Objects.equals(primaryValue, backupValue)) 152 { 153 return true; 154 } 155 } 156 else 157 { 158 return true; 159 } 160 } 161 else 162 { 163 return true; 164 } 165 } 166 else 167 { 168 return true; 169 } 170 } 171 else 172 { 173 if (invokers.containsKey(backup.getId())) 174 { 175 return true; 176 } 177 } 178 179 return false; 180 } 181}