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