View Javadoc

1   /**
2    * Copyright 2010-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.maven.wagon;
17  
18  import java.io.File;
19  import java.io.UnsupportedEncodingException;
20  import java.net.URLEncoder;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.apache.maven.wagon.ConnectionException;
25  import org.apache.maven.wagon.ResourceDoesNotExistException;
26  import org.apache.maven.wagon.TransferFailedException;
27  import org.apache.maven.wagon.Wagon;
28  import org.apache.maven.wagon.authentication.AuthenticationException;
29  import org.apache.maven.wagon.authentication.AuthenticationInfo;
30  import org.apache.maven.wagon.authorization.AuthorizationException;
31  import org.apache.maven.wagon.events.SessionListener;
32  import org.apache.maven.wagon.events.TransferEvent;
33  import org.apache.maven.wagon.events.TransferListener;
34  import org.apache.maven.wagon.observers.Debug;
35  import org.apache.maven.wagon.proxy.ProxyInfo;
36  import org.apache.maven.wagon.proxy.ProxyInfoProvider;
37  import org.apache.maven.wagon.repository.Repository;
38  import org.apache.maven.wagon.resource.Resource;
39  
40  /**
41   * An abstract implementation of the Wagon interface. This implementation manages listener and other common behaviors.
42   *
43   * @author Ben Hale
44   * @author Jeff Caddel - Updates for version 2.0 of the Wagon interface
45   * @since 1.1
46   */
47  public abstract class AbstractWagon implements Wagon {
48  
49      private int timeout;
50  
51      private boolean interactive;
52  
53      private Repository repository;
54  
55      private final boolean supportsDirectoryCopy;
56  
57      private final SessionListenerSupport sessionListeners = new SessionListenerSupport(this);
58  
59      private final TransferListenerSupport transferListeners = new TransferListenerSupport(this);
60  
61      protected AbstractWagon(final boolean supportsDirectoryCopy) {
62          this.supportsDirectoryCopy = supportsDirectoryCopy;
63      }
64  
65      public final void addSessionListener(final SessionListener listener) {
66          if (listener.getClass().equals(Debug.class)) {
67              // This is a junky listener that spews things to System.out in an ugly way
68              return;
69          }
70          sessionListeners.addListener(listener);
71      }
72  
73      protected final SessionListenerSupport getSessionListeners() {
74          return sessionListeners;
75      }
76  
77      public final boolean hasSessionListener(final SessionListener listener) {
78          return sessionListeners.hasListener(listener);
79      }
80  
81      public final void removeSessionListener(final SessionListener listener) {
82          sessionListeners.removeListener(listener);
83      }
84  
85      public final void addTransferListener(final TransferListener listener) {
86          transferListeners.addListener(listener);
87      }
88  
89      protected final TransferListenerSupport getTransferListeners() {
90          return transferListeners;
91      }
92  
93      public final boolean hasTransferListener(final TransferListener listener) {
94          return transferListeners.hasListener(listener);
95      }
96  
97      public final void removeTransferListener(final TransferListener listener) {
98          transferListeners.removeListener(listener);
99      }
100 
101     public final Repository getRepository() {
102         return repository;
103     }
104 
105     public final boolean isInteractive() {
106         return interactive;
107     }
108 
109     public final void setInteractive(final boolean interactive) {
110         this.interactive = interactive;
111     }
112 
113     public final void connect(final Repository source) throws ConnectionException, AuthenticationException {
114         doConnect(source, null, null);
115     }
116 
117     public final void connect(final Repository source, final ProxyInfo proxyInfo) throws ConnectionException,
118             AuthenticationException {
119         connect(source, null, proxyInfo);
120     }
121 
122     public final void connect(final Repository source, final AuthenticationInfo authenticationInfo)
123             throws ConnectionException, AuthenticationException {
124         doConnect(source, authenticationInfo, null);
125     }
126 
127     protected void doConnect(final Repository source, final AuthenticationInfo authenticationInfo,
128             final ProxyInfo proxyInfo) throws ConnectionException, AuthenticationException {
129         repository = source;
130         sessionListeners.fireSessionOpening();
131         try {
132             connectToRepository(source, authenticationInfo, proxyInfo);
133         } catch (ConnectionException e) {
134             sessionListeners.fireSessionConnectionRefused();
135             throw e;
136         } catch (AuthenticationException e) {
137             sessionListeners.fireSessionConnectionRefused();
138             throw e;
139         } catch (Exception e) {
140             sessionListeners.fireSessionConnectionRefused();
141             throw new ConnectionException("Could not connect to repository", e);
142         }
143         sessionListeners.fireSessionLoggedIn();
144         sessionListeners.fireSessionOpened();
145     }
146 
147     public final void connect(final Repository source, final AuthenticationInfo authenticationInfo,
148             final ProxyInfo proxyInfo) throws ConnectionException, AuthenticationException {
149         doConnect(source, authenticationInfo, proxyInfo);
150     }
151 
152     public final void disconnect() throws ConnectionException {
153         sessionListeners.fireSessionDisconnecting();
154         try {
155             disconnectFromRepository();
156         } catch (ConnectionException e) {
157             sessionListeners.fireSessionConnectionRefused();
158             throw e;
159         } catch (Exception e) {
160             sessionListeners.fireSessionConnectionRefused();
161             throw new ConnectionException("Could not disconnect from repository", e);
162         }
163         sessionListeners.fireSessionLoggedOff();
164         sessionListeners.fireSessionDisconnected();
165     }
166 
167     public final void get(final String resourceName, final File destination) throws TransferFailedException,
168             ResourceDoesNotExistException, AuthorizationException {
169         Resource resource = new Resource(resourceName);
170         transferListeners.fireTransferInitiated(resource, TransferEvent.REQUEST_GET);
171         transferListeners.fireTransferStarted(resource, TransferEvent.REQUEST_GET);
172 
173         try {
174             getResource(resourceName, destination, new TransferProgress(resource, TransferEvent.REQUEST_GET,
175                     transferListeners));
176             transferListeners.fireTransferCompleted(resource, TransferEvent.REQUEST_GET);
177         } catch (TransferFailedException e) {
178             throw e;
179         } catch (ResourceDoesNotExistException e) {
180             throw e;
181         } catch (AuthorizationException e) {
182             throw e;
183         } catch (Exception e) {
184             transferListeners.fireTransferError(resource, TransferEvent.REQUEST_GET, e);
185             throw new TransferFailedException("Transfer of resource " + destination + "failed", e);
186         }
187     }
188 
189     public final List<String> getFileList(final String destinationDirectory) throws TransferFailedException,
190             ResourceDoesNotExistException, AuthorizationException {
191         try {
192             return listDirectory(destinationDirectory);
193         } catch (TransferFailedException e) {
194             throw e;
195         } catch (ResourceDoesNotExistException e) {
196             throw e;
197         } catch (AuthorizationException e) {
198             throw e;
199         } catch (Exception e) {
200             sessionListeners.fireSessionError(e);
201             throw new TransferFailedException("Listing of directory " + destinationDirectory + "failed", e);
202         }
203     }
204 
205     public final boolean getIfNewer(final String resourceName, final File destination, final long timestamp)
206             throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException {
207         Resource resource = new Resource(resourceName);
208         try {
209             if (isRemoteResourceNewer(resourceName, timestamp)) {
210                 get(resourceName, destination);
211                 return true;
212             } else {
213                 return false;
214             }
215         } catch (TransferFailedException e) {
216             throw e;
217         } catch (ResourceDoesNotExistException e) {
218             throw e;
219         } catch (AuthorizationException e) {
220             throw e;
221         } catch (Exception e) {
222             transferListeners.fireTransferError(resource, TransferEvent.REQUEST_GET, e);
223             throw new TransferFailedException("Transfer of resource " + destination + "failed", e);
224         }
225     }
226 
227     public final void openConnection() throws ConnectionException, AuthenticationException {
228         // Nothing to do here (never called by the wagon manager)
229     }
230 
231     protected PutFileContext getPutFileContext(File source, String destination) {
232         Resource resource = new Resource(destination);
233         PutFileContext context = new PutFileContext();
234         context.setResource(resource);
235         context.setProgress(new TransferProgress(resource, TransferEvent.REQUEST_PUT, transferListeners));
236         context.setListeners(transferListeners);
237         context.setDestination(destination);
238         context.setSource(source);
239         return context;
240     }
241 
242     public final void put(final File source, final String destination) throws TransferFailedException,
243             ResourceDoesNotExistException, AuthorizationException {
244         PutFileContext context = getPutFileContext(source, destination);
245 
246         try {
247             context.fireStart();
248             putResource(source, destination, context.getProgress());
249             context.fireComplete();
250         } catch (Exception e) {
251             handleException(e, context);
252         }
253     }
254 
255     protected void handleException(Exception e, PutFileContext context) throws TransferFailedException,
256             ResourceDoesNotExistException, AuthorizationException {
257         if (e instanceof TransferFailedException) {
258             throw (TransferFailedException) e;
259         }
260         if (e instanceof ResourceDoesNotExistException) {
261             throw (ResourceDoesNotExistException) e;
262         }
263         if (e instanceof AuthorizationException) {
264             throw (AuthorizationException) e;
265         }
266         transferListeners.fireTransferError(context.getResource(), TransferEvent.REQUEST_PUT, e);
267         throw new TransferFailedException("Transfer of resource " + context.getDestination() + "failed", e);
268     }
269 
270     protected List<PutFileContext> getPutFileContexts(File sourceDirectory, String destinationDirectory) {
271         List<PutFileContext> contexts = new ArrayList<PutFileContext>();
272         // Cycle through all the files in this directory
273         for (File f : sourceDirectory.listFiles()) {
274 
275             /**
276              * The filename is used 2 ways:<br>
277              *
278              * 1 - as a "key" into the bucket<br>
279              * 2 - In the http url itself<br>
280              *
281              * We encode it here so the key matches the url AND to guarantee that the url is valid even in cases where
282              * filenames contain characters (eg spaces) that are not allowed in urls
283              */
284             String filename = encodeUTF8(f.getName());
285 
286             // We hit a directory
287             if (f.isDirectory()) {
288                 // Recurse into the sub-directory and create put requests for any files we find
289                 contexts.addAll(getPutFileContexts(f, destinationDirectory + "/" + filename));
290             } else {
291                 PutFileContext context = getPutFileContext(f, destinationDirectory + "/" + filename);
292                 contexts.add(context);
293             }
294         }
295         return contexts;
296     }
297 
298     protected String encodeUTF8(String s) {
299         try {
300             return URLEncoder.encode(s, "UTF-8");
301         } catch (UnsupportedEncodingException e) {
302             throw new RuntimeException(e);
303         }
304     }
305 
306     public final boolean resourceExists(final String resourceName) throws TransferFailedException,
307             AuthorizationException {
308         try {
309             return doesRemoteResourceExist(resourceName);
310         } catch (TransferFailedException e) {
311             throw e;
312         } catch (AuthorizationException e) {
313             throw e;
314         } catch (Exception e) {
315             sessionListeners.fireSessionError(e);
316             throw new TransferFailedException("Listing of resource " + resourceName + "failed", e);
317         }
318     }
319 
320     public final boolean supportsDirectoryCopy() {
321         return supportsDirectoryCopy;
322     }
323 
324     /**
325      * Subclass must implement with specific connection behavior
326      *
327      * @param source
328      *            The repository connection information
329      * @param authenticationInfo
330      *            Authentication information, if any
331      * @param proxyInfo
332      *            Proxy information, if any
333      * @throws Exception
334      *             Implementations can throw any exception and it will be handled by the base class
335      */
336     protected abstract void connectToRepository(Repository source, AuthenticationInfo authenticationInfo,
337             ProxyInfo proxyInfo) throws Exception;
338 
339     /**
340      * Subclass must implement with specific detection behavior
341      *
342      * @param resourceName
343      *            The remote resource to detect
344      * @return true if the remote resource exists
345      * @throws Exception
346      *             Implementations can throw any exception and it will be handled by the base class
347      */
348     protected abstract boolean doesRemoteResourceExist(String resourceName) throws Exception;
349 
350     /**
351      * Subclasses must implement with specific disconnection behavior
352      *
353      * @throws Exception
354      *             Implementations can throw any exception and it will be handled by the base class
355      */
356     protected abstract void disconnectFromRepository() throws Exception;
357 
358     /**
359      * Subclass must implement with specific get behavior
360      *
361      * @param resourceName
362      *            The name of the remote resource to read
363      * @param destination
364      *            The local file to write to
365      * @param progress
366      *            A progress notifier for the upload. It must be used or hashes will not be calculated correctly
367      * @throws Exception
368      *             Implementations can throw any exception and it will be handled by the base class
369      */
370     protected abstract void getResource(String resourceName, File destination, TransferProgress progress)
371             throws Exception;
372 
373     /**
374      * Subclass must implement with newer detection behavior
375      *
376      * @param resourceName
377      *            The name of the resource being compared
378      * @param timestamp
379      *            The timestamp to compare against
380      * @return true if the current version of the resource is newer than the timestamp
381      * @throws Exception
382      *             Implementations can throw any exception and it will be handled by the base class
383      */
384     protected abstract boolean isRemoteResourceNewer(String resourceName, long timestamp) throws Exception;
385 
386     /**
387      * Subclass must implement with specific directory listing behavior
388      *
389      * @param directory
390      *            The directory to list files in
391      * @return A collection of file names
392      * @throws Exception
393      *             Implementations can throw any exception and it will be handled by the base class
394      */
395     protected abstract List<String> listDirectory(String directory) throws Exception;
396 
397     /**
398      * Subclasses must implement with specific put behavior
399      *
400      * @param source
401      *            The local source file to read from
402      * @param destination
403      *            The name of the remote resource to write to
404      * @param progress
405      *            A progress notifier for the upload. It must be used or hashes will not be calculated correctly
406      * @throws Exception
407      *             Implementations can throw any exception and it will be handled by the base class
408      */
409     protected abstract void putResource(File source, String destination, TransferProgress progress) throws Exception;
410 
411     public void connect(final Repository source, final AuthenticationInfo authenticationInfo,
412             final ProxyInfoProvider proxyInfoProvider) throws ConnectionException, AuthenticationException {
413         doConnect(source, authenticationInfo, null);
414     }
415 
416     public void connect(final Repository source, final ProxyInfoProvider proxyInfoProvider) throws ConnectionException,
417             AuthenticationException {
418         doConnect(source, null, null);
419     }
420 
421     public int getTimeout() {
422         return this.timeout;
423     }
424 
425     public void setTimeout(final int timeoutValue) {
426         this.timeout = timeoutValue;
427     }
428 
429 }