View Javadoc

1   /*
2    *  
3    *  Fosstrak LLRP Commander (www.fosstrak.org)
4    * 
5    *  Copyright (C) 2008 ETH Zurich
6    *
7    *  This program is free software: you can redistribute it and/or modify
8    *  it under the terms of the GNU General Public License as published by
9    *  the Free Software Foundation, either version 3 of the License, or
10   *  (at your option) any later version.
11   *
12   *  This program is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   *  GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License
18   *  along with this program.  If not, see <http://www.gnu.org/licenses/> 
19   *
20   */
21  
22  package org.fosstrak.llrp.adaptor;
23  
24  import java.rmi.AlreadyBoundException;
25  import java.rmi.NotBoundException;
26  import java.rmi.RemoteException;
27  import java.rmi.registry.LocateRegistry;
28  import java.rmi.registry.Registry;
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.LinkedList;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.concurrent.ConcurrentHashMap;
35  
36  import org.apache.log4j.Logger;
37  import org.fosstrak.llrp.adaptor.config.AdaptorConfiguration;
38  import org.fosstrak.llrp.adaptor.config.ConfigurationLoader;
39  import org.fosstrak.llrp.adaptor.config.ReaderConfiguration;
40  import org.fosstrak.llrp.adaptor.exception.LLRPDuplicateNameException;
41  import org.fosstrak.llrp.adaptor.exception.LLRPRuntimeException;
42  import org.fosstrak.llrp.adaptor.queue.QueueEntry;
43  import org.fosstrak.llrp.client.LLRPExceptionHandler;
44  import org.fosstrak.llrp.client.LLRPExceptionHandlerTypeMap;
45  import org.fosstrak.llrp.client.MessageHandler;
46  import org.llrp.ltk.types.LLRPMessage;
47  
48  /**
49   * The AdaptorManagement handles your adaptors, enqueues LLRPMessages, handles 
50   * errors from the reader site and notifies you about incoming LLRPMessages.<br/>
51   * <br/>
52   * There are some common pitfalls when using the AdaptorManagement:
53   * <ul>
54   * <li>you must specify the repository where the messages shall be logged to (see example)</li>
55   * <li>you must register an exception handler (see example)</li>
56   * <li>you must shutdown the AdaptorManagement through the provided 
57   * shutdown method. Otherwise the reader connections don't get shutdown properly (see example)</li>
58   * </ul>
59   * <br/>
60   * Below there is some sample-code, how you can use the AdaptorManagement:
61   * <p>
62   * <code>// create a message handler</code><br/>
63   * <code>MessageHandler msgHandler = new MessageHandler();</code><br/>
64   * <br/>
65   * <code>// create an exception handler</code><br/>
66   * <code>ExceptionHandler handler = new ExceptionHandler();</code><br/>
67   * <br/>
68   * <code>// run the initializer method</code><br/>
69   * <code>String readConfig = Utility.findWithFullPath("/readerDefaultConfig.properties");</code><br/>
70   * <code>String writeConfig = readConfig;</code><br/>
71   * <code>boolean commitChanges = true;</code><br/>
72   * <code>AdaptorManagement.getInstance().initialize(</code><br/>
73   * <code>&nbsp;&nbsp;&nbsp;&nbsp;readConfig, storeConfig, commitChanges, handler, msgHandler);</code><br/>
74   * <br/>
75   * <code>// now the management should be initialized and ready to be used</code><br/>
76   * <br/>
77   * <code>// create an adaptor</code><br/>
78   * <code>String adaptorName = "myAdaptor";</code><br/>
79   * <code>AdaptorManagement.getInstance().define(adaptorName, "localhost");</code><br/>
80   * <br/>
81   * <code>// create a reader</code><br/>
82   * <code>String readerName = "myReader";</code><br/>
83   * <code>Adaptor adaptor = AdaptorManagement.getAdaptor(adaptorName);</code><br/>
84   * <code>adaptor.define(readerName, "192.168.1.23", 5084, true, true);</code><br/>
85   * <br/>
86   * <code>//Enqueue some LLRPMessage on the adaptor</code><br/>
87   * <code>AdaptorManagement.enqueueLLRPMessage(adaptorName, readerName, message);</code><br/>
88   * <br/>
89   * <code>// when you shutdown your application call the shutdown method</code><br/>
90   * <code>AdaptorManagement.getInstance().shutdown();</code><br/>
91   * </p>
92   * @author sawielan
93   *
94   */
95  public class AdaptorManagement {
96  	
97  	/** the name for the default local adaptor. */
98  	public static final String DEFAULT_ADAPTOR_NAME = "DEFAULT";
99  	
100 	
101 	
102 	/** the logger. */
103 	private static Logger log = Logger.getLogger(AdaptorManagement.class);
104 
105 	/** the exception handler. */
106 	private LLRPExceptionHandler exceptionHandler = null;
107 	
108 	/** 
109 	 * if storeConfig is set and commitChanges is true then all 
110 	 * the changes to the AdaptorManagement are committed to storeConfig.
111 	 */
112 	private boolean commitChanges = true;
113 	
114 	/** where the configuration shall be read from. */
115 	private String readConfig = null;
116 	
117 	/** where the configuration shall be written to (if changes happen). */
118 	private String storeConfig = null;
119 	
120 	/** 
121 	 * flags whether the AdaptorManagement has been initialized or not. 
122 	 * you cannot initialize it twice!.
123 	 */
124 	private boolean initialized = false;
125 	
126 	
127 	/** internal state keeper. if set to true, the first local adaptor gets exported by rmi. */
128 	private boolean export = false;
129 	
130 	/** if there is a severe error in the adaptorManagement this is set to true */
131 	private static boolean error = false;
132 	
133 	/** the exception that discribes the error condition. */
134 	private static LLRPRuntimeException errorException = null;
135 	
136 	/** the error code for the exception handler. */
137 	private static LLRPExceptionHandlerTypeMap errorType = null;
138 	
139 	
140 	
141 	/** if the configuration is load from file we can use this loader to read/write it */
142 	private ConfigurationLoader configLoader = new ConfigurationLoader();
143 
144 	
145 	// we need to distinguish between local and remote adaptors as 
146 	// for local adaptors we want to be able to store the configuration
147 	// at all the time.
148 	
149 	/** all the worker threads running an adaptor held by the management (local and remote adaptors). */
150 	private Map<String, AdaptorWorker> workers = new ConcurrentHashMap<String, AdaptorWorker> ();
151 	
152 	/** all the worker threads running an adaptor held by the management (local adaptors). */
153 	private Map<String, AdaptorWorker> localWorkers = new ConcurrentHashMap<String, AdaptorWorker> ();
154 	
155 	/** all the worker threads running an adaptor held by the management (remote adaptors). */
156 	private Map<String, AdaptorWorker> remoteWorkers = new ConcurrentHashMap<String, AdaptorWorker> ();
157 	
158 	/** a list of handlers that like to receive all the LLRP messages. */
159 	private LinkedList<MessageHandler> fullHandlers = new LinkedList<MessageHandler> ();
160 	
161 	/** these handlers would like to receive only certain LLRP Messages. */
162 	private Map<Class, LinkedList<MessageHandler> > partialHandlers = new HashMap<Class, LinkedList<MessageHandler> > ();
163 	
164 	
165 	
166 	// ------------------------------- initialization -------------------------------
167 	
168 	/**
169 	 * initializes the AdaptorManagement.
170 	 * @param readConfig where the configuration shall be read from.
171 	 * @param storeConfig where the configuration shall be written to (if changes happen).
172 	 * @param commitChanges if storeConfig is set and commitChanges is true then all 
173 	 * the changes to the AdaptorManagement are committed to storeConfig.
174 	 * @param exceptionHandler the exception handler from the GUI.
175 	 * @param handler a handler to dispatch the LLRP messages (can be set to null).
176 	 * @throws LLRPRuntimeException whenever the AdaptorManagement could not be loaded.
177 	 * @return returns 
178 	 * <ul>
179 	 * <li>true if initialization has been performed</li>
180 	 * <li>false if initialization has already been performed and therefore the 
181 	 * process was aborted</li>
182 	 * </ul>.
183 	 */
184 	public boolean initialize(
185 			String readConfig, 
186 			String storeConfig,
187 			boolean commitChanges,
188 			LLRPExceptionHandler exceptionHandler,
189 			MessageHandler handler) 
190 		throws LLRPRuntimeException {
191 		
192 		return initialize(readConfig, 
193 				storeConfig, 
194 				commitChanges, exceptionHandler, handler, false);
195 	}
196 	
197 	/**
198 	 * ATTENTION: initializes the AdaptorManagement.DO NOT USE THIS METHOD as long as you know 
199 	 * what you are doing (this method instructs with export=true to export the 
200 	 * first local adaptor as a server adaptor. 
201 	 * @param readConfig where the configuration shall be read from.
202 	 * @param storeConfig where the configuration shall be written to (if changes happen).
203 	 * @param commitChanges if storeConfig is set and commitChanges is true then all 
204 	 * the changes to the AdaptorManagement are committed to storeConfig.
205 	 * @param exceptionHandler the exception handler from the GUI.
206 	 * @param handler a handler to dispatch the LLRP messages (can be set to null).
207 	 * @param export if the first local adaptor is to be exported by RMI or not.
208 	 * @throws LLRPRuntimeException whenever the AdaptorManagement could not be loaded.
209 	 * @return returns 
210 	 * <ul>
211 	 * <li>true if initialization has been performed</li>
212 	 * <li>false if initialization has already been performed and therefore the 
213 	 * process was aborted</li>
214 	 * </ul>.
215 	 */
216 	public boolean initialize(
217 			String readConfig, 
218 			String storeConfig,
219 			boolean commitChanges,
220 			LLRPExceptionHandler exceptionHandler,
221 			MessageHandler handler,
222 			boolean export) 
223 		throws LLRPRuntimeException {
224 		if (initialized) {
225 			log.error("You cannot initialize the AdaptorManagement twice!\n" +
226 					"use the getters/setters to perform the requested changes!\n" +
227 					"we will abort now!!!");
228 			return false;
229 		}
230 		
231 		this.export = export;
232 		
233 		this.readConfig = readConfig;
234 		this.storeConfig = storeConfig;
235 		this.commitChanges = commitChanges;
236 		this.exceptionHandler = exceptionHandler;
237 		
238 		if (null != handler) {
239 			registerFullHandler(handler);
240 		}
241 		
242 		load();
243 		initialized = true;
244 		return true;
245 	}
246 	
247 	/**
248 	 * flags whether the AdaptorManagement has already been initialized.
249 	 * @return whether the AdaptorManagement has already been initialized.
250 	 */
251 	public boolean isInitialized() {
252 		return initialized;
253 	}
254 	
255 	/**
256 	 * resets the management to initial state. 
257 	 * @throws LLRPRuntimeException if an error occurs during reset.
258 	 */
259 	public synchronized void reset() throws LLRPRuntimeException {
260 		if (!initialized) {
261 			throw new LLRPRuntimeException("AdaptorManagement is not initialized");
262 		}
263 		clearWorkers();
264 		
265 		// drop all the handlers
266 		synchronized (fullHandlers) {
267 			fullHandlers.clear();
268 		}
269 		
270 		synchronized (partialHandlers) {
271 			partialHandlers.clear();
272 		}
273 		
274 		
275 		load();
276 		log.debug("finished reset");
277 	}
278 	
279 	/**
280 	 * loads the whole AdaptorManagement.
281 	 * @throws LLRPRuntimeException when the configuration could not be loaded from file.
282 	 */
283 	private void load() throws LLRPRuntimeException {
284 		try {
285 			loadFromFile();
286 		} catch (LLRPRuntimeException e) {
287 			setStatus(true, 
288 					e,
289 					LLRPExceptionHandlerTypeMap.EXCEPTION_ADAPTOR_MANAGEMENT_NOT_INITIALIZED);
290 			throw e;
291 		}
292 	}
293 	
294 	/**
295 	 * commits the configuration to the properties file.
296 	 */
297 	public void commit() {
298 		if (isCommitChanges()) {
299 			try {
300 				storeToFile();
301 			} catch (LLRPRuntimeException e) {
302 				log.debug("could not commit the changes to the configuration file");
303 				setStatus(true, 
304 						e,
305 						LLRPExceptionHandlerTypeMap.EXCEPTION_ADAPTOR_MANAGEMENT_NOT_INITIALIZED);
306 			}
307 		}
308 	}
309 	
310 	/**
311 	 * check whether the AdaptorManagement is ok or not. 
312 	 * if not, an exception is thrown and reported to the exception handler.
313 	 */
314 	public void checkStatus() throws LLRPRuntimeException {
315 		if (error) {
316 			postException(errorException, errorType, "", "");
317 			throw errorException;
318 		}
319 	}
320 	
321 	/**
322 	 * sets the status of the adaptorManagement.
323 	 */
324 	private void setStatus(
325 			boolean error,
326 			LLRPRuntimeException errorException, 
327 			LLRPExceptionHandlerTypeMap errorType) 
328 	{
329 		this.error = error;
330 		this.errorException = errorException;
331 		this.errorType = errorType;
332 	}
333 	
334 	/**
335 	 * the client leaves the adaptor management. the management makes the 
336 	 * cleanup.
337 	 */
338 	public synchronized void shutdown() {
339 		log.debug("shutting AdaptorManagement down.");
340 		
341 		// first disconnect the local readers.
342 		disconnectReaders();
343 		
344 		// remove the remote readers.
345 		synchronized (AdaptorManagement.class) {
346 			synchronized (workers) {
347 				synchronized (localWorkers) {
348 					synchronized (remoteWorkers) {
349 						for (AdaptorWorker worker : workers.values()) {
350 							// stop all the workers.
351 							worker.tearDown();
352 						}
353 						// deregister the asynchronous callbacks
354 						for (AdaptorWorker worker : remoteWorkers.values()) {
355 							// stop all the workers.
356 							try {
357 								worker.getAdaptor().deregisterFromAsynchronous(worker.getCallback());
358 							} catch (RemoteException e) {
359 								log.error("an error occured when deregistering from remote adaptor: " 
360 										+ e.getMessage());
361 							}
362 						}
363 					} // synchronized remoteWorkers
364 				} // synchronized localWorkers
365 			} // synchronized workers
366 		} // synchronized adaptorManagement
367 	}
368 	
369 	// --------------------------- adaptor handling ---------------------------
370 		
371 	/**
372 	 * remove all the adaptors before loading the new adaptors from configuration file.
373 	 * @throws LLRPRuntimeException
374 	 */
375 	private synchronized void clearWorkers() throws LLRPRuntimeException {
376 		synchronized (workers) {
377 			synchronized (localWorkers) {
378 				synchronized (remoteWorkers) {
379 					// remove the workers.
380 					for (AdaptorWorker worker : workers.values()) {
381 						try {
382 							if (worker.getAdaptorIpAddress() == null) {
383 								// if it is a local adaptor undefine the readers 
384 								worker.getAdaptor().undefineAll();
385 							}
386 							undefine(worker.getAdaptor().getAdaptorName());
387 						} catch (RemoteException e) {
388 							e.printStackTrace();
389 						}
390 					}		
391 					
392 					// erase all existing adaptors
393 					remoteWorkers.clear();
394 					localWorkers.clear();
395 					workers.clear();
396 				}
397 			}
398 		}
399 	}
400 	
401 	/**
402 	 * disconnectReaders shuts down all local readers. 
403 	 */
404 	public void disconnectReaders() {
405 		synchronized (workers) {
406 			synchronized (localWorkers) {
407 				for (AdaptorWorker worker : localWorkers.values()) {
408 					try {
409 						worker.getAdaptor().disconnectAll();
410 					} catch (RemoteException e) {
411 						e.printStackTrace();
412 					} catch (LLRPRuntimeException e) {
413 						e.printStackTrace();
414 					}
415 				}
416 			}
417 		}
418 	}
419 	
420 	/**
421 	 * tells whether an adaptorName already exists.
422 	 * @param adaptorName the name of the adaptor to check.
423 	 * @throws LLRPRuntimeException whever something goes wrong ...
424 	 * @return true if adaptor exists else false.
425 	 */
426 	public boolean containsAdaptor(String adaptorName) throws LLRPRuntimeException {
427 		checkStatus();
428 	
429 		return workers.containsKey(adaptorName);
430 	}
431 	
432 	/**
433 	 * checks, whether a given adapter is a local adapter or not.
434 	 * @param adapterName the name of the adapter to check.
435 	 * @return true if the adapter is local, false otherwise.
436 	 * @throws LLRPRuntimeException whenever something goes wrong...
437 	 */
438 	public boolean isLocalAdapter(String adapterName) throws LLRPRuntimeException {
439 		checkStatus();
440 		
441 		return localWorkers.containsKey(adapterName);
442 	}
443 	
444 	/**
445 	 * adds a new adaptor to the adaptor list.
446 	 * @param adaptorName the name of the new adaptor.
447 	 * @param address if you are using a client adaptor you have to provide the address of the server stub.
448 	 * @throws LLRPRuntimeException when either name already exists or when there occurs an error in adaptor creation.
449 	 * @throws RemoteException when there is an error during transmition.
450 	 * @throws NotBoundException when there is no registry available.
451 	 */
452 	public synchronized String define(String adaptorName, String address) 
453 		throws LLRPRuntimeException, RemoteException, NotBoundException {
454 		checkStatus();
455 		
456 		synchronized (workers) {
457 			synchronized (localWorkers) {
458 				synchronized (remoteWorkers) {
459 					
460 					Adaptor adaptor = null;
461 					if (address != null) {
462 						// try to get the instance from the remote 
463 						// adaptor.
464 						Registry registry = LocateRegistry.getRegistry(address, Constants.registryPort);
465 						adaptor = (Adaptor) registry.lookup(Constants.adaptorNameInRegistry);
466 						
467 						// server adaptor always keeps its name. therefore 
468 						// we rename the adaptor
469 						log.debug(String.format("adaptor is remote. therefore renaming %s to %s.",
470 								adaptorName, adaptor.getAdaptorName()));
471 						
472 						adaptorName = adaptor.getAdaptorName();
473 					}
474 					
475 					// tests whether there exists already a adaptor of this name
476 					if (containsAdaptor(adaptorName)) {
477 						log.error("Adaptor '" + adaptorName + "' already exists!");
478 						LLRPDuplicateNameException e = new LLRPDuplicateNameException(adaptorName, 
479 								"Adaptor '" + adaptorName + "' already exists!");
480 						
481 						postException(
482 								e, LLRPExceptionHandlerTypeMap.EXCEPTION_ADAPTOR_ALREADY_EXISTS,
483 								adaptorName, "");
484 						throw e;
485 					}
486 					
487 					AdaptorCallback cb = null;
488 					AdaptorWorker worker = null;
489 					if (address == null) {
490 						// determine the special hopefully unique server adaptor name
491 						if (export) {
492 							// change the name of the adaptor to the ip of the current machine.
493 							String HOST_NAME_PREFIX = "server adaptor - ";
494 							String HOST_ADDRESS = String.format("unknown ip %d", System.currentTimeMillis());
495 							try {
496 								java.net.InetAddress localMachine = java.net.InetAddress.getLocalHost();
497 								HOST_ADDRESS = localMachine.getHostAddress();
498 							}
499 							catch (java.net.UnknownHostException uhe) {
500 								log.debug("hmmm, what happened? " +
501 										"This should not occur here :-).");
502 							}
503 							adaptorName = HOST_NAME_PREFIX + HOST_ADDRESS;
504 						}
505 						
506 						// local case
507 						adaptor = new AdaptorImpl(adaptorName);
508 						((AdaptorImpl)adaptor).setAdaptorManagement(this);
509 						cb = new AdaptorCallback(false);
510 						worker = new AdaptorWorker(cb, adaptor);
511 						worker.setAdaptorIpAddress(null);
512 						localWorkers.put(adaptorName, worker);
513 						
514 						log.debug("created a new local adaptor '" + 
515 								adaptorName + "'.");
516 					} else {
517 						// remote case
518 						cb = new AdaptorCallback(true);
519 						worker = new AdaptorWorker(cb, adaptor);
520 						// store the ip address of the remote adaptor.
521 						worker.setAdaptorIpAddress(address);
522 						remoteWorkers.put(adaptorName, worker);
523 						
524 						log.debug("created a new client adaptor '" + 
525 								adaptorName + "' with url '" + address + "'.");
526 					}
527 					
528 					// register the callback.
529 					try {
530 						adaptor.registerForAsynchronous(cb);
531 					} catch (RemoteException e) {
532 						e.printStackTrace();
533 					}
534 					
535 					// register the thread.
536 					workers.put(adaptorName, worker);
537 					
538 					// start the thread
539 					new Thread(worker).start();
540 					
541 					// if the user requests an export of the adaptor we do this...
542 					// the adaptor HAS to be local!
543 					if ((export) && (address == null)) {
544 						// now export the adaptor
545 					
546 						// create the new registry
547 						log.debug("create a registry for the export of the local adaptor.");
548 						LocateRegistry.createRegistry(Constants.registryPort);
549 						Registry registry = LocateRegistry.getRegistry(Constants.registryPort);
550 						
551 						log.debug("bind the adaptor to the registry");
552 						try {
553 							registry.bind(Constants.adaptorNameInRegistry, adaptor);
554 						} catch (AlreadyBoundException e) {
555 
556 							// this exception should NEVER occur as we destroy the 
557 							// registry when we register the new adaptor.
558 							log.error("THERE WAS A SEVERE ERROR THAT SHOULD NEVER OCCUR!!!");
559 							e.printStackTrace();
560 						}
561 					}
562 				}
563 			}
564 			
565 			commit();
566 		}
567 		
568 		return adaptorName;
569 	}
570 	
571 	/**
572 	 * removes an adaptor from the adaptor list.
573 	 * @param adaptorName the name of the adaptor to remove.
574 	 * @throws LLRPRuntimeException when either the name does not exist or when an internal runtime error occurs.
575 	 */
576 	public synchronized void undefine(String adaptorName) throws LLRPRuntimeException {
577 		checkStatus();
578 		
579 		synchronized (workers) {
580 			synchronized (localWorkers) {
581 				synchronized (remoteWorkers) {
582 					if (!containsAdaptor(adaptorName)) {
583 						log.error("Adaptor '" + adaptorName + "' does not exist!");
584 						LLRPRuntimeException e = new LLRPRuntimeException("Adaptor '" + adaptorName + "' does not exist!");
585 						
586 						postException(
587 								e, LLRPExceptionHandlerTypeMap.EXCEPTION_ADAPTER_NOT_EXIST,
588 								adaptorName, "");
589 						throw e;
590 					}
591 					
592 					localWorkers.remove(adaptorName);
593 					remoteWorkers.remove(adaptorName);
594 					
595 					// remove the adaptor
596 					AdaptorWorker worker = workers.remove(adaptorName);
597 					try {
598 						worker.getAdaptor().deregisterFromAsynchronous(worker.getCallback());
599 						// stop the worker.
600 						worker.tearDown();
601 						
602 					} catch (RemoteException e) {
603 						e.printStackTrace();
604 					}
605 				}
606 			}
607 		}
608 		commit();
609 	}
610 	
611 	/**
612 	 * returns a list of all the available adaptor names.
613 	 * @return a list of all the available adaptor names.
614 	 */
615 	public List<String> getAdaptorNames() throws LLRPRuntimeException {
616 		checkStatus();
617 		
618 		ArrayList<String> adaptorNames = new ArrayList<String> ();
619 		
620 		// make a deep copy (no leakage)
621 		for (AdaptorWorker worker : workers.values()) {
622 			try {
623 				adaptorNames.add(worker.getAdaptor().getAdaptorName());
624 				worker.cleanConnFailure();
625 			} catch (RemoteException e) {
626 				worker.reportConnFailure();
627 				log.error("could not connect to remote adaptor: " + e.getMessage());
628 			}
629 			
630 		}
631 		checkWorkers();
632 
633 		return adaptorNames;
634 	}
635 	
636 	/**
637 	 * returns an adaptor to a given adaptorName.
638 	 * @param adaptorName the name of the requested adaptor.
639 	 * @return an adaptor to a given adaptorName.
640 	 * @throws LLRPRuntimeException when the adaptor does not exist.
641 	 */
642 	public Adaptor getAdaptor(String adaptorName) throws LLRPRuntimeException {
643 		checkStatus();
644 		
645 		if (!containsAdaptor(adaptorName)) {			
646 			log.error("Adaptor '" + adaptorName + "' does not exist!");
647 			LLRPRuntimeException e = new LLRPRuntimeException("Adaptor '" + adaptorName + "' does not exist!");
648 			
649 			postException(
650 					e, LLRPExceptionHandlerTypeMap.EXCEPTION_ADAPTER_NOT_EXIST,
651 					adaptorName, "");
652 			throw e;
653 		}
654 		
655 		return workers.get(adaptorName).getAdaptor();
656 	}
657 	
658 	/**
659 	 * helper to access the default local adaptor more convenient.
660 	 * @return the default local adaptor.
661 	 * @throws LLRPRuntimeException this should never occur!
662 	 */
663 	public AdaptorImpl getDefaultAdaptor() throws LLRPRuntimeException {
664 		checkStatus();
665 						
666 		if (!workers.containsKey(DEFAULT_ADAPTOR_NAME)) {
667 			// create the default adaptor
668 			try {
669 				define(DEFAULT_ADAPTOR_NAME, null);
670 			} catch (Exception e) {
671 				// these two exceptions only occur in remote adaptors. 
672 				// therefore we can safely ignore them
673 				log.debug("hmmm, what happened? This should not occur here :-).");
674 			}
675 		}
676 		return (AdaptorImpl)getAdaptor(DEFAULT_ADAPTOR_NAME);
677 	}
678 	
679 	/**
680 	 * you can check whether an adaptor is ready to accept messages.
681 	 * @param adaptorName the name of the adaptor to check.
682 	 * @return true when ok, else false.
683 	 */
684 	public boolean isReady(String adaptorName) throws LLRPRuntimeException {
685 		checkStatus();
686 		
687 		if (!containsAdaptor(adaptorName)) {			
688 			log.error("Adaptor '" + adaptorName + "' does not exist!");
689 			LLRPRuntimeException e = new LLRPRuntimeException("Adaptor '" + adaptorName + "' does not exist!");
690 			
691 			postException(
692 					e, LLRPExceptionHandlerTypeMap.EXCEPTION_ADAPTER_NOT_EXIST,
693 					adaptorName, "");
694 			throw e;
695 		}
696 		
697 		return workers.get(adaptorName).isReady();
698 	}
699 	
700 	// --------------------------- message and error handling ---------------------------
701 	/**
702 	 * enqueue an LLRPMessage to be sent to a llrp reader. the adaptor will
703 	 * process the message when ready.
704 	 * @param adaptorName the name of the adaptor holding the llrp reader.
705 	 * @param readerName the name of the llrp reader.
706 	 * @param message the LLRPMessage.
707 	 * @throws LLRPRuntimeException when the queue of the adaptor is full.
708 	 */
709 	public void enqueueLLRPMessage(String adaptorName, String readerName, LLRPMessage message) throws LLRPRuntimeException {
710 		checkStatus();
711 		
712 		synchronized (workers) {
713 			AdaptorWorker theWorker = null;
714 			if (!workers.containsKey(adaptorName)) {
715 				postException(new LLRPRuntimeException("Adaptor does not exist"), 
716 						LLRPExceptionHandlerTypeMap.EXCEPTION_ADAPTER_NOT_EXIST, adaptorName, readerName);
717 				
718 			} else {
719 				theWorker = workers.get(adaptorName);
720 			}
721 			
722 			if (!theWorker.isReady()) {
723 				LLRPRuntimeException e = new LLRPRuntimeException("Queue is full");
724 				postException(e, LLRPExceptionHandlerTypeMap.EXCEPTION_READER_LOST, "AdaptorManagement", readerName);
725 				throw e;
726 			}
727 			log.debug("enqueueLLRPMessage(" + adaptorName + ", " + readerName + ")");
728 			theWorker.enqueue(new QueueEntry(message, readerName, adaptorName));
729 		}
730 	}
731 	
732 	/**
733 	 * register a handler that will receive all the incoming messages.
734 	 * @param handler the handler.
735 	 */
736 	public void registerFullHandler(MessageHandler handler) {
737 		synchronized (fullHandlers) {
738 			fullHandlers.add(handler);
739 		}
740 	}
741 	
742 	/**
743 	 * remove a handler from the full handler list.
744 	 * @param handler the handler to be removed.
745 	 */
746 	public void deregisterFullHandler(MessageHandler handler) {
747 		synchronized (fullHandlers) {
748 			fullHandlers.remove(handler);
749 		}
750 	}
751 	
752 	/**
753 	 * tests whether a given handler is already registered or not.
754 	 * @param handler the handler to check for.
755 	 * @return true if the handler is present, false otherwise.
756 	 */
757 	public boolean hasFullHandler(MessageHandler handler) {
758 		synchronized (fullHandlers) {
759 			return fullHandlers.contains(handler);
760 		}
761 	}
762 	
763 	/**
764 	 * register a handler that will receive only a restricted set of messages.
765 	 * @param handler the handler.
766 	 * @param clzz the type of messages that the handler likes to receive (example KEEPALIVE.class).
767 	 */
768 	public void registerPartialHandler(MessageHandler handler, Class clzz) {
769 		synchronized (partialHandlers) {
770 			LinkedList<MessageHandler> handlers = partialHandlers.get(clzz);
771 			if (null == handlers) {
772 				handlers = new LinkedList<MessageHandler> ();
773 				partialHandlers.put(clzz, handlers);
774 			}
775 			handlers.add(handler);
776 		}
777 	}
778 	
779 	/**
780 	 * remove a handler from the handlers list.
781 	 * @param handler the handler to remove.
782 	 * @param clzz the class where the handler is registered.
783 	 */
784 	public void deregisterPartialHandler(MessageHandler handler, Class clzz) {
785 		synchronized (partialHandlers) {
786 			LinkedList<MessageHandler> handlers = partialHandlers.get(clzz);
787 			if (null != handlers) {
788 				synchronized (handlers) {					
789 					handlers.remove(handler);
790 				}
791 			}
792 		}
793 	}
794 	
795 	/**
796 	 * checks whether a given handler is registered at a given selector class.
797 	 * @param handler the handler to check.
798 	 * @param clzz the class where to search for the handler.
799 	 * @return true if the handler is present, false otherwise.
800 	 */
801 	public boolean hasPartialHandler(MessageHandler handler, Class clzz) {
802 		synchronized (partialHandlers) {
803 			LinkedList<MessageHandler> handlers = partialHandlers.get(clzz);
804 			if (null != handlers) {
805 				synchronized (handlers) {
806 					return handlers.contains(handler);
807 				}
808 			}
809 		}
810 		return false;
811 	}
812 	
813 	/**
814 	 * dispatches an LLRP message to all the registered full handlers. All the 
815 	 * handlers that have interest into the class of the message will be 
816 	 * informed as well.
817 	 * @param adaptorName the name of the adapter that received the message.
818 	 * @param readerName the reader that received the message. 
819 	 * @param message the LLRP message itself.
820 	 */
821 	public void dispatchHandlers(String adaptorName, String readerName, 
822 			LLRPMessage message) {
823 		
824 		// handle full handlers...
825 		synchronized (fullHandlers) {
826 			for (MessageHandler handler : fullHandlers) {
827 				handler.handle(adaptorName, readerName, message);
828 			}
829 		}
830 		
831 		// handle partial handlers
832 		synchronized (partialHandlers) {
833 			LinkedList<MessageHandler> handlers = partialHandlers.get(message.getClass());
834 			if (null != handlers) {
835 				synchronized (handlers) {
836 					for (MessageHandler handler : handlers) {
837 						handler.handle(adaptorName, readerName, message);
838 					}
839 				}
840 			}
841 		}
842 		
843 	}
844 	
845 	/**
846 	 * posts an exception the the exception handler.
847 	 * @param exceptionType the type of the exception. see {@link LLRPExceptionHandler} for more details.
848 	 * @param adapterName the name of the adaptor that caused the exception.
849 	 * @param readerName the name of the reader that caused the exception.
850 	 * @param e the exception itself.
851 	 */
852 	public void postException(
853 			LLRPRuntimeException e, 
854 			LLRPExceptionHandlerTypeMap 
855 			exceptionType, 
856 			String adapterName, 
857 			String readerName) 
858 	{
859 		
860 		if (exceptionHandler == null) {
861 			log.error("ExceptionHandler not set!!!");
862 			e.printStackTrace();
863 			return;
864 		}
865 
866 		log.debug(String.format("Received error call on callback from '%s'.\nException:\n%s", readerName, e.getMessage()));
867 		
868 		exceptionHandler.postExceptionToGUI(exceptionType, e, adapterName, readerName);
869 	}
870 	
871 	
872 	// ------------------------------- singleton handling -------------------------------
873 	
874 	/** private constructor for singleton. */
875 	private AdaptorManagement() {}
876 	
877 	/** the instance of the singleton. */
878 	private static AdaptorManagement instance = new AdaptorManagement();
879 	
880 	/**
881 	 * returns the singleton of the AdaptorManagement.
882 	 * @return the singleton of the AdaptorManagement.
883 	 */
884 	public static AdaptorManagement getInstance() {
885 		return instance;
886 	}
887 
888 	
889 	
890 	// ------------------------------- default config -------------------------------
891 	private synchronized void createDefaultConfiguration() throws LLRPRuntimeException {
892 			
893 		// do not store this configuration
894 		storeConfig = null;
895 		
896 		// no config -> no changes to commit
897 		setCommitChanges(false);
898 		
899 		// clear the workers 
900 		clearWorkers();
901 		
902 		// create a default adaptor
903 		try {
904 			define(DEFAULT_ADAPTOR_NAME, null);
905 		} catch (RemoteException e) {
906 			e.printStackTrace();
907 		} catch (NotBoundException e) {
908 			e.printStackTrace();
909 		}
910 	}
911 	// ------------------------------- load and store -------------------------------
912 	
913 	/**
914 	 * loads the adaptorManagement configuration from file (holds the adaptors and the readers for the local adaptor).
915 	 * all the adaptors defined currently get removed!!! the action is atomic, this means that depending on your 
916 	 * setting, the client might get blocked for a short moment!
917 	 * @throws LLRPRuntimeException whenever there is an exception during restoring.
918 	 */
919 	public synchronized void loadFromFile() throws LLRPRuntimeException {
920 	
921 		if (readConfig == null) {
922 
923 			// if the config cannot be read, then we inform the user about that
924 			// issue but then just use a default configuration
925 			
926 			log.info("config not specified -> create a default configuration");	
927 			createDefaultConfiguration();
928 			return;
929 		}
930 		
931 		// store the commit mode.
932 		boolean commitMode = isCommitChanges();
933 		setCommitChanges(false);
934 		
935 		boolean isExported = false;
936 		synchronized (AdaptorManagement.class) {
937 			synchronized (workers) {
938 				synchronized (localWorkers) {
939 					synchronized (remoteWorkers) {
940 						
941 						// clear out all available adaptors
942 						clearWorkers();
943 						
944 						List<AdaptorConfiguration> configurations = null;
945 						try {
946 							configurations = configLoader.getConfiguration(readConfig);
947 						} catch (LLRPRuntimeException e) {
948 							log.info("could not read the config -> create a default configuration");
949 							
950 							createDefaultConfiguration();
951 							return;
952 						}
953 						
954 						for (AdaptorConfiguration adaptorConfiguration : configurations) {
955 							
956 							String adaptorName = adaptorConfiguration.getAdaptorName();
957 							String adaptorIP = adaptorConfiguration.getIp();
958 											
959 							if (adaptorConfiguration.isLocal()) {
960 								log.debug("Load local Adaptor");
961 								adaptorIP = null;
962 							} else {
963 								log.debug(String.format("Load Remote Adaptor: '%s' on '%s'",
964 										adaptorName, 
965 										adaptorConfiguration.getIp()));
966 							}
967 							
968 							boolean adaptorCreated = false;
969 							try {
970 								if ((export) && (isExported)) {
971 									// only export the first adaptor
972 									isExported = true;
973 									define(adaptorName, adaptorIP);
974 								}
975 								adaptorName = define(adaptorName, adaptorIP);
976 	
977 								adaptorCreated = true;
978 								log.debug(String.format("adaptor '%s' successfully created", adaptorName));
979 							} catch (Exception e) {
980 								log.error(String.format("could not create adaptor '%s': %s", adaptorName,
981 										e.getMessage()));
982 							}
983 							
984 							// only create the readers when the adaptor has been created successfully
985 							// and if the adaptor is remote, we just retrieve the readers. 
986 							if ((adaptorCreated) && (adaptorConfiguration.isLocal())) {
987 								// get a handle of the adaptor and register all the readers.
988 								Adaptor adaptor = getAdaptor(adaptorName);
989 								
990 								if (adaptorConfiguration.getReaderPrototypes() != null) {
991 									for (ReaderConfiguration readerConfiguration : adaptorConfiguration.getReaderPrototypes()) {
992 										
993 										String readerName = readerConfiguration.getReaderName();
994 										String readerIp = readerConfiguration.getReaderIp();
995 										int readerPort = readerConfiguration.getReaderPort();
996 										boolean readerClientInitiated = readerConfiguration.isReaderClientInitiated();
997 										boolean connectImmediately = readerConfiguration.isConnectImmediately();
998 										
999 										log.debug(String.format("Load llrp reader: '%s' on '%s:%d', clientInitiatedConnection: %b, connectImmediately: %b", 
1000 												readerName, readerIp, readerPort, readerClientInitiated, connectImmediately));
1001 										
1002 										// create the reader
1003 										try {
1004 											// try to establish the connection immediately
1005 											adaptor.define(readerName, readerIp, readerPort, readerClientInitiated, connectImmediately);
1006 											log.debug(String.format("reader '%s' successfully created", readerName));
1007 										} catch (RemoteException e) {
1008 											log.error(String.format("could not create reader '%s'", readerName));
1009 											e.printStackTrace();
1010 										}
1011 									}
1012 								}
1013 							}
1014 						}
1015 						
1016 					} // synchronized remoteWorkers
1017 				} // synchronized localWorkers
1018 			} // synchronized workers
1019 		} // synchronized adaptorManagement
1020 		
1021 		// restore the commit mode.
1022 		setCommitChanges(commitMode);
1023 	}
1024 	
1025 	/**
1026 	 * stores the configuration of the adaptor management to file. the remote adaptors get stored and for 
1027 	 * the local adaptor all readers get stored as well.
1028 	 * @throws LLRPRuntimeException whenever there occurs an error during storage.
1029 	 */
1030 	public synchronized void storeToFile() throws LLRPRuntimeException {
1031 		if (storeConfig == null) {
1032 			log.info("Store config not specified, not storing the configuration.");
1033 			return;
1034 		}
1035 		
1036 		synchronized (AdaptorManagement.class) {
1037 			synchronized (workers) {
1038 				synchronized (localWorkers) {
1039 					synchronized (remoteWorkers) {
1040 						
1041 						List<AdaptorConfiguration> configurations = new LinkedList<AdaptorConfiguration>();
1042 						
1043 		 				for (String adaptorName : workers.keySet()) {
1044 		 					String ip = workers.get(adaptorName).getAdaptorIpAddress();
1045 		 					boolean isLocal = false;
1046 		 					if (ip == null) {
1047 		 						isLocal = true;
1048 		 					}
1049 							configurations.add(
1050 									new AdaptorConfiguration(
1051 											adaptorName, 
1052 											ip,
1053 											isLocal,
1054 											null));
1055 						}
1056 						
1057 		 				for (AdaptorConfiguration configuration : configurations) {
1058 		 					if (configuration.isLocal()) {
1059 			 					List<ReaderConfiguration> readerConfigurations = new LinkedList<ReaderConfiguration> ();
1060 			 					configuration.setReaderConfigurations(readerConfigurations);
1061 			 					// get a handle on the adaptor
1062 			 					Adaptor adaptor = getAdaptor(configuration.getAdaptorName());
1063 			 					try {
1064 									for (String readerName : adaptor.getReaderNames()) {
1065 										Reader reader = adaptor.getReader(readerName);
1066 										boolean connectImmed = false;	// somehow this causes bugs with MINA, if we start the reader at startup.
1067 										boolean clientInit = reader.isClientInitiated();
1068 										String ip = reader.getReaderAddress();
1069 										int port = reader.getPort();
1070 										
1071 										readerConfigurations.add(new ReaderConfiguration(
1072 													readerName,
1073 													ip,
1074 													port,
1075 													clientInit,
1076 													connectImmed
1077 												)
1078 										);
1079 									}
1080 								} catch (RemoteException e) {
1081 									// local configuration therefore we can ignore the remote exception.
1082 									e.printStackTrace();
1083 								}
1084 		 					}
1085 		 				}
1086 		 				
1087 		 				try {
1088 		 					configLoader.writeConfiguration(configurations, storeConfig);
1089 		 				} catch (LLRPRuntimeException e) {
1090 		 					postException(e, 
1091 		 							LLRPExceptionHandlerTypeMap.EXCEPTION_ADAPTOR_MANAGEMENT_CONFIG_NOT_STORABLE, 
1092 		 							"", "");
1093 		 				}
1094 		 				
1095 					} // synchronized remoteWorkers
1096 				} // synchronized localWorkers
1097 			} // synchronized workers
1098 		} // synchronized adaptorManagement
1099 	}
1100 	
1101 	// ------------------------------- getter and setter -------------------------------
1102 	/**
1103 	 * returns the exception handler.
1104 	 * @return the exception handler.
1105 	 */
1106 	public LLRPExceptionHandler getExceptionHandler() {
1107 		return exceptionHandler;
1108 	}
1109 
1110 	/**
1111 	 * sets the exception handler.
1112 	 * @param exceptionHandler the exception handler.
1113 	 */
1114 	public void setExceptionHandler(LLRPExceptionHandler exceptionHandler) {
1115 		this.exceptionHandler = exceptionHandler;
1116 	}
1117 
1118 	/**
1119 	 * flags whether all changes to the AdaptorManagement get reflected to the 
1120 	 * configuration file.
1121 	 * @return true if yes, false otherwise.
1122 	 */
1123 	public boolean isCommitChanges() {
1124 		return commitChanges;
1125 	}
1126 
1127 	/**
1128 	 * sets whether all changes to the AdaptorManagement get reflected to the 
1129 	 * configuration file.
1130 	 * @param commitChanges 
1131 	 * <ul>
1132 	 * <li>true then the changes get stored back to the configuration immediately</li>
1133 	 * <li>false the changes are not stored back</li>
1134 	 * </ul>
1135 	 */
1136 	public void setCommitChanges(boolean commitChanges) {
1137 		this.commitChanges = commitChanges;
1138 	}
1139 
1140 	/**
1141 	 * returns the configuration file where to read the settings.
1142 	 * @return the configuration file where to read the settings.
1143 	 */
1144 	public String getReadConfig() {
1145 		return readConfig;
1146 	}
1147 
1148 	/**
1149 	 * sets the configuration file.
1150 	 * @param readConfig the configuration file.
1151 	 */
1152 	public void setReadConfig(String readConfig) {
1153 		this.readConfig = readConfig;
1154 	}
1155 
1156 	/**
1157 	 * returns the configuration file where to store changes.
1158 	 * @return the configuration file where to store changes.
1159 	 */
1160 	public String getStoreConfig() {
1161 		return storeConfig;
1162 	}
1163 
1164 	/**
1165 	 * sets the configuration file where to store changes.
1166 	 * @param storeConfig the configuration file where to store changes.
1167 	 */
1168 	public void setStoreConfig(String storeConfig) {
1169 		this.storeConfig = storeConfig;
1170 	}
1171 	
1172 	private synchronized void checkWorkers() {
1173 		LinkedList<AdaptorWorker> error = new LinkedList<AdaptorWorker> ();
1174 		synchronized (workers) {
1175 			synchronized (localWorkers) {
1176 				synchronized (remoteWorkers) {
1177 					for (AdaptorWorker worker : workers.values()) {
1178 						if (!worker.ok()) {
1179 							error.add(worker);
1180 						}
1181 					}
1182 					
1183 					// remove the erroneous
1184 					for (AdaptorWorker worker : error) {
1185 						// remove from all the workers.
1186 						workers.remove(worker);
1187 						remoteWorkers.remove(worker);
1188 						localWorkers.remove(worker);
1189 					}
1190 				}
1191 			}
1192 		}
1193 		commit();
1194 	}
1195 }