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.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 @ToString
61 @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 @NotNull final File file) {
96 this.client = clnt;
97 this.bucket = bckt;
98 this.key = label;
99 this.war = file;
100 if (!this.war.exists()) {
101 throw new DeploymentException(
102 String.format("WAR file %s doesn't exist", this.war)
103 );
104 }
105 }
106
107 @Cacheable
108 @Override
109 public S3Location location() {
110 if (this.exists()) {
111 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 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 final PutObjectResult res = this.client.putObject(
126 this.bucket, this.key, this.war
127 );
128 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 return new S3Location(this.bucket, this.key);
138 }
139
140
141
142
143 @Override
144 public String name() {
145 return this.key;
146 }
147
148
149
150
151 @Override
152 public String etag() {
153 try {
154 final InputStream stream = new FileInputStream(this.war);
155 final String hash = DigestUtils.md5Hex(stream);
156 IOUtils.closeQuietly(stream);
157 return hash;
158 } catch (final java.io.IOException ex) {
159 throw new DeploymentException(ex);
160 }
161 }
162
163
164
165
166
167 private boolean exists() {
168 boolean exists = false;
169 if (this.keyExists()) {
170 final ObjectMetadata meta = this.client.getObjectMetadata(
171 this.bucket, this.key
172 );
173 final String etag = this.etag();
174 if (meta.getETag().equals(etag)) {
175 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 exists = true;
184 } else {
185 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 return exists;
196 }
197
198
199
200
201
202 private boolean keyExists() {
203 final ObjectListing listing = this.client.listObjects(
204 new ListObjectsRequest()
205 .withBucketName(this.bucket)
206 .withDelimiter("")
207 .withMaxKeys(1)
208 .withPrefix(this.key)
209 );
210 final List<S3ObjectSummary> summaries = listing.getObjectSummaries();
211 boolean exists = false;
212 if (summaries.isEmpty()) {
213 Logger.info(
214 this,
215 "S3 object '%s' not found in '%s' bucket",
216 this.key, this.bucket
217 );
218 } else {
219 final S3ObjectSummary summary = summaries.get(0);
220 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 exists = true;
228 }
229 return exists;
230 }
231
232 }