View Javadoc

1   package com.terradue.jcatalogue.client;
2   
3   /*
4    *    Copyright 2011-2012 Terradue srl
5    *
6    *    Licensed under the Apache License, Version 2.0 (the "License");
7    *    you may not use this file except in compliance with the License.
8    *    You may obtain a copy of the License at
9    *
10   *       http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *    Unless required by applicable law or agreed to in writing, software
13   *    distributed under the License is distributed on an "AS IS" BASIS,
14   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *    See the License for the specific language governing permissions and
16   *    limitations under the License.
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" ) // would throw classcast exception anyway
121         D downloader = (D) downloaders.get( protocol );
122         return downloader;
123     }
124 
125     // Description methods
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     // Catalogue methods
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     // Series methods
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     // DataSet methods
162 
163     /**
164      * @since 0.2
165      */
166     public DataSet getDataSet( String uri, Parameter... parameters )
167     {
168         return invoke( singleDataSetDigesterLoader, uri, parameters );
169     }
170 
171     /**
172      * @since 0.2
173      */
174     public DataSet getDataSet( URI uri, Parameter... parameters )
175     {
176         return invoke( singleDataSetDigesterLoader, uri, parameters );
177     }
178 
179     // generic internal methods
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      * @since 0.8
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      * @since 0.8
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 }