View Javadoc

1   /***
2    * 
3    */
4   package org.astrogrid.desktop.modules.votech;
5   
6   import java.net.URI;
7   import java.util.Collection;
8   import java.util.HashMap;
9   import java.util.Iterator;
10  import java.util.List;
11  import java.util.Map;
12  
13  import junit.framework.Test;
14  import junit.framework.TestCase;
15  import junit.framework.TestSuite;
16  import net.sf.ehcache.Ehcache;
17  import net.sf.ehcache.Element;
18  import net.sf.ehcache.Status;
19  
20  import org.apache.commons.collections.IteratorUtils;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.astrogrid.acr.ivoa.resource.Resource;
24  import org.astrogrid.desktop.modules.system.ui.UIContext;
25  import org.astrogrid.desktop.modules.ui.BackgroundWorker;
26  
27  /*** Implemnentation of {@link AnnotationService}.
28   * @author Noel.Winstanley@manchester.ac.uk
29   * @since Jun 18, 20077:12:26 PM
30   */
31  public class AnnotationServiceImpl implements AnnotationService{
32  	/***
33  	 * Logger for this class
34  	 */
35  	private static final Log logger = LogFactory
36  			.getLog(AnnotationServiceImpl.class);
37  
38  	
39  	private final List<AnnotationSource> annotationSources;
40  	private final AnnotationIO io;
41  	private final Ehcache cache;
42  	protected final UIContext ui;	
43  	public AnnotationServiceImpl(final Ehcache cache, final UIContext ui, final AnnotationIO io) {
44  		super();
45  		this.io = io;
46  		this.cache = cache;
47  		this.ui = ui;
48  		
49  		// populate the annotation source list.
50  		this.annotationSources = io.getSourcesList();
51  		// this file load runs on startup thread, but at least it's local
52  		run();
53  	}
54  
55  // source management.
56  	public void addSource(final AnnotationSource nu) {
57  		if (annotationSources.contains(nu)) {
58  			logger.warn("Ignoring attempt to add duplicate source " + nu);
59  			return;
60  		}
61  		annotationSources.add(nu);
62  		saveSourceList();
63  		if (! (nu instanceof DynamicAnnotationSource)) {
64  			loadStaticSource(nu);
65  		}
66  	}
67  
68  private void saveSourceList() {
69  	(new BackgroundWorker(ui,"Saving annotation source list") {
70  
71  		@Override
72          protected Object construct() throws Exception {
73  			io.saveAnnotationSourceList(listSources());
74  			return null;
75  		}
76  	}).start();
77  }
78  
79  	public AnnotationSource[] listSources() {
80  		return annotationSources.toArray(new AnnotationSource[annotationSources.size()]);
81  	}
82  	
83  	public void removeSource(final AnnotationSource remove) {
84  		if (io.getUserSource().equals(remove)) {
85  			logger.warn("Ignoring attempt to remove user source");
86  			return; //ignored.
87  		}
88  		annotationSources.remove(remove);
89  		saveSourceList();
90  		// and in this case, all annotations are now invalid - simples way is to flush cache, and reload..
91  		cache.removeAll();
92  		run(); // reload.
93  	}
94  
95  	/*** load and process the annotation sources - or at least those that are static. */
96  	public final void run() {
97  		for (final Iterator<AnnotationSource> i = annotationSources.iterator(); i.hasNext();) {
98  			final AnnotationSource s = i.next();
99  			if (! (s instanceof DynamicAnnotationSource)) {
100 				// so it's a static source.
101 				loadStaticSource(s);
102 			}
103 		}
104 	}	
105 	
106 	/*** process a single static source */
107 	public void loadStaticSource(final AnnotationSource source) {
108 		// do this on a background thread..
109 		(new BackgroundWorker(ui,"Loading annotations from " + source.getName(),BackgroundWorker.LONG_TIMEOUT,Thread.MIN_PRIORITY) {
110 
111 			@Override
112             protected Object construct() throws Exception {
113 				final Collection anns = io.load(source);
114 				for (final Iterator i = anns.iterator(); i.hasNext();) {
115 					final Annotation a = (Annotation) i.next();
116 					a.setSource(source); // as probably haven't persisted this.
117 					final URI resourceId = a.getResourceId();
118 					Element el = cache.get(resourceId);
119 					if (el == null) { // new.
120 						final Map<AnnotationSource, Annotation> m = new HashMap<AnnotationSource, Annotation>(annotationSources.size());
121 						m.put(a.getSource(),a);
122 						el = new Element(resourceId,m);
123 					} else { // existing. just add it.
124 						final Map<AnnotationSource, Annotation> m = (Map<AnnotationSource, Annotation>)el.getValue();
125 						m.put(a.getSource(),a);
126 					}
127 					cache.put(el);
128 				} // end for loop.
129 				return null; // done.
130 			}
131 			@Override
132             protected void doError(final Throwable ex) {
133 			    // silently swallow exceptions.
134                 //parent.showTransientWarning("Failed to load annotations from " + source.getName(),ExceptionFormatter.formatException(ex));			   
135                  logger.warn("Failed to load annotations from " + source.getName());
136                  logger.debug("Cause",ex);
137                 
138 			}
139 		}).start();
140 	}
141 	
142 	
143 // access the user annotation
144 	public AnnotationSource getUserAnnotationSource() {
145 		return io.getUserSource();
146 	}
147 	public UserAnnotation getUserAnnotation(final Resource r) {
148 	    if (r == null) {
149 	        return null;
150 	    }
151 	    return getUserAnnotation(r.getId());
152 	}
153 	public UserAnnotation getUserAnnotation(final URI resourceId) {
154 	    if (resourceId == null) {
155 	        return null;
156 	    }	        	        
157 		final Element el = cache.get(resourceId);
158 		if (el == null) {
159 			return null;
160 		}
161 		final Map m = (Map)el.getValue();
162 		final UserAnnotation ua = (UserAnnotation)m.get(io.getUserSource());
163 		return ua;
164 	}
165 	
166 
167 	
168 	public void setUserAnnotation(final Resource r, final UserAnnotation ann) {
169 		if (ann == null || r == null) {
170 			return;
171 		}
172 		final AnnotationSource userSource = io.getUserSource();
173 		// make sure references are correct.
174 		ann.setResourceId(r.getId());
175 		ann.setSource(userSource);
176 		
177 		// wap it into the cache.
178 		Element el = cache.get(r.getId());
179 		if (el == null) {
180 			final Map<AnnotationSource, UserAnnotation> m = new HashMap<AnnotationSource, UserAnnotation>();
181 			m.put(userSource,ann);
182 			el = new Element(r.getId(),m);
183 		} else {
184 			final Map<AnnotationSource, UserAnnotation> m = (Map<AnnotationSource, UserAnnotation>)el.getValue();
185 			m.put(userSource,ann);
186 		}
187 		cache.put(el);
188 		io.updateUserAnnotation(ann);
189 		
190 	}
191 	
192 	
193 	public void removeUserAnnotation(final Resource r) {
194 	    if (r == null) {
195 	        return;
196 	    }
197 	    final Element el = cache.get(r.getId());
198 	    if (el != null) { // else nothing needs doing.
199 	        final Map m = (Map)el.getValue();
200 	        if (m.remove(io.getUserSource()) != null) { // else wasn't a user annotaiton anyhow
201 	            cache.put(el);
202 	            io.removeUserAnnotation(r);
203 	        }
204 	    }
205 	}
206 
207 // process annotations
208 	public void processLocalAnnotations(final Resource r, final AnnotationProcessor procesor) {
209 		if (r == null || procesor == null) {
210 		    return;
211 		}
212 	    final Element el = cache.get(r.getId());
213 		if (el == null) {
214 			return;
215 		}
216 		final Map m = (Map)el.getValue();
217 		for (final Iterator i = m.values().iterator(); i.hasNext(); ) {
218 			final Annotation a = (Annotation)i.next();
219 			if (a instanceof UserAnnotation) {
220 				procesor.process((UserAnnotation)a);
221 			} else {
222 				procesor.process(a);
223 			}
224 		}
225 	}
226 	
227 	public Iterator getLocalAnnotations(final Resource r) {
228 	    if (r == null) {
229             return IteratorUtils.emptyIterator();
230         }
231 		final Element el = cache.get(r.getId());
232 		if (el == null) {
233 			return IteratorUtils.emptyIterator();
234 		}
235 		final Map m = (Map)el.getValue();
236 		return m.values().iterator();
237 	}
238 
239 	public void processRemainingAnnotations(final Resource r, final AnnotationProcessor processor) {
240 		Element el = cache.get(r.getId());
241 		final Map<AnnotationSource, Annotation> m;
242 		if (el == null) {
243 			m = new HashMap<AnnotationSource, Annotation>();
244 			el = new Element(r.getId(),m);
245 		} else {
246 			m = (Map<AnnotationSource, Annotation>)el.getValue();
247 		}
248 		final Element toCache = el; // final restriction work-around.
249 		for (final Iterator<AnnotationSource> i = annotationSources.iterator(); i.hasNext();) {
250 			final AnnotationSource source = i.next();
251 			if (source instanceof DynamicAnnotationSource &&
252 					! m.containsKey(source)) {
253 				(new BackgroundWorker(ui,"Loading annotations from " + source.getName(),BackgroundWorker.SHORT_TIMEOUT,Thread.MIN_PRIORITY) {
254 					@Override
255                     protected Object construct() throws Exception {
256 						final DynamicAnnotationSource dynSource = (DynamicAnnotationSource)source;
257 						final Annotation ann = (dynSource).getAnnotationFor(r);
258 						if (ann != null && dynSource.shouldCache()) {
259 							m.put(source,ann);
260 							cache.put(toCache); // risk of race, and repeated cache writes here.
261 							// but don't think it's a large risk.
262 						}
263 						return ann;
264 					}
265 					@Override
266                     protected void doFinished(final Object result) {
267 						final Annotation ann = (Annotation)result;
268 						if (ann != null) {
269 							if (ann instanceof UserAnnotation) {
270 								processor.process((UserAnnotation)ann);
271 							} else {
272 								processor.process(ann);
273 							}
274 						}
275 					}
276 					@Override
277                     protected void doError(final Throwable ex) {
278 					    // silently swallow exceptions.
279 					    //parent.showTransientWarning("Failed to load annotations from " + source.getName(),ExceptionFormatter.formatException(ex));
280 	                    logger.warn("Failed to load annotations from " + source.getName());
281 	                    logger.debug("Cause",ex);
282 					    
283 					}
284 				}).start();
285 			}
286 		}
287 	}
288 
289     public Test getSelftest() {
290         final TestSuite ts = new TestSuite("Annotations");
291         ts.addTest(new TestCase("Annotations") {
292             @Override
293             protected void runTest()  {
294                 assertEquals("Problem with cache",Status.STATUS_ALIVE,cache.getStatus());
295             }
296         });
297         // don't think there's much point doing any of this - none are vital.
298 //        AnnotationSource[] srcs = listSources();
299 //        for (int i = 0; i < srcs.length; i++) {
300 //            final AnnotationSource src = srcs[i];
301 //            if (src == io.getUserSource()) {
302 //                continue; // don't test this one - it's local, and not always present
303 //            }
304 //            ts.addTest(new TestCase( src.getName() + " annotations"){
305 //                protected void runTest() throws Throwable {
306 //                    try {
307 //                        src.getSource().toURL().openConnection().connect();
308 //                    } catch (MalformedURLException x) {
309 //                        fail("invalid endpoint");
310 //                    } catch (IOException x) {
311 //                        fail("Unable to connect to endpoint");
312 //                    }
313 //                }
314 //            });
315 //
316 //        }
317         return ts;
318     }
319 
320 
321 }