1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package com.jcabi.beanstalk.maven.plugin;
31
32 import com.amazonaws.services.elasticbeanstalk.model.S3Location;
33 import com.amazonaws.services.s3.AmazonS3;
34 import com.amazonaws.services.s3.model.ListObjectsRequest;
35 import com.amazonaws.services.s3.model.ObjectListing;
36 import com.amazonaws.services.s3.model.ObjectMetadata;
37 import com.amazonaws.services.s3.model.PutObjectResult;
38 import com.amazonaws.services.s3.model.S3ObjectSummary;
39 import com.jcabi.aspects.Loggable;
40 import com.jcabi.log.Logger;
41 import java.io.File;
42 import java.io.FileInputStream;
43 import java.io.InputStream;
44 import java.util.List;
45 import javax.validation.constraints.NotNull;
46 import lombok.EqualsAndHashCode;
47 import lombok.ToString;
48 import org.apache.commons.codec.digest.DigestUtils;
49 import org.apache.commons.io.FileUtils;
50 import org.apache.commons.io.IOUtils;
51
52
53
54
55
56
57
58
59 @ToString
60 @EqualsAndHashCode(of = { "client", "bucket", "key", "war" })
61 @Loggable(Loggable.DEBUG)
62 final class OverridingBundle implements Bundle {
63
64
65
66
67 private final transient AmazonS3 client;
68
69
70
71
72 private final transient String bucket;
73
74
75
76
77 private final transient String key;
78
79
80
81
82 private final transient File war;
83
84
85
86
87
88
89
90
91
92 protected OverridingBundle(@NotNull final AmazonS3 clnt,
93 @NotNull final String bckt, @NotNull final String label,
94 @NotNull final File file) {
95 this.client = clnt;
96 this.bucket = bckt;
97 this.key = label;
98 this.war = file;
99 if (!this.war.exists()) {
100 throw new DeploymentException(
101 String.format("WAR file %s doesn't exist", this.war)
102 );
103 }
104 }
105
106
107
108
109
110
111
112 @Override
113 public S3Location location() {
114 if (this.exists()) {
115 Logger.info(
116 this,
117 "No need to upload %s (%s) to S3, will use existing object",
118 this.war,
119 FileUtils.byteCountToDisplaySize(this.war.length())
120 );
121 } else {
122 Logger.info(
123 this,
124 "Uploading %s (%s) to s3://%s/%s... (may take a few minutes)",
125 this.war,
126 FileUtils.byteCountToDisplaySize(this.war.length()),
127 this.bucket, this.key
128 );
129 final PutObjectResult res = this.client.putObject(
130 this.bucket, this.key, this.war
131 );
132 Logger.info(
133 this,
134
135 "Uploaded successfully to S3, etag=%s, expires=%s, exp.rule=%s, encryption=%s, version=%s",
136 res.getETag(), res.getExpirationTime(),
137 res.getExpirationTimeRuleId(), res.getServerSideEncryption(),
138 res.getVersionId()
139 );
140 }
141 return new S3Location(this.bucket, this.key);
142 }
143
144
145
146
147 @Override
148 public String name() {
149 return this.key;
150 }
151
152
153
154
155 @Override
156 public String etag() {
157 try {
158 final InputStream stream = new FileInputStream(this.war);
159 final String hash = DigestUtils.md5Hex(stream);
160 IOUtils.closeQuietly(stream);
161 return hash;
162 } catch (java.io.IOException ex) {
163 throw new DeploymentException(ex);
164 }
165 }
166
167
168
169
170
171 private boolean exists() {
172 boolean exists = false;
173 if (this.keyExists()) {
174 final ObjectMetadata meta = this.client.getObjectMetadata(
175 this.bucket, this.key
176 );
177 final String etag = this.etag();
178 if (meta.getETag().equals(etag)) {
179 Logger.info(
180 this,
181
182 "MD5 ETag '%s' of existing S3 object '%s' (%s) equals to the one of the local file (%s)",
183 meta.getETag(), this.key,
184 FileUtils.byteCountToDisplaySize(meta.getContentLength()),
185 FileUtils.byteCountToDisplaySize(this.war.length())
186 );
187 exists = true;
188 } else {
189 Logger.info(
190 this,
191
192 "MD5 ETag '%s' of S3 object '%s' (%s) differs from '%s' of the local file (%s)",
193 meta.getETag(), this.key,
194 FileUtils.byteCountToDisplaySize(meta.getContentLength()),
195 etag, FileUtils.byteCountToDisplaySize(this.war.length())
196 );
197 }
198 }
199 return exists;
200 }
201
202
203
204
205
206 private boolean keyExists() {
207 final ObjectListing listing = this.client.listObjects(
208 new ListObjectsRequest()
209 .withBucketName(this.bucket)
210 .withDelimiter("")
211 .withMaxKeys(1)
212 .withPrefix(this.key)
213 );
214 final List<S3ObjectSummary> summaries = listing.getObjectSummaries();
215 boolean exists = false;
216 if (summaries.isEmpty()) {
217 Logger.info(
218 this,
219 "S3 object '%s' not found in '%s' bucket",
220 this.key, this.bucket
221 );
222 } else {
223 final S3ObjectSummary summary = summaries.get(0);
224 Logger.info(
225 this,
226
227 "S3 object '%s' found in '%s' bucket (size=%d, last-modified=%s, etag=%s)",
228 summary.getKey(), summary.getBucketName(), summary.getSize(),
229 summary.getLastModified(), summary.getETag()
230 );
231 exists = true;
232 }
233 return exists;
234 }
235
236 }