View Javadoc

1   package org.xvsm.coordinators;
2   
3   import java.util.ArrayList;
4   import java.util.HashMap;
5   import java.util.List;
6   import java.util.Map;
7   
8   import org.apache.log4j.Logger;
9   import org.xvsm.core.Entry;
10  import org.xvsm.interfaces.IExplicitCoordinator;
11  import org.xvsm.interfaces.container.IContainer;
12  import org.xvsm.internal.exceptions.CannotShiftException;
13  import org.xvsm.internal.exceptions.ContainerFullException;
14  import org.xvsm.internal.exceptions.CountNotMetException;
15  import org.xvsm.internal.exceptions.TransactionLockException;
16  import org.xvsm.selectors.GenericKeySelector;
17  import org.xvsm.selectors.Selector;
18  import org.xvsm.transactions.Transaction;
19  
20  /***
21   * @author Christian Schreiber, Michael Proestler
22   * 
23   */
24  public class GenericKeyCoordinator extends IExplicitCoordinator {
25  
26  	/***
27  	 * The logger.
28  	 */
29  	private static Logger logger = Logger
30  			.getLogger(GenericKeyCoordinator.class);
31  
32  	/***
33  	 * The SerialVersionUID of this Class.
34  	 */
35  	private static final long serialVersionUID = 3027037571449489139L;
36  
37  	/***
38  	 * Contains all entries which have been written with a KeySelector.
39  	 */
40  	private Map<String, Map<Object, Entry>> entries;
41  
42  	/***
43  	 * The maximum size of the container where this KeyCoordinator belongs to.
44  	 */
45  	private int maxSize;
46  
47  	/***
48  	 * The amount of entries currently managed by this coordinator.
49  	 */
50  	private int currentSize;
51  
52  	/***
53  	 * Contains entries and selectors which have been deleted. This information
54  	 * is necessary to rollback a transaction.
55  	 */
56  	private Map<Transaction, Map<Entry, GenericKeySelector>> deletedEntries = new HashMap<Transaction, Map<Entry, GenericKeySelector>>();
57  
58  	/***
59  	 * We need to store which entry has been written with which selector.
60  	 * Otherwise we need to iterate over all entries on delete.
61  	 */
62  	private Map<Entry, GenericKeySelector> reverseMap = new HashMap<Entry, GenericKeySelector>();
63  
64  	/***
65  	 * Default Constructor.
66  	 * 
67  	 */
68  	public GenericKeyCoordinator() {
69  		this.entries = new HashMap<String, Map<Object, Entry>>();
70  	}
71  
72  	/***
73  	 * {@inheritDoc}.
74  	 */
75  	public void commit(Transaction tx) throws TransactionLockException {
76  		// remove the information about the deleted entries.
77  		this.deletedEntries.remove(tx);
78  	}
79  
80  	/***
81  	 * {@inheritDoc}
82  	 * 
83  	 * @throws TransactionLockException
84  	 */
85  	@Override
86  	public void commitSubTransaction(Transaction tx)
87  			throws TransactionLockException {
88  		Map<Entry, GenericKeySelector> deletedUnderChildTx = deletedEntries
89  				.get(tx);
90  		if (deletedUnderChildTx != null) {
91  			deletedEntries.put(tx.getFather(), deletedUnderChildTx);
92  		}
93  	}
94  
95  	/***
96  	 * {@inheritDoc}.
97  	 */
98  	public void delete(Transaction tx, Entry e) throws TransactionLockException {
99  		GenericKeySelector ks = this.reverseMap.get(e);
100 		if (logger.isDebugEnabled()) {
101 			logger.debug("KeySelector: " + ks);
102 		}
103 		if (ks != null) {
104 			this.remove(e, ks, tx);
105 			// remove the entry from the reverse map because it is no longer
106 			// needed.
107 			this.reverseMap.remove(e);
108 		}
109 	}
110 
111 	/***
112 	 * {@inheritDoc}.
113 	 */
114 	public List<Entry> read(Transaction tx, Selector selector,
115 			List<Entry> centries) throws TransactionLockException,
116 			CountNotMetException {
117 		GenericKeySelector<?> ks = (GenericKeySelector<?>) selector;
118 
119 		// there can only be one entry read (there can not be more then one
120 		// entry with the same key). therefore we ignore the count of
121 		// the selector.
122 		Map<Object, Entry> keyMap = this.entries.get(ks.getKeyName());
123 
124 		Entry e;
125 		if (keyMap == null) {
126 			e = null;
127 		} else {
128 			e = keyMap.get(ks.getKeyValue());
129 		}
130 		if (e == null || !e.canBeRead(tx)) {
131 			throw new CountNotMetException(
132 					"No entry with this key found. Key: " + ks.getKeyName()
133 							+ " : " + ks.getKeyValue());
134 		}
135 
136 		List<Entry> retVal = new ArrayList<Entry>();
137 		// is e in the preselected entries?
138 		if (centries != null) {
139 			for (Entry en : centries) {
140 				if (e.equals(en)) {
141 					// ok, the entry is in the preselected entries.
142 					// super.readLockEntry(e, tx);
143 					retVal.add(en);
144 					break;
145 				}
146 			}
147 			if (retVal.size() == 0) {
148 				throw new CountNotMetException(
149 						"The entry was not in the list of the preselected entries.");
150 			}
151 		} else {
152 			// super.readLockEntry(e, tx);
153 			retVal.add(e);
154 		}
155 
156 		return retVal;
157 	}
158 
159 	/***
160 	 * {@inheritDoc}.
161 	 */
162 	public void rollback(Transaction tx) throws TransactionLockException {
163 		// remove entries which have been written under tx:
164 		for (Entry e : tx.getModifiedEntries(this.getCref())) {
165 			GenericKeySelector s = this.reverseMap.get(e);
166 			if (s != null) {
167 				Map<Object, Entry> map = this.entries.get(s.getKeyName());
168 				Entry entry = map.get(s.getKeyValue());
169 				if (entry.removeOnRollback(tx)) {
170 					map.remove(s.getKeyValue());
171 				}
172 			}
173 		}
174 
175 		// read entries which have been deleted under tx:
176 		Map<Entry, GenericKeySelector> deleted = this.deletedEntries.get(tx);
177 		if (deleted != null) {
178 			for (Map.Entry<Entry, GenericKeySelector> x : deleted.entrySet()) {
179 				Map<Object, Entry> keymap = this.entries.get(x.getValue()
180 						.getKeyName());
181 				if (keymap == null) {
182 					keymap = new HashMap<Object, Entry>();
183 					this.entries.put(x.getValue().getKeyName(), keymap);
184 				}
185 				keymap.put(x.getValue().getKeyValue(), x.getKey());
186 				this.currentSize++;
187 			}
188 		}
189 	}
190 
191 	/***
192 	 * {@inheritDoc}.
193 	 */
194 	public void setMaxContainerSize(int size) {
195 		this.maxSize = size;
196 	}
197 
198 	/***
199 	 * {@inheritDoc}.
200 	 */
201 	public Entry shift(Entry e, Transaction tx, Selector s)
202 			throws TransactionLockException, CannotShiftException {
203 		GenericKeySelector ks = (GenericKeySelector) s;
204 		// get the map for this key. If it does not exist create it.
205 		Map<Object, Entry> keyMap = this.entries.get(ks.getKeyName());
206 		if (keyMap == null) {
207 			keyMap = new HashMap<Object, Entry>();
208 			this.entries.put(ks.getKeyName(), keyMap);
209 		}
210 
211 		// is there already an entry at this position?
212 		Entry shifted = keyMap.get(ks.getKeyValue());
213 		if (shifted != null && !shifted.hasDeleteLock(tx)) {
214 			if (this.currentSize > this.maxSize
215 					&& this.maxSize != IContainer.INFINITE_SIZE) {
216 				throw new CannotShiftException(
217 						"Can not decide which Entry has to be deleted");
218 			} else {
219 				// delete the entry.
220 				this.remove(shifted, ks, tx);
221 			}
222 		}
223 		// store the new entry.
224 		keyMap.put(ks.getKeyValue(), e);
225 		this.reverseMap.put(e, ks);
226 		this.currentSize++;
227 		return shifted;
228 	}
229 
230 	/***
231 	 * Removes the entry and stores it in this.deletedEntries.
232 	 * 
233 	 * @param e
234 	 *            the entry to remove
235 	 * @param s
236 	 *            the selector for the entry.
237 	 * @param tx
238 	 *            the Transaction.
239 	 */
240 	private void remove(Entry e, GenericKeySelector s, Transaction tx) {
241 		Map<Entry, GenericKeySelector> deleted = this.deletedEntries.get(tx);
242 		if (deleted == null) {
243 			deleted = new HashMap<Entry, GenericKeySelector>();
244 			this.deletedEntries.put(tx, deleted);
245 		}
246 		deleted.put(e, s);
247 		if (e.getId().equals(
248 				this.entries.get(s.getKeyName()).get(s.getKeyValue()).getId())) {
249 			// if the entry at the position is not equals to the entry that has
250 			// been shifted and at the position is now a new entry which must
251 			// not be deleted.
252 			this.entries.get(s.getKeyName()).remove(s.getKeyValue());
253 		}
254 
255 		this.currentSize--;
256 	}
257 
258 	/***
259 	 * {@inheritDoc}.
260 	 */
261 	public void write(Entry e, Transaction tx, Selector s)
262 			throws ContainerFullException, TransactionLockException {
263 		GenericKeySelector ks = (GenericKeySelector) s;
264 
265 		// is there place left?
266 		if (this.currentSize == this.maxSize
267 				&& this.maxSize != IContainer.INFINITE_SIZE) {
268 			throw new ContainerFullException();
269 		}
270 
271 		// get the map for this key. If it does not exist create it.
272 		Map<Object, Entry> keyMap = this.entries.get(ks.getKeyName());
273 		if (keyMap == null) {
274 			keyMap = new HashMap<Object, Entry>();
275 			this.entries.put(ks.getKeyName(), keyMap);
276 		}
277 
278 		// is there already an old entry on this position?
279 		Entry olde = keyMap.get(ks.getKeyValue());
280 		if (olde != null) {
281 			if (!olde.hasDeleteLock(tx)) {
282 				// block.
283 				throw new ContainerFullException();
284 			} else {
285 				// there is an entry at this positition which is marked as
286 				// deleted.
287 				this.remove(olde, ks, tx);
288 			}
289 		}
290 		keyMap.put(ks.getKeyValue(), e);
291 		this.reverseMap.put(e, ks);
292 		this.currentSize++;
293 	}
294 
295 	/***
296 	 * {@inheritDoc}.
297 	 */
298 	@Override
299 	public Class<? extends Selector> getDefaultSelector() {
300 		return GenericKeySelector.class;
301 	}
302 
303 }