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