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.s3auth.hosts;
31
32 import com.amazonaws.services.s3.AmazonS3;
33 import com.amazonaws.services.s3.model.ListVersionsRequest;
34 import com.amazonaws.services.s3.model.S3VersionSummary;
35 import com.amazonaws.services.s3.model.VersionListing;
36 import com.google.common.collect.ImmutableList;
37 import com.google.common.collect.ImmutableSet;
38 import com.jcabi.aspects.Immutable;
39 import com.jcabi.aspects.Loggable;
40 import com.jcabi.xml.XMLDocument;
41 import com.jcabi.xml.XSL;
42 import com.jcabi.xml.XSLDocument;
43 import java.io.IOException;
44 import java.io.OutputStream;
45 import java.net.HttpURLConnection;
46 import java.util.Collection;
47 import java.util.Date;
48 import java.util.zip.CRC32;
49 import javax.validation.constraints.NotNull;
50 import javax.ws.rs.core.HttpHeaders;
51 import lombok.EqualsAndHashCode;
52 import lombok.ToString;
53 import org.apache.commons.io.Charsets;
54 import org.xembly.Directives;
55 import org.xembly.ImpossibleModificationException;
56 import org.xembly.Xembler;
57
58
59
60
61
62
63 @Immutable
64 @ToString
65 @EqualsAndHashCode(of = "content")
66 @Loggable(Loggable.DEBUG)
67 final class ObjectVersionListing implements Resource {
68
69
70
71
72 private static final XSL STYLESHEET = XSLDocument.make(
73 ObjectVersionListing.class.getResourceAsStream("versions.xsl")
74 );
75
76
77
78
79 @Immutable.Array
80 private final transient byte[] content;
81
82
83
84
85
86
87
88 ObjectVersionListing(@NotNull final AmazonS3 client,
89 @NotNull final String bucket, @NotNull final String key) {
90 VersionListing listing = client.listVersions(
91 new ListVersionsRequest().withPrefix(key).withBucketName(bucket)
92 );
93 final ImmutableList.Builder<S3VersionSummary> versions =
94 ImmutableList.builder();
95 versions.addAll(listing.getVersionSummaries());
96 while (listing.isTruncated()) {
97 listing = client.listNextBatchOfVersions(listing);
98 versions.addAll(listing.getVersionSummaries());
99 }
100
101 final Directives dirs = new Directives()
102 .add("versions").attr("object", key);
103 for (final S3VersionSummary version : versions.build()) {
104 dirs.add("version")
105 .attr("key", version.getKey())
106 .set(version.getVersionId()).up();
107 }
108 try {
109 this.content = ObjectVersionListing.STYLESHEET.transform(
110 new XMLDocument(new Xembler(dirs).xml())
111 ).toString().getBytes(Charsets.UTF_8);
112 } catch (final ImpossibleModificationException ex) {
113 throw new IllegalStateException(
114 "Unable to generate version listing", ex
115 );
116 }
117 }
118
119 @Override
120 public int status() {
121 return HttpURLConnection.HTTP_OK;
122 }
123
124 @Override
125 public long writeTo(final OutputStream stream) throws IOException {
126 stream.write(this.content);
127 return this.content.length;
128 }
129
130 @Override
131 public Collection<String> headers() throws IOException {
132 final ImmutableSet.Builder<String> headers = ImmutableSet.builder();
133 headers.add(
134 ObjectVersionListing.header(
135 HttpHeaders.CONTENT_TYPE,
136 this.contentType()
137 )
138 );
139 headers.add(
140 ObjectVersionListing.header(
141 HttpHeaders.CONTENT_LENGTH,
142 String.valueOf(this.content.length)
143 )
144 );
145 return headers.build();
146 }
147
148 @Override
149 public String etag() {
150 final CRC32 crc = new CRC32();
151 crc.update(this.content);
152 return Long.toHexString(crc.getValue());
153 }
154 @Override
155 public Date lastModified() {
156 return new Date();
157 }
158
159 @Override
160 public String contentType() {
161 return "application/xhtml+xml";
162 }
163
164 @Override
165 public void close() {
166
167 }
168
169
170
171
172
173
174
175 @NotNull
176 private static String header(@NotNull final String name,
177 @NotNull final String value) {
178 return String.format("%s: %s", name, value);
179 }
180 }