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