Clover Coverage Report - AWS S3 Wagon 1.0.29-SNAPSHOT
Coverage timestamp: Tue Apr 12 2011 19:06:18 EST
../../../../img/srcFileCovDistChart0.png 14% of files have more coverage
89   292   26   5.56
12   185   0.29   16
16     1.62  
1    
 
  S3Wagon       Line # 75 89 0% 26 117 0% 0.0
 
No Tests
 
1    /*
2    * Copyright 2004-2007 the original author or authors. Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License. You may obtain a copy of the License at
4    * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
5    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
6    * either express or implied. See the License for the specific language governing permissions and limitations under the
7    * License.
8    */
9    package org.kuali.maven.wagon;
10   
11    import java.io.File;
12    import java.io.FileNotFoundException;
13    import java.io.IOException;
14    import java.io.InputStream;
15    import java.io.OutputStream;
16    import java.net.URI;
17    import java.util.ArrayList;
18    import java.util.Date;
19    import java.util.List;
20   
21    import org.apache.commons.io.IOUtils;
22    import org.apache.maven.wagon.ResourceDoesNotExistException;
23    import org.apache.maven.wagon.authentication.AuthenticationException;
24    import org.apache.maven.wagon.authentication.AuthenticationInfo;
25    import org.apache.maven.wagon.proxy.ProxyInfo;
26    import org.apache.maven.wagon.repository.Repository;
27    import org.slf4j.Logger;
28    import org.slf4j.LoggerFactory;
29   
30    import com.amazonaws.AmazonClientException;
31    import com.amazonaws.auth.AWSCredentials;
32    import com.amazonaws.auth.BasicAWSCredentials;
33    import com.amazonaws.services.s3.AmazonS3Client;
34    import com.amazonaws.services.s3.internal.Mimetypes;
35    import com.amazonaws.services.s3.model.Bucket;
36    import com.amazonaws.services.s3.model.CannedAccessControlList;
37    import com.amazonaws.services.s3.model.ObjectListing;
38    import com.amazonaws.services.s3.model.ObjectMetadata;
39    import com.amazonaws.services.s3.model.PutObjectRequest;
40    import com.amazonaws.services.s3.model.S3Object;
41    import com.amazonaws.services.s3.model.S3ObjectSummary;
42   
43    /**
44    * An implementation of the Maven Wagon interface that is integrated with the Amazon S3 service. URLs that reference the
45    * S3 service should be in the form of <code>s3://bucket.name</code>. As an example <code>s3://maven.kuali.org</code>
46    * puts files into the <code>maven.kuali.org</code> bucket on the S3 service.
47    * <p/>
48    * This implementation uses the <code>username</code> and <code>password</code> portions of the server authentication
49    * metadata for credentials. <code>
50    *
51    * pom.xml
52    * <snapshotRepository>
53    * <id>kuali.snapshot</id>
54    * <name>Kuali Snapshot Repository</name>
55    * <url>s3://maven.kuali.org/snapshot</url>
56    * </snapshotRepository>
57    *
58    * settings.xml
59    * <server>
60    * <id>kuali.snapshot</id>
61    * <username>[AWS Access Key ID]</username>
62    * <password>[AWS Secret Access Key]</password>
63    * </server>
64    *
65    * </code> Kuali Updates -------------<br>
66    * 1) Use username/password instead of passphrase/privatekey for AWS credentials (Maven 3.0 is ignoring passphrase)<br>
67    * 2) Fixed a bug in getBaseDir() if it was passed a one character string<br>
68    * 3) Removed directory creation. The concept of a "directory" inside an AWS bucket is not needed for tools like S3Fox,
69    * Bucket Explorer and https://s3browse.springsource.com/browse/maven.kuali.org/snapshot to correctly display the
70    * contents of the bucket
71    *
72    * @author Ben Hale
73    * @author Jeff Caddel
74    */
 
75    public class S3Wagon extends AbstractWagon {
76   
77    final Logger log = LoggerFactory.getLogger(S3Listener.class);
78   
79    private AmazonS3Client client;
80   
81    private Bucket bucket;
82   
83    private String basedir;
84   
85    private final Mimetypes mimeTypes = Mimetypes.getInstance();
86   
 
87  0 toggle public S3Wagon() {
88  0 super(true);
89  0 S3Listener listener = new S3Listener();
90  0 super.addSessionListener(listener);
91  0 super.addTransferListener(listener);
92    }
93   
 
94  0 toggle protected Bucket getOrCreateBucket(final AmazonS3Client client, final String bucketName) {
95  0 List<Bucket> buckets = client.listBuckets();
96  0 for (Bucket bucket : buckets) {
97  0 if (bucket.getName().equals(bucketName)) {
98  0 return bucket;
99    }
100    }
101  0 return client.createBucket(bucketName);
102    }
103   
 
104  0 toggle @Override
105    protected void connectToRepository(final Repository source, final AuthenticationInfo authenticationInfo,
106    final ProxyInfo proxyInfo) throws AuthenticationException {
107   
108  0 AWSCredentials credentials = getCredentials(authenticationInfo);
109  0 client = new AmazonS3Client(credentials);
110  0 bucket = getOrCreateBucket(client, source.getHost());
111  0 basedir = getBaseDir(source);
112    }
113   
 
114  0 toggle @Override
115    protected boolean doesRemoteResourceExist(final String resourceName) {
116  0 try {
117  0 client.getObjectMetadata(bucket.getName(), basedir + resourceName);
118    } catch (AmazonClientException e1) {
119  0 return false;
120    }
121  0 return true;
122    }
123   
 
124  0 toggle @Override
125    protected void disconnectFromRepository() {
126    // Nothing to do for S3
127    }
128   
129    /**
130    * Pull an object out of an S3 bucket and write it to a file
131    */
 
132  0 toggle @Override
133    protected void getResource(final String resourceName, final File destination, final TransferProgress progress)
134    throws ResourceDoesNotExistException, IOException {
135    // Obtain the object from S3
136  0 S3Object object = null;
137  0 try {
138  0 String key = basedir + resourceName;
139  0 object = client.getObject(bucket.getName(), key);
140    } catch (Exception e) {
141  0 throw new ResourceDoesNotExistException("Resource " + resourceName + " does not exist in the repository", e);
142    }
143   
144    //
145  0 InputStream in = null;
146  0 OutputStream out = null;
147  0 try {
148  0 in = object.getObjectContent();
149  0 out = new TransferProgressFileOutputStream(destination, progress);
150  0 byte[] buffer = new byte[1024];
151  0 int length;
152  0 while ((length = in.read(buffer)) != -1) {
153  0 out.write(buffer, 0, length);
154    }
155    } finally {
156  0 IOUtils.closeQuietly(in);
157  0 IOUtils.closeQuietly(out);
158    }
159    }
160   
161    /**
162    * Is the S3 object newer than the timestamp passed in?
163    */
 
164  0 toggle @Override
165    protected boolean isRemoteResourceNewer(final String resourceName, final long timestamp) {
166  0 ObjectMetadata metadata = client.getObjectMetadata(bucket.getName(), basedir + resourceName);
167  0 return metadata.getLastModified().compareTo(new Date(timestamp)) < 0;
168    }
169   
170    /**
171    * List all of the objects in a given directory
172    */
 
173  0 toggle @Override
174    protected List<String> listDirectory(final String directory) throws Exception {
175  0 ObjectListing objectListing = client.listObjects(bucket.getName(), basedir + directory);
176  0 List<String> fileNames = new ArrayList<String>();
177  0 for (S3ObjectSummary summary : objectListing.getObjectSummaries()) {
178  0 fileNames.add(summary.getKey());
179    }
180  0 return fileNames;
181    }
182   
183    /**
184    * Normalize the key to our S3 object<br>
185    * 1. Convert "./css/style.css" into "/css/style.css"<br>
186    * 2. Convert "/foo/bar/../../css/style.css" into "/css/style.css"
187    *
188    * @see java.net.URI.normalize()
189    */
 
190  0 toggle protected String getNormalizedKey(final File source, final String destination) {
191    // Generate our bucket key for this file
192  0 String key = basedir + destination;
193  0 try {
194  0 String prefix = "http://s3.amazonaws.com/" + bucket.getName() + "/";
195  0 String urlString = prefix + key;
196  0 URI rawURI = new URI(urlString);
197  0 URI normalizedURI = rawURI.normalize();
198  0 String normalized = normalizedURI.toString();
199  0 int pos = normalized.indexOf(prefix) + prefix.length();
200  0 String normalizedKey = normalized.substring(pos);
201  0 return normalizedKey;
202    } catch (Exception e) {
203  0 throw new RuntimeException(e);
204    }
205    }
206   
 
207  0 toggle protected ObjectMetadata getObjectMetadata(final File source, final String destination) {
208    // Set the mime type according to the extension of the destination file
209  0 String contentType = mimeTypes.getMimetype(destination);
210  0 long contentLength = source.length();
211   
212  0 ObjectMetadata omd = new ObjectMetadata();
213  0 omd.setContentLength(contentLength);
214  0 omd.setContentType(contentType);
215  0 return omd;
216    }
217   
218    /**
219    * Create a PutObjectRequest based on the source file and destination passed in
220    */
 
221  0 toggle protected PutObjectRequest getPutObjectRequest(final File source, final String destination,
222    final TransferProgress progress) throws FileNotFoundException {
223  0 String key = getNormalizedKey(source, destination);
224  0 String bucketName = bucket.getName();
225  0 InputStream input = new TransferProgressFileInputStream(source, progress);
226  0 ObjectMetadata metadata = getObjectMetadata(source, destination);
227  0 PutObjectRequest request = new PutObjectRequest(bucketName, key, input, metadata);
228  0 request.setCannedAcl(CannedAccessControlList.PublicRead);
229  0 return request;
230    }
231   
232    /**
233    * Store a resource into S3
234    */
 
235  0 toggle @Override
236    protected void putResource(final File source, final String destination, final TransferProgress progress)
237    throws IOException {
238   
239    // Create a new S3Object
240  0 PutObjectRequest request = getPutObjectRequest(source, destination, progress);
241   
242    // Store the file on S3
243  0 client.putObject(request);
244    }
245   
 
246  0 toggle protected String getDestinationPath(final String destination) {
247  0 return destination.substring(0, destination.lastIndexOf('/'));
248    }
249   
250    /**
251    * Convert "/" -> ""<br>
252    * Convert "/snapshot/" -> "snapshot/"<br>
253    * Convert "/snapshot" -> "snapshot/"<br>
254    */
 
255  0 toggle protected String getBaseDir(final Repository source) {
256  0 StringBuilder sb = new StringBuilder(source.getBasedir());
257  0 sb.deleteCharAt(0);
258  0 if (sb.length() == 0) {
259  0 return "";
260    }
261  0 if (sb.charAt(sb.length() - 1) != '/') {
262  0 sb.append('/');
263    }
264  0 return sb.toString();
265    }
266   
 
267  0 toggle protected String getAuthenticationErrorMessage() {
268  0 StringBuffer sb = new StringBuffer();
269  0 sb.append("The S3 wagon needs AWS Access Key set as the username and AWS Secret Key set as the password. eg:\n");
270  0 sb.append("<server>\n");
271  0 sb.append(" <id>my.server</id>\n");
272  0 sb.append(" <username>[AWS Access Key ID]</username>\n");
273  0 sb.append(" <password>[AWS Secret Access Key]</password>\n");
274  0 sb.append("</server>\n");
275  0 return sb.toString();
276    }
277   
278    /**
279    * Create AWSCredentionals from the information in settings.xml
280    */
 
281  0 toggle protected AWSCredentials getCredentials(final AuthenticationInfo authenticationInfo) throws AuthenticationException {
282  0 if (authenticationInfo == null) {
283  0 throw new AuthenticationException(getAuthenticationErrorMessage());
284    }
285  0 String accessKey = authenticationInfo.getUserName();
286  0 String secretKey = authenticationInfo.getPassword();
287  0 if (accessKey == null || secretKey == null) {
288  0 throw new AuthenticationException(getAuthenticationErrorMessage());
289    }
290  0 return new BasicAWSCredentials(accessKey, secretKey);
291    }
292    }