1 package com.terradue.jcatalogue.client;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import static com.terradue.jcatalogue.client.internal.lang.Assertions.checkArgument;
20 import static com.terradue.jcatalogue.client.internal.lang.Assertions.checkNotNull;
21 import static java.lang.String.format;
22 import static org.apache.commons.beanutils.ConvertUtils.register;
23 import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
24
25 import java.io.File;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.nio.charset.Charset;
29 import java.util.Arrays;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.Locale;
34 import java.util.Map;
35
36 import lombok.Getter;
37
38 import org.apache.commons.beanutils.Converter;
39 import org.apache.commons.digester3.binder.DigesterLoader;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import com.ning.http.client.AsyncCompletionHandler;
44 import com.ning.http.client.Response;
45 import com.terradue.jcatalogue.client.download.DownloadHandler;
46 import com.terradue.jcatalogue.client.download.Downloader;
47 import com.terradue.jcatalogue.client.download.HttpDownloader;
48 import com.terradue.jcatalogue.client.download.Protocol;
49 import com.terradue.jcatalogue.client.geo.Line;
50 import com.terradue.jcatalogue.client.geo.Point;
51 import com.terradue.jcatalogue.client.geo.Polygon;
52 import com.terradue.jcatalogue.client.internal.ahc.HttpInvoker;
53 import com.terradue.jcatalogue.client.internal.converters.AtomDateConverter;
54 import com.terradue.jcatalogue.client.internal.converters.CharsetConverter;
55 import com.terradue.jcatalogue.client.internal.converters.GeoConverter;
56 import com.terradue.jcatalogue.client.internal.converters.LocaleConverter;
57 import com.terradue.jcatalogue.client.internal.digester.AtomRulesModule;
58 import com.terradue.jcatalogue.client.internal.digester.DataSetRulesModule;
59 import com.terradue.jcatalogue.client.internal.digester.LinkedAtomEntityModule;
60 import com.terradue.jcatalogue.client.internal.digester.OpenSearchModule;
61 import com.terradue.jcatalogue.client.internal.digester.SingleDataSetRulesModule;
62
63 public final class CatalogueClient
64 {
65
66 static
67 {
68 register( new AtomDateConverter(), Date.class );
69 register( new LocaleConverter(), Locale.class );
70 register( new CharsetConverter(), Charset.class );
71
72 Converter geoConverter = new GeoConverter();
73 register( geoConverter, Line.class );
74 register( geoConverter, Point.class );
75 register( geoConverter, Polygon.class );
76 }
77
78 private final Map<String, Downloader> downloaders;
79
80 private final Logger logger = LoggerFactory.getLogger( getClass() );
81
82 @Getter
83 private final HttpInvoker httpInvoker;
84
85 private final DigesterLoader descriptionDigesterLoader;
86
87 private final DigesterLoader catalogueDigesterLoader;
88
89 private final DigesterLoader serieDigesterLoader;
90
91 private final DigesterLoader singleDataSetDigesterLoader;
92
93 public CatalogueClient()
94 {
95 this( new Configuration() );
96 }
97
98 public CatalogueClient( Configuration configuration )
99 {
100 httpInvoker = configuration.httpInvoker;
101 downloaders = configuration.downloaders;
102
103 descriptionDigesterLoader = newLoader( new OpenSearchModule() ).setNamespaceAware( true );
104 catalogueDigesterLoader = newLoader( new AtomRulesModule( Catalogue.class ), new LinkedAtomEntityModule() )
105 .setNamespaceAware( true );
106 serieDigesterLoader = newLoader( new AtomRulesModule( Series.class ), new DataSetRulesModule() )
107 .setNamespaceAware( true );
108 singleDataSetDigesterLoader = newLoader( new SingleDataSetRulesModule() ).setNamespaceAware( true );
109 }
110
111 public <D extends Downloader> D lookupDownloader( String protocol )
112 {
113 protocol = checkNotNull( protocol, "Input protocol cannot be null" );
114
115 if ( !downloaders.containsKey( protocol ) )
116 {
117 return null;
118 }
119
120 @SuppressWarnings( "unchecked" )
121 D downloader = (D) downloaders.get( protocol );
122 return downloader;
123 }
124
125
126
127 public CatalogueDescription discover( String uri, Parameter... parameters )
128 {
129 return invoke( descriptionDigesterLoader, uri, parameters );
130 }
131
132 public CatalogueDescription discover( URI uri, Parameter...parameters )
133 {
134 return invoke( descriptionDigesterLoader, uri, parameters );
135 }
136
137
138
139 public Catalogue getCatalogue( String uri, Parameter... parameters )
140 {
141 return invoke( catalogueDigesterLoader, uri, parameters );
142 }
143
144 public Catalogue getCatalogue( URI uri, Parameter... parameters )
145 {
146 return invoke( catalogueDigesterLoader, uri, parameters );
147 }
148
149
150
151 public Series getSeries( String uri, Parameter... parameters )
152 {
153 return invoke( serieDigesterLoader, uri, parameters );
154 }
155
156 public Series getSeries( URI uri, Parameter... parameters )
157 {
158 return invoke( serieDigesterLoader, uri, parameters );
159 }
160
161
162
163
164
165
166 public DataSet getDataSet( String uri, Parameter... parameters )
167 {
168 return invoke( singleDataSetDigesterLoader, uri, parameters );
169 }
170
171
172
173
174 public DataSet getDataSet( URI uri, Parameter... parameters )
175 {
176 return invoke( singleDataSetDigesterLoader, uri, parameters );
177 }
178
179
180
181 <CE extends CatalogueEntity> CE invoke( final DigesterLoader digesterLoader, String uri, Parameter...parameters )
182 {
183 uri = checkNotNull( uri, "Input URI cannot be null" );
184
185 try
186 {
187 return invoke( digesterLoader, new URI( uri ), parameters );
188 }
189 catch ( URISyntaxException e )
190 {
191 throw new RuntimeException( uri + " is not a valid URI", e );
192 }
193 }
194
195 <CE extends CatalogueEntity> CE invoke( final DigesterLoader digesterLoader, final URI uri, Parameter...parameters )
196 {
197 checkNotNull( uri, "Input URI cannot be null" );
198
199 if ( logger.isDebugEnabled() )
200 {
201 logger.debug( "Invoking Catalogue URI '{}' with parameters: ", uri, Arrays.toString( parameters ) );
202 }
203
204 CE description = httpInvoker.invoke( HttpMethod.GET, uri, new AsyncCompletionHandler<CE>()
205 {
206
207 @Override
208 public CE onCompleted( Response response )
209 throws Exception
210 {
211 return digesterLoader.newDigester().parse( response.getResponseBodyAsStream() );
212 }
213
214 }, parameters );
215
216 description.setCatalogueClient( this );
217 return description;
218 }
219
220 <T> T downloadFile( File targetDir, Iterator<URI> fileUrisIterator, final DownloadHandler<T> handler )
221 {
222 if ( !targetDir.exists() )
223 {
224 if ( logger.isInfoEnabled() )
225 {
226 logger.info( "Directory {} does not exist, creating it...", targetDir );
227 }
228
229 if ( !targetDir.mkdirs() )
230 {
231 throw new RuntimeException( format( "Impossible to create '%s' directory, please make sure you have enough permissions",
232 targetDir ) );
233 }
234 else if ( logger.isInfoEnabled() )
235 {
236 logger.info( "Directory {} created.", targetDir );
237 }
238 }
239
240 while ( fileUrisIterator.hasNext() )
241 {
242 URI fileUri = fileUrisIterator.next();
243
244 Downloader downloader = lookupDownloader( fileUri.getScheme() );
245
246 if ( downloader != null )
247 {
248 EventsRegisterDownloadHandler<T> wrapperHandler = new EventsRegisterDownloadHandler<T>( handler );
249 T returned = downloader.download( targetDir, fileUri, wrapperHandler );
250
251 if ( !wrapperHandler.hasDetectedErrors() )
252 {
253 return returned;
254 }
255 }
256 else
257 {
258 handler.onWarning( format( "'%s' protocol is not supported, impossible to download %s",
259 fileUri.getScheme(), fileUri ) );
260 }
261 }
262
263 throw new IllegalStateException( "DataSet media file download not possible, none of the submitted URIs succeeded" );
264 }
265
266
267
268
269 public void shutDown()
270 {
271 httpInvoker.shutDown();
272 }
273
274 private static final class EventsRegisterDownloadHandler<T>
275 implements DownloadHandler<T>
276 {
277
278 private final DownloadHandler<T> adapted;
279
280 private boolean detectedErrors = false;
281
282 public EventsRegisterDownloadHandler( DownloadHandler<T> adapted )
283 {
284 this.adapted = adapted;
285 }
286
287 @Override
288 public void onError( Throwable t )
289 {
290 detectedErrors = true;
291 adapted.onError( t );
292 }
293
294 @Override
295 public void onError( String message )
296 {
297 detectedErrors = true;
298 adapted.onError( message );
299 }
300
301 @Override
302 public void onWarning( String message )
303 {
304 adapted.onWarning( message );
305 }
306
307 @Override
308 public void onFatal( String message )
309 {
310 detectedErrors = true;
311 adapted.onFatal( message );
312 }
313
314 @Override
315 public void onContentDownloadProgress( long current, long total )
316 {
317 adapted.onContentDownloadProgress( current, total );
318 }
319
320 @Override
321 public T onCompleted( File file )
322 {
323 return adapted.onCompleted( file );
324 }
325
326 public boolean hasDetectedErrors()
327 {
328 return detectedErrors;
329 }
330
331 }
332
333
334
335
336 public static final class Configuration
337 {
338
339 final HttpInvoker httpInvoker = new HttpInvoker();
340
341 final Map<String, Downloader> downloaders = new HashMap<String, Downloader>();
342
343 public Configuration()
344 {
345 registerDownloader( new HttpDownloader( httpInvoker ) );
346 }
347
348 public Configuration registerDownloader( Downloader downloader )
349 {
350 downloader = checkNotNull( downloader, "Input downloader cannot be null" );
351 checkArgument( downloader.getClass().isAnnotationPresent( Protocol.class ),
352 "Class %s must be annotated with %s", downloader.getClass().getName(), Protocol.class.getName() );
353
354 for ( String protocol : downloader.getClass().getAnnotation( Protocol.class ).value() )
355 {
356 registerDownloader( protocol, downloader );
357 }
358
359 return this;
360 }
361
362 public Configuration registerDownloader( String protocol, Downloader downloader )
363 {
364 protocol = checkNotNull( protocol, "Input protocol cannot be null" );
365 downloader = checkNotNull( downloader, "Input downloader cannot be null" );
366
367 downloaders.put( protocol, downloader );
368
369 return this;
370 }
371
372 public Configuration registerRealm( String host, String username, String password, boolean preemptive, HttpAuthScheme authScheme )
373 {
374 httpInvoker.registerRealm( host, username, password, preemptive, authScheme );
375
376 return this;
377 }
378
379 public Configuration registerSSLCerificates( File sslCertificate, File sslKey, String sslPassword )
380 {
381 httpInvoker.registerSSLCerificates( sslCertificate, sslKey, sslPassword );
382
383 return this;
384 }
385
386 public Configuration registerSSLProxy( File proxyCertificate )
387 {
388 httpInvoker.registerSSLProxy( proxyCertificate );
389
390 return this;
391 }
392
393 public Configuration registerUmSsoAccess( String loginFormUrl, HttpMethod httpMethod, Parameter...parameters )
394 {
395 httpInvoker.registerUmSsoAccess( loginFormUrl, httpMethod, parameters );
396
397 return this;
398 }
399
400 public Configuration registerUmSsoCredentials( URI loginFormUrl, HttpMethod httpMethod, Parameter...parameters )
401 {
402 httpInvoker.registerUmSsoCredentials( loginFormUrl, httpMethod, parameters );
403
404 return this;
405 }
406
407 }
408
409 }