View Javadoc

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