Revision 54720
Added by Sandro La Bruzzo almost 6 years ago
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/deploy.info | ||
---|---|---|
1 |
{"type_source": "SVN", "goal": "package -U -T 4C source:jar", "url": "http://svn-public.driver.research-infrastructures.eu/driver/dnet45/modules/dnet-s3-objectStore/trunk/", "deploy_repository": "dnet45-snapshots", "version": "4", "mail": "sandro.labruzzo@isti.cnr.it,michele.artini@isti.cnr.it, claudio.atzori@isti.cnr.it, alessia.bardi@isti.cnr.it", "deploy_repository_url": "http://maven.research-infrastructures.eu/nexus/content/repositories/dnet45-snapshots", "name": "dnet-s3-objectStore"} |
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/src/main/java/eu/dnetlib/data/objectstore/s3/MongoS3OptionsFactory.java | ||
---|---|---|
1 |
package eu.dnetlib.data.objectstore.s3; |
|
2 |
|
|
3 |
import com.mongodb.MongoClientOptions; |
|
4 |
import org.springframework.beans.BeansException; |
|
5 |
import org.springframework.beans.factory.FactoryBean; |
|
6 |
|
|
7 |
public class MongoS3OptionsFactory implements FactoryBean<MongoClientOptions> { |
|
8 |
|
|
9 |
private int connectionsPerHost; |
|
10 |
|
|
11 |
@Override |
|
12 |
public MongoClientOptions getObject() throws BeansException { |
|
13 |
return MongoClientOptions.builder().connectionsPerHost(connectionsPerHost).build(); |
|
14 |
} |
|
15 |
|
|
16 |
@Override |
|
17 |
public Class<MongoClientOptions> getObjectType() { |
|
18 |
return MongoClientOptions.class; |
|
19 |
} |
|
20 |
|
|
21 |
@Override |
|
22 |
public boolean isSingleton() { |
|
23 |
return false; |
|
24 |
} |
|
25 |
|
|
26 |
public int getConnectionsPerHost() { |
|
27 |
return connectionsPerHost; |
|
28 |
} |
|
29 |
|
|
30 |
public void setConnectionsPerHost(final int connectionsPerHost) { |
|
31 |
this.connectionsPerHost = connectionsPerHost; |
|
32 |
} |
|
33 |
|
|
34 |
} |
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/src/main/java/eu/dnetlib/data/objectstore/s3/S3ObjectStore.java | ||
---|---|---|
1 |
package eu.dnetlib.data.objectstore.s3; |
|
2 |
|
|
3 |
import com.amazonaws.ClientConfiguration; |
|
4 |
import com.amazonaws.Protocol; |
|
5 |
import com.amazonaws.auth.AWSCredentials; |
|
6 |
import com.amazonaws.auth.AWSStaticCredentialsProvider; |
|
7 |
import com.amazonaws.auth.BasicAWSCredentials; |
|
8 |
import com.amazonaws.client.builder.AwsClientBuilder; |
|
9 |
import com.amazonaws.services.s3.AmazonS3; |
|
10 |
import com.amazonaws.services.s3.AmazonS3ClientBuilder; |
|
11 |
import com.amazonaws.services.s3.model.ListObjectsV2Request; |
|
12 |
import com.amazonaws.services.s3.model.ListObjectsV2Result; |
|
13 |
import com.amazonaws.services.s3.model.S3ObjectSummary; |
|
14 |
import com.google.common.collect.Lists; |
|
15 |
import com.mongodb.client.MongoCollection; |
|
16 |
import com.mongodb.client.model.Filters; |
|
17 |
import eu.dnetlib.data.objectstore.modular.ObjectStoreRecord; |
|
18 |
import eu.dnetlib.data.objectstore.modular.connector.ObjectStore; |
|
19 |
import eu.dnetlib.data.objectstore.rmi.MetadataObjectRecord; |
|
20 |
import eu.dnetlib.data.objectstore.rmi.ObjectStoreFile; |
|
21 |
import eu.dnetlib.data.objectstore.rmi.ObjectStoreServiceException; |
|
22 |
import eu.dnetlib.enabling.resultset.ResultSetListener; |
|
23 |
import org.apache.commons.lang3.StringUtils; |
|
24 |
import org.apache.commons.lang3.tuple.ImmutableTriple; |
|
25 |
import org.apache.commons.lang3.tuple.Triple; |
|
26 |
import org.apache.commons.logging.Log; |
|
27 |
import org.apache.commons.logging.LogFactory; |
|
28 |
import org.bson.Document; |
|
29 |
|
|
30 |
import java.io.*; |
|
31 |
import java.nio.file.Files; |
|
32 |
import java.nio.file.Path; |
|
33 |
import java.nio.file.StandardCopyOption; |
|
34 |
import java.util.concurrent.atomic.AtomicInteger; |
|
35 |
import java.util.regex.Pattern; |
|
36 |
|
|
37 |
public class S3ObjectStore implements ObjectStore { |
|
38 |
|
|
39 |
//CONSTANT VARIABLE NAME |
|
40 |
private static final String S3_REGION = "eu-west-3"; |
|
41 |
private static final String URI_FIELD = "uri"; |
|
42 |
private static final String ID_FIELD = "id"; |
|
43 |
private static final String MIME_FIELD = "mime"; |
|
44 |
private static final String ORIGINAL_OBJECT_FIELD = "originalObject"; |
|
45 |
private static final String TIMESTAMP_FIELD = "timestamp"; |
|
46 |
private static final String MD5_FIELD = "md5Sum"; |
|
47 |
private static final String SIZE_FIELD = "size"; |
|
48 |
|
|
49 |
|
|
50 |
private final String id; |
|
51 |
private final String interpretation; |
|
52 |
|
|
53 |
private final String s3AccessKey; |
|
54 |
private final String s3SecretKey; |
|
55 |
private final String s3EndPoint; |
|
56 |
private final String objectStoreBucket; |
|
57 |
|
|
58 |
|
|
59 |
private final MongoCollection<Document> mongoCollection; |
|
60 |
|
|
61 |
private AmazonS3 client; |
|
62 |
|
|
63 |
private static final Log log = LogFactory.getLog(S3ObjectStore.class); |
|
64 |
|
|
65 |
public S3ObjectStore(final String identifier, final String interpretation, final String s3AccessKey, final String s3SecretKey, final String s3EndPoint, final String objectStoreBucket, final MongoCollection<Document> mongoCollection) throws ObjectStoreServiceException { |
|
66 |
this.id = identifier; |
|
67 |
this.interpretation = interpretation; |
|
68 |
this.s3AccessKey = s3AccessKey; |
|
69 |
this.s3SecretKey = s3SecretKey; |
|
70 |
this.mongoCollection = mongoCollection; |
|
71 |
this.s3EndPoint = s3EndPoint; |
|
72 |
this.objectStoreBucket = objectStoreBucket; |
|
73 |
} |
|
74 |
|
|
75 |
|
|
76 |
private AmazonS3 initializeClient() throws ObjectStoreServiceException { |
|
77 |
try { |
|
78 |
final AWSCredentials credentials = new BasicAWSCredentials(this.s3AccessKey, this.s3SecretKey); |
|
79 |
final ClientConfiguration cfg = new ClientConfiguration().withProtocol(Protocol.HTTPS); |
|
80 |
final AmazonS3 s3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(credentials)) |
|
81 |
.withClientConfiguration(cfg) |
|
82 |
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(this.s3EndPoint, S3_REGION)) |
|
83 |
.build(); |
|
84 |
if (s3 == null) |
|
85 |
throw new ObjectStoreServiceException("Cannot initialize s3 client because is NULL"); |
|
86 |
return s3; |
|
87 |
} catch (Throwable e) { |
|
88 |
log.error("An Error happen on initialize client ", e); |
|
89 |
throw new ObjectStoreServiceException("Cannot initialize s3 client", e); |
|
90 |
} |
|
91 |
} |
|
92 |
|
|
93 |
|
|
94 |
@Override |
|
95 |
public String getId() { |
|
96 |
return this.id; |
|
97 |
} |
|
98 |
|
|
99 |
@Override |
|
100 |
public String getInterpretation() { |
|
101 |
return this.interpretation; |
|
102 |
} |
|
103 |
|
|
104 |
@Override |
|
105 |
public int feed(Iterable<ObjectStoreRecord> iterable, boolean upsert) throws ObjectStoreServiceException { |
|
106 |
final AtomicInteger count = new AtomicInteger(); |
|
107 |
iterable.forEach(objectStoreRecord -> { |
|
108 |
try { |
|
109 |
feedObjectRecord(objectStoreRecord); |
|
110 |
count.incrementAndGet(); |
|
111 |
} catch (ObjectStoreServiceException e) { |
|
112 |
log.error("Error on saving file in a temporary Folder"); |
|
113 |
} |
|
114 |
}); |
|
115 |
return count.intValue(); |
|
116 |
} |
|
117 |
|
|
118 |
@Override |
|
119 |
public int feedMetadataRecord(Iterable<MetadataObjectRecord> iterable, boolean b) throws ObjectStoreServiceException { |
|
120 |
final AtomicInteger count = new AtomicInteger(); |
|
121 |
iterable.forEach(mor -> { |
|
122 |
final ObjectStoreRecord r = new ObjectStoreRecord(); |
|
123 |
r.setInputStream(new ByteArrayInputStream(mor.getRecord().getBytes())); |
|
124 |
final ObjectStoreFile fileMetadata = new ObjectStoreFile(); |
|
125 |
fileMetadata.setObjectID(mor.getId()); |
|
126 |
fileMetadata.setMimeType(mor.getMime()); |
|
127 |
r.setFileMetadata(fileMetadata); |
|
128 |
try { |
|
129 |
feedObjectRecord(r); |
|
130 |
count.incrementAndGet(); |
|
131 |
} catch (ObjectStoreServiceException e) { |
|
132 |
log.error("Unable to store record r", e); |
|
133 |
} |
|
134 |
}); |
|
135 |
return count.intValue(); |
|
136 |
} |
|
137 |
|
|
138 |
@Override |
|
139 |
public String feedObjectRecord(ObjectStoreRecord objectStoreRecord) throws ObjectStoreServiceException { |
|
140 |
if (client == null) |
|
141 |
this.client = initializeClient(); |
|
142 |
try { |
|
143 |
long start = System.currentTimeMillis(); |
|
144 |
final Triple<String, Long, Path> result = saveAndGenerateMD5(objectStoreRecord.getInputStream()); |
|
145 |
log.debug("Total time to download into fileSystem " + (System.currentTimeMillis() - start)); |
|
146 |
if (StringUtils.isNotBlank(result.getLeft())) { |
|
147 |
log.debug("Saving object with ID: " + objectStoreRecord.getFileMetadata().getObjectID() + " on s3 "); |
|
148 |
start = System.currentTimeMillis(); |
|
149 |
this.client.putObject(objectStoreBucket, id + "/" + objectStoreRecord.getFileMetadata().getObjectID(), result.getRight().toFile()); |
|
150 |
log.debug("Total time to put into the ObjectStore " + (System.currentTimeMillis() - start)); |
|
151 |
log.debug("Deleting file"); |
|
152 |
boolean deleteSuccess = Files.deleteIfExists(result.getRight()); |
|
153 |
log.debug("Temporary file deleting success " + deleteSuccess); |
|
154 |
log.debug("Saving object on s3 "); |
|
155 |
double timestamp = System.currentTimeMillis(); |
|
156 |
Document metadata = new Document() |
|
157 |
.append(ID_FIELD, objectStoreRecord.getFileMetadata().getObjectID()) |
|
158 |
.append(MIME_FIELD, objectStoreRecord.getFileMetadata().getMimeType()) |
|
159 |
.append(ORIGINAL_OBJECT_FIELD, objectStoreRecord.getFileMetadata().toJSON()) |
|
160 |
.append(TIMESTAMP_FIELD, timestamp) |
|
161 |
.append(MD5_FIELD, result.getLeft()) |
|
162 |
.append(SIZE_FIELD, result.getMiddle()) |
|
163 |
.append(URI_FIELD, String.format("s3://%s/%s/%s", objectStoreBucket, id, objectStoreRecord.getFileMetadata().getObjectID())); |
|
164 |
log.debug("saving metadata object to the collection: " + metadata.toString()); |
|
165 |
start = System.currentTimeMillis(); |
|
166 |
mongoCollection.insertOne(metadata); |
|
167 |
log.debug("Total time to sav in Mongo " + (System.currentTimeMillis() - start)); |
|
168 |
|
|
169 |
} |
|
170 |
} catch (IOException e) { |
|
171 |
log.error("Error on saving file in a temporary Folder"); |
|
172 |
throw new ObjectStoreServiceException(e); |
|
173 |
|
|
174 |
} |
|
175 |
return null; |
|
176 |
} |
|
177 |
|
|
178 |
@Override |
|
179 |
public ResultSetListener deliver(final Long from, final Long until) throws ObjectStoreServiceException { |
|
180 |
S3ObjectStoreResultSetListener resultSet = new S3ObjectStoreResultSetListener(); |
|
181 |
resultSet.setMongoCollection(this.mongoCollection); |
|
182 |
resultSet.setObjectStoreID(getId()); |
|
183 |
resultSet.setFromDate(from); |
|
184 |
resultSet.setUntilDate(until); |
|
185 |
return resultSet; |
|
186 |
} |
|
187 |
|
|
188 |
@Override |
|
189 |
public ResultSetListener deliverIds(final Iterable<String> ids) throws ObjectStoreServiceException { |
|
190 |
S3ObjectStoreResultSetListener resultSet = new S3ObjectStoreResultSetListener(); |
|
191 |
resultSet.setMongoCollection(this.mongoCollection); |
|
192 |
resultSet.setObjectStoreID(getId()); |
|
193 |
resultSet.setRecords(Lists.newArrayList(ids)); |
|
194 |
return resultSet; |
|
195 |
} |
|
196 |
|
|
197 |
@Override |
|
198 |
public ObjectStoreFile deliverObject(String objectId) throws ObjectStoreServiceException { |
|
199 |
Document resultQuery = this.mongoCollection.find(Filters.eq("id", objectId)).first(); |
|
200 |
if (resultQuery!= null) |
|
201 |
return ObjectStoreS3Utility.build(resultQuery); |
|
202 |
else return null; |
|
203 |
} |
|
204 |
|
|
205 |
@Override |
|
206 |
public int getSize() throws ObjectStoreServiceException { |
|
207 |
return (int) this.mongoCollection.count(); |
|
208 |
} |
|
209 |
|
|
210 |
@Override |
|
211 |
public void deleteObject(String objectId) throws ObjectStoreServiceException { |
|
212 |
final Document response =this.mongoCollection.findOneAndDelete(Filters.eq("id", objectId)); |
|
213 |
if (response == null) |
|
214 |
throw new ObjectStoreServiceException("Error document not found with objectId: "+objectId); |
|
215 |
|
|
216 |
if (this.client == null) |
|
217 |
this.client = initializeClient(); |
|
218 |
|
|
219 |
this.client.deleteObject(this.objectStoreBucket, String.format("%s/%s", this.id, response.get(ID_FIELD))); |
|
220 |
} |
|
221 |
|
|
222 |
@Override |
|
223 |
public String getObject(String objectId) throws ObjectStoreServiceException { |
|
224 |
|
|
225 |
Document response = this.mongoCollection.find(Filters.eq("id", objectId)).first(); |
|
226 |
if (response == null || !response.containsKey(URI_FIELD)) |
|
227 |
return null; |
|
228 |
return response.getString(URI_FIELD); |
|
229 |
} |
|
230 |
|
|
231 |
@Override |
|
232 |
public boolean existIDStartsWith(String startId) throws ObjectStoreServiceException { |
|
233 |
return this.mongoCollection.count(Filters.regex("id", Pattern.compile(startId))) > 0; |
|
234 |
} |
|
235 |
|
|
236 |
@Override |
|
237 |
public boolean dropContent() throws ObjectStoreServiceException { |
|
238 |
if (this.client == null) { |
|
239 |
this.client = initializeClient(); |
|
240 |
} |
|
241 |
final ListObjectsV2Request req = new ListObjectsV2Request().withBucketName(objectStoreBucket).withPrefix(id); |
|
242 |
ListObjectsV2Result result; |
|
243 |
do { |
|
244 |
result = this.client.listObjectsV2(req); |
|
245 |
|
|
246 |
for (S3ObjectSummary objectSummary : result.getObjectSummaries()) { |
|
247 |
log.debug(String.format(" - %s (size: %d)\n", objectSummary.getKey(), objectSummary.getSize())); |
|
248 |
this.client.deleteObject("openaire-sandro-test", objectSummary.getKey()); |
|
249 |
log.debug("Object Deleted"); |
|
250 |
} |
|
251 |
String token = result.getNextContinuationToken(); |
|
252 |
log.debug("Next Continuation Token: " + token); |
|
253 |
req.setContinuationToken(token); |
|
254 |
} while (result.isTruncated()); |
|
255 |
|
|
256 |
this.mongoCollection.drop(); |
|
257 |
return true; |
|
258 |
} |
|
259 |
|
|
260 |
private static Triple<String, Long, Path> saveAndGenerateMD5(final InputStream inputStream) throws IOException { |
|
261 |
if (inputStream == null) return null; |
|
262 |
|
|
263 |
File tempFile = File.createTempFile("obsFile", ".tmp"); |
|
264 |
log.debug("Temp file On Default Location: " + tempFile.getAbsolutePath()); |
|
265 |
String md5 = null; |
|
266 |
long size = 0; |
|
267 |
try { |
|
268 |
Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); |
|
269 |
|
|
270 |
final FileInputStream fis = new FileInputStream(tempFile); |
|
271 |
md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(fis); |
|
272 |
fis.close(); |
|
273 |
size = Files.size(tempFile.toPath()); |
|
274 |
|
|
275 |
} catch (IOException e1) { |
|
276 |
log.error(e1); |
|
277 |
} |
|
278 |
return new ImmutableTriple<>(md5, size, tempFile.toPath()); |
|
279 |
} |
|
280 |
} |
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/src/main/java/eu/dnetlib/data/objectstore/s3/S3ObjectStoreResultSetListener.java | ||
---|---|---|
1 |
package eu.dnetlib.data.objectstore.s3; |
|
2 |
|
|
3 |
import com.google.common.collect.Lists; |
|
4 |
import com.mongodb.client.FindIterable; |
|
5 |
import com.mongodb.client.MongoCollection; |
|
6 |
import com.mongodb.client.MongoCursor; |
|
7 |
import com.mongodb.client.model.Filters; |
|
8 |
import com.mongodb.client.model.Sorts; |
|
9 |
import eu.dnetlib.enabling.resultset.ResultSet; |
|
10 |
import eu.dnetlib.enabling.resultset.ResultSetAware; |
|
11 |
import eu.dnetlib.enabling.resultset.ResultSetListener; |
|
12 |
import eu.dnetlib.miscutils.collections.MappedCollection; |
|
13 |
import org.apache.commons.logging.Log; |
|
14 |
import org.apache.commons.logging.LogFactory; |
|
15 |
import org.bson.Document; |
|
16 |
import org.bson.conversions.Bson; |
|
17 |
|
|
18 |
import java.util.List; |
|
19 |
|
|
20 |
/** |
|
21 |
* The listener interface for receiving fileSystemObjectStoreResultSet events. |
|
22 |
* The class that is interested in processing a fileSystemObjectStoreResultSet |
|
23 |
* event implements this interface, and the object created |
|
24 |
* with that class is registered with a component using the |
|
25 |
* component's <code>addFileSystemObjectStoreResultSetListener<code> method. When |
|
26 |
* the fileSystemObjectStoreResultSet event occurs, that object's appropriate |
|
27 |
* method is invoked. |
|
28 |
* |
|
29 |
* @author sandro |
|
30 |
*/ |
|
31 |
public class S3ObjectStoreResultSetListener implements ResultSetListener, ResultSetAware { |
|
32 |
|
|
33 |
|
|
34 |
/** The Constant log. */ |
|
35 |
private static final Log log = LogFactory.getLog(S3ObjectStoreResultSetListener.class); // NOPMD by marko on 11/24/08 5:02 PM |
|
36 |
|
|
37 |
|
|
38 |
/** The from date. */ |
|
39 |
private Long fromDate; |
|
40 |
|
|
41 |
/** The until date. */ |
|
42 |
private Long untilDate; |
|
43 |
|
|
44 |
/** The records. */ |
|
45 |
private List<String> records; |
|
46 |
|
|
47 |
/** The object store id. */ |
|
48 |
private String objectStoreID; |
|
49 |
|
|
50 |
|
|
51 |
/** The mongo collection. */ |
|
52 |
private MongoCollection<Document> mongoCollection; |
|
53 |
|
|
54 |
/** The base uri. */ |
|
55 |
private String baseURI; |
|
56 |
|
|
57 |
/** The current size. */ |
|
58 |
private int currentSize = -1; |
|
59 |
|
|
60 |
/** The current cursor. */ |
|
61 |
private MongoCursor<Document> currentCursor; |
|
62 |
|
|
63 |
/** The cursor position. */ |
|
64 |
private long cursorPosition; |
|
65 |
|
|
66 |
@Override |
|
67 |
public List<String> getResult(final int from, final int to) { |
|
68 |
if (log.isDebugEnabled()) { |
|
69 |
log.debug(String.format("ObjectStoreId :%s, from: %d, to: %d", objectStoreID, from, to)); |
|
70 |
} |
|
71 |
if (records != null) { |
|
72 |
List<String> ids = Lists.newArrayList(); |
|
73 |
for (int i = from-1; i < Math.min(records.size(),to); i++) { |
|
74 |
ids.add(records.get(i)); |
|
75 |
} |
|
76 |
Bson q = Filters.in("id", ids); |
|
77 |
FindIterable<Document> res = getMongoCollection().find(q); |
|
78 |
return MappedCollection.listMap(res, ObjectStoreS3Utility.asJSON()); |
|
79 |
} else if ((fromDate != null) && (untilDate != null)) { |
|
80 |
if ((currentCursor == null) || (cursorPosition > from)) { |
|
81 |
createCurrentCursor(); |
|
82 |
} |
|
83 |
while (cursorPosition < from) { |
|
84 |
currentCursor.next(); |
|
85 |
cursorPosition++; |
|
86 |
} |
|
87 |
List<Document> result = Lists.newArrayList(); |
|
88 |
for (int i = from; i <= to; i++) { |
|
89 |
if (currentCursor.hasNext()) { |
|
90 |
result.add(currentCursor.next()); |
|
91 |
cursorPosition++; |
|
92 |
} |
|
93 |
} |
|
94 |
return MappedCollection.listMap(result, ObjectStoreS3Utility.asJSON()); |
|
95 |
} |
|
96 |
|
|
97 |
throw new IllegalArgumentException("Missing parameters on Delivery must provide either from, to, or ObjectStoreIDs"); |
|
98 |
} |
|
99 |
|
|
100 |
/** |
|
101 |
* Creates the current cursor. |
|
102 |
*/ |
|
103 |
private void createCurrentCursor() { |
|
104 |
Bson timestampQuery = Filters.and(Filters.gt("timestamp", fromDate.doubleValue()), Filters.lt("timestamp", untilDate.doubleValue())); |
|
105 |
if (currentCursor != null) { |
|
106 |
currentCursor.close(); |
|
107 |
} |
|
108 |
currentCursor = getMongoCollection().find(timestampQuery).sort(Sorts.orderBy(Filters.eq("_id", 1))).iterator(); |
|
109 |
cursorPosition = 1; |
|
110 |
|
|
111 |
} |
|
112 |
|
|
113 |
@Override |
|
114 |
public int getSize() { |
|
115 |
if (currentSize == -1) { |
|
116 |
currentSize = calculateSize(); |
|
117 |
} |
|
118 |
return Math.max(0, currentSize - 1); |
|
119 |
} |
|
120 |
|
|
121 |
/** |
|
122 |
* Calculate size. |
|
123 |
* |
|
124 |
* @return the int |
|
125 |
*/ |
|
126 |
private int calculateSize() { |
|
127 |
if (records != null) { |
|
128 |
Bson query = Filters.in("id", records); |
|
129 |
return (int) getMongoCollection().count(query); |
|
130 |
} else if ((fromDate != null) && (untilDate != null)) { |
|
131 |
Bson timestampQuery = Filters.and(Filters.gt("timestamp", fromDate.doubleValue()), Filters.lt("timestamp", untilDate.doubleValue())); |
|
132 |
return (int) getMongoCollection().count(timestampQuery); |
|
133 |
} |
|
134 |
return 0; |
|
135 |
} |
|
136 |
|
|
137 |
|
|
138 |
/** |
|
139 |
* {@inheritDoc} |
|
140 |
* @see ResultSetAware#setResultSet(ResultSet) |
|
141 |
*/ |
|
142 |
@Override |
|
143 |
public void setResultSet(final ResultSet resultSet) { |
|
144 |
resultSet.close(); |
|
145 |
} |
|
146 |
|
|
147 |
|
|
148 |
/** |
|
149 |
* Gets the from date. |
|
150 |
* |
|
151 |
* @return the fromDate |
|
152 |
*/ |
|
153 |
public Long getFromDate() { |
|
154 |
return fromDate; |
|
155 |
} |
|
156 |
|
|
157 |
|
|
158 |
/** |
|
159 |
* Sets the from date. |
|
160 |
* |
|
161 |
* @param fromDate the fromDate to set |
|
162 |
*/ |
|
163 |
public S3ObjectStoreResultSetListener setFromDate(final Long fromDate) { |
|
164 |
this.fromDate = fromDate; |
|
165 |
return this; |
|
166 |
} |
|
167 |
|
|
168 |
|
|
169 |
/** |
|
170 |
* Gets the until date. |
|
171 |
* |
|
172 |
* @return the untilDate |
|
173 |
*/ |
|
174 |
public Long getUntilDate() { |
|
175 |
return untilDate; |
|
176 |
} |
|
177 |
|
|
178 |
|
|
179 |
/** |
|
180 |
* Sets the until date. |
|
181 |
* |
|
182 |
* @param untilDate the untilDate to set |
|
183 |
*/ |
|
184 |
public S3ObjectStoreResultSetListener setUntilDate(final Long untilDate) { |
|
185 |
this.untilDate = untilDate; |
|
186 |
return this; |
|
187 |
} |
|
188 |
|
|
189 |
|
|
190 |
/** |
|
191 |
* Gets the records. |
|
192 |
* |
|
193 |
* @return the records |
|
194 |
*/ |
|
195 |
public List<String> getRecords() { |
|
196 |
return records; |
|
197 |
} |
|
198 |
|
|
199 |
|
|
200 |
/** |
|
201 |
* Sets the records. |
|
202 |
* |
|
203 |
* @param records the records to set |
|
204 |
*/ |
|
205 |
public void setRecords(final List<String> records) { |
|
206 |
this.records = records; |
|
207 |
} |
|
208 |
|
|
209 |
|
|
210 |
/** |
|
211 |
* Gets the object store id. |
|
212 |
* |
|
213 |
* @return the objectStoreID |
|
214 |
*/ |
|
215 |
public String getObjectStoreID() { |
|
216 |
return objectStoreID; |
|
217 |
} |
|
218 |
|
|
219 |
|
|
220 |
/** |
|
221 |
* Sets the object store id. |
|
222 |
* |
|
223 |
* @param objectStoreID the objectStoreID to set |
|
224 |
*/ |
|
225 |
public void setObjectStoreID(final String objectStoreID) { |
|
226 |
this.objectStoreID = objectStoreID; |
|
227 |
} |
|
228 |
|
|
229 |
|
|
230 |
|
|
231 |
|
|
232 |
|
|
233 |
/** |
|
234 |
* Gets the base uri. |
|
235 |
* |
|
236 |
* @return the baseURI |
|
237 |
*/ |
|
238 |
public String getBaseURI() { |
|
239 |
return baseURI; |
|
240 |
} |
|
241 |
|
|
242 |
|
|
243 |
/** |
|
244 |
* Sets the base uri. |
|
245 |
* |
|
246 |
* @param baseURI the baseURI to set |
|
247 |
*/ |
|
248 |
public void setBaseURI(final String baseURI) { |
|
249 |
this.baseURI = baseURI; |
|
250 |
} |
|
251 |
|
|
252 |
|
|
253 |
/** |
|
254 |
* Gets the current size. |
|
255 |
* |
|
256 |
* @return the currentSize |
|
257 |
*/ |
|
258 |
public int getCurrentSize() { |
|
259 |
return currentSize; |
|
260 |
} |
|
261 |
|
|
262 |
|
|
263 |
/** |
|
264 |
* Sets the current size. |
|
265 |
* |
|
266 |
* @param currentSize the currentSize to set |
|
267 |
*/ |
|
268 |
public void setCurrentSize(final int currentSize) { |
|
269 |
this.currentSize = currentSize; |
|
270 |
} |
|
271 |
|
|
272 |
|
|
273 |
/** |
|
274 |
* Gets the current cursor. |
|
275 |
* |
|
276 |
* @return the currentCursor |
|
277 |
*/ |
|
278 |
public MongoCursor<Document> getCurrentCursor() { |
|
279 |
return currentCursor; |
|
280 |
} |
|
281 |
|
|
282 |
|
|
283 |
/** |
|
284 |
* Sets the current cursor. |
|
285 |
* |
|
286 |
* @param currentCursor the currentCursor to set |
|
287 |
*/ |
|
288 |
public void setCurrentCursor(final MongoCursor<Document> currentCursor) { |
|
289 |
this.currentCursor = currentCursor; |
|
290 |
} |
|
291 |
|
|
292 |
|
|
293 |
/** |
|
294 |
* Gets the cursor position. |
|
295 |
* |
|
296 |
* @return the cursorPosition |
|
297 |
*/ |
|
298 |
public long getCursorPosition() { |
|
299 |
return cursorPosition; |
|
300 |
} |
|
301 |
|
|
302 |
|
|
303 |
/** |
|
304 |
* Sets the cursor position. |
|
305 |
* |
|
306 |
* @param cursorPosition the cursorPosition to set |
|
307 |
*/ |
|
308 |
public void setCursorPosition(final long cursorPosition) { |
|
309 |
this.cursorPosition = cursorPosition; |
|
310 |
} |
|
311 |
|
|
312 |
/** |
|
313 |
* Gets the mongo collection. |
|
314 |
* |
|
315 |
* @return the mongo collection |
|
316 |
*/ |
|
317 |
public MongoCollection<Document> getMongoCollection() { |
|
318 |
return mongoCollection; |
|
319 |
} |
|
320 |
|
|
321 |
/** |
|
322 |
* Sets the mongo collection. |
|
323 |
* |
|
324 |
* @param mongoCollection the new mongo collection |
|
325 |
*/ |
|
326 |
public void setMongoCollection(final MongoCollection<Document> mongoCollection) { |
|
327 |
this.mongoCollection = mongoCollection; |
|
328 |
} |
|
329 |
|
|
330 |
} |
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/src/main/java/eu/dnetlib/data/objectstore/s3/ObjectStoreS3Utility.java | ||
---|---|---|
1 |
package eu.dnetlib.data.objectstore.s3; |
|
2 |
|
|
3 |
import eu.dnetlib.data.objectstore.rmi.ObjectStoreFile; |
|
4 |
import eu.dnetlib.data.objectstore.rmi.Protocols; |
|
5 |
import eu.dnetlib.miscutils.functional.UnaryFunction; |
|
6 |
import org.apache.commons.lang3.StringUtils; |
|
7 |
import org.apache.commons.logging.Log; |
|
8 |
import org.apache.commons.logging.LogFactory; |
|
9 |
|
|
10 |
import org.bson.Document; |
|
11 |
|
|
12 |
|
|
13 |
/** |
|
14 |
* The Class ObjectStoreFileBuilder generates an objectStoreFile bean |
|
15 |
*/ |
|
16 |
public class ObjectStoreS3Utility { |
|
17 |
|
|
18 |
private static final int KB_SIZE = 1024; |
|
19 |
|
|
20 |
/** |
|
21 |
* The Constant log. |
|
22 |
*/ |
|
23 |
private static final Log log = LogFactory.getLog(ObjectStoreS3Utility.class); |
|
24 |
|
|
25 |
public static ObjectStoreFile build(final Document metadata) { |
|
26 |
|
|
27 |
final String originalFile = metadata.getString("originalObject"); |
|
28 |
final ObjectStoreFile original = ObjectStoreFile.createObject(originalFile); |
|
29 |
final ObjectStoreFile newFile = new ObjectStoreFile(); |
|
30 |
newFile.setObjectID(metadata.getString("id")); |
|
31 |
newFile.setAccessProtocol(Protocols.None); |
|
32 |
newFile.setMimeType( metadata.getString("mime")); |
|
33 |
newFile.setMd5Sum(metadata.getString("md5Sum")); |
|
34 |
try { |
|
35 |
newFile.setFileSizeKB(Long.parseLong(metadata.get("size").toString()) / KB_SIZE); |
|
36 |
} catch (Throwable e) { |
|
37 |
log.error("Error on getting file size", e); |
|
38 |
} |
|
39 |
if (originalFile != null) { |
|
40 |
newFile.setMetadataRelatedID(original.getMetadataRelatedID()); |
|
41 |
if (StringUtils.isBlank(original.getDownloadedURL())) { |
|
42 |
newFile.setDownloadedURL(original.getURI()); |
|
43 |
} else { |
|
44 |
newFile.setDownloadedURL(original.getDownloadedURL()); |
|
45 |
} |
|
46 |
} |
|
47 |
newFile.setURI(metadata.get("uri").toString()); |
|
48 |
return newFile; |
|
49 |
} |
|
50 |
|
|
51 |
public static UnaryFunction<String, Document> asJSON() { |
|
52 |
return input -> build(input).toJSON(); |
|
53 |
} |
|
54 |
} |
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/src/main/java/eu/dnetlib/data/objectstore/s3/S3ObjectStoreDao.java | ||
---|---|---|
1 |
package eu.dnetlib.data.objectstore.s3; |
|
2 |
|
|
3 |
import com.mongodb.BasicDBObject; |
|
4 |
import com.mongodb.client.MongoCollection; |
|
5 |
import com.mongodb.client.MongoDatabase; |
|
6 |
import com.mongodb.client.model.Filters; |
|
7 |
import com.mongodb.client.model.IndexModel; |
|
8 |
import com.mongodb.client.result.DeleteResult; |
|
9 |
import com.mongodb.client.result.UpdateResult; |
|
10 |
import eu.dnetlib.data.objectstore.modular.connector.ObjectStore; |
|
11 |
import eu.dnetlib.data.objectstore.modular.connector.ObjectStoreDao; |
|
12 |
import eu.dnetlib.data.objectstore.rmi.ObjectStoreFileNotFoundException; |
|
13 |
import eu.dnetlib.data.objectstore.rmi.ObjectStoreServiceException; |
|
14 |
import org.apache.commons.lang3.StringUtils; |
|
15 |
import org.apache.commons.logging.Log; |
|
16 |
import org.apache.commons.logging.LogFactory; |
|
17 |
import org.bson.Document; |
|
18 |
import org.bson.conversions.Bson; |
|
19 |
import org.springframework.beans.factory.annotation.Value; |
|
20 |
|
|
21 |
import javax.annotation.Resource; |
|
22 |
import java.util.Arrays; |
|
23 |
import java.util.List; |
|
24 |
import java.util.stream.Collectors; |
|
25 |
import java.util.stream.StreamSupport; |
|
26 |
|
|
27 |
public class S3ObjectStoreDao implements ObjectStoreDao { |
|
28 |
public static final String INTERPRETATION_FIELD = "interpretation"; |
|
29 |
public final static String OBJECTSTORE_METADATA_NAME_FIELD = "metadataObjectStore"; |
|
30 |
public final static String OBJECTSTORE_ID_FIELD = "obsId"; |
|
31 |
private static final String OBJECTSTORE_PROFILE_SUFFIX = "_T2JqZWN0U3RvcmVEU1Jlc291cmNlcy9PYmplY3RTdG9yZURTUmVzb3VyY2VUeXBl"; |
|
32 |
private static final Log log = LogFactory.getLog(S3ObjectStoreDao.class); |
|
33 |
|
|
34 |
private static final List<IndexModel> metadataIndexes = Arrays.asList(new IndexModel(new Document().append("id",1)), new IndexModel(new Document().append("timestamp",1))); |
|
35 |
|
|
36 |
@Resource(name="objectstoreMongoDB") |
|
37 |
private MongoDatabase db; |
|
38 |
|
|
39 |
@Value("${dnet.objectStore.s3.accessKey}") |
|
40 |
private String accessKey; |
|
41 |
|
|
42 |
|
|
43 |
@Value("${dnet.objectStore.s3.secretKey}") |
|
44 |
private String secretKey; |
|
45 |
|
|
46 |
@Value("${dnet.objectStore.s3.endPoint}") |
|
47 |
private String s3EndPoint; |
|
48 |
|
|
49 |
@Value("${dnet.objectStore.s3.objectStoreBucket}") |
|
50 |
private String objectStoreBucket; |
|
51 |
|
|
52 |
|
|
53 |
private Document getObjectStoreMetadata(final String objectStoreId) { |
|
54 |
String find_id = objectStoreId; |
|
55 |
if (objectStoreId.length() == 36) { |
|
56 |
find_id += OBJECTSTORE_PROFILE_SUFFIX; |
|
57 |
} |
|
58 |
final MongoCollection<Document> metadataObjectStore = getDb().getCollection(OBJECTSTORE_METADATA_NAME_FIELD); |
|
59 |
final Bson query = Filters.eq(OBJECTSTORE_ID_FIELD, find_id); |
|
60 |
log.debug("QUERY :" + query.toString()); |
|
61 |
final Document resultQuery = metadataObjectStore.find(query).first(); |
|
62 |
log.debug("result " + resultQuery); |
|
63 |
return resultQuery; |
|
64 |
} |
|
65 |
|
|
66 |
@Override |
|
67 |
public ObjectStore getObjectStore(final String objectStoreId) throws ObjectStoreServiceException { |
|
68 |
if (StringUtils.isBlank(objectStoreId)) throw new ObjectStoreServiceException("Error on getting ObjectStore, id is Blank"); |
|
69 |
final Document resultQuery = getObjectStoreMetadata(objectStoreId); |
|
70 |
if ((resultQuery == null)) throw new ObjectStoreFileNotFoundException("the objectStore with identifier: "+objectStoreId+" was not found"); |
|
71 |
final MongoCollection<Document> collection = getDb().getCollection(objectStoreId.substring(0,36)); |
|
72 |
collection.createIndexes(metadataIndexes); |
|
73 |
return new S3ObjectStore(resultQuery.getString(OBJECTSTORE_ID_FIELD),resultQuery.getString(INTERPRETATION_FIELD),accessKey, secretKey, s3EndPoint, objectStoreBucket, collection); |
|
74 |
} |
|
75 |
|
|
76 |
@Override |
|
77 |
public List<String> listObjectStores() { |
|
78 |
MongoCollection<Document> metadata = getDb().getCollection(OBJECTSTORE_METADATA_NAME_FIELD); |
|
79 |
Iterable<Document> tmp = () -> metadata.find().iterator(); |
|
80 |
return StreamSupport.stream(tmp.spliterator(), false).map(it-> it.getString(OBJECTSTORE_ID_FIELD)).collect(Collectors.toList()); |
|
81 |
} |
|
82 |
|
|
83 |
|
|
84 |
@Override |
|
85 |
public boolean createObjectStore(final String obsId, final String interpretation, final String basePath) throws ObjectStoreServiceException { |
|
86 |
if (getObjectStoreMetadata(obsId)!= null) |
|
87 |
throw new ObjectStoreServiceException("Error unable to create an ObjectStore that already exists in mongo"); |
|
88 |
final MongoCollection<Document> metadata = getDb().getCollection(OBJECTSTORE_METADATA_NAME_FIELD); |
|
89 |
final Document item = new Document() |
|
90 |
.append(OBJECTSTORE_ID_FIELD, obsId) |
|
91 |
.append(INTERPRETATION_FIELD, interpretation); |
|
92 |
metadata.insertOne(item); |
|
93 |
MongoCollection<Document> objectStore = getDb().getCollection(obsId.substring(0, 36)); |
|
94 |
objectStore.createIndex(new BasicDBObject("id", 1)); |
|
95 |
objectStore.createIndex(new BasicDBObject("timestamp", 1)); |
|
96 |
return true; |
|
97 |
} |
|
98 |
|
|
99 |
@Override |
|
100 |
public boolean updateObjectStore(final String obsId, final String interpretation) { |
|
101 |
MongoCollection<Document> coll = getDb().getCollection(OBJECTSTORE_METADATA_NAME_FIELD); |
|
102 |
final Document update = new Document().append("$set", new Document(INTERPRETATION_FIELD, interpretation)); |
|
103 |
final UpdateResult updateResult = coll.updateOne(Filters.eq(OBJECTSTORE_ID_FIELD, obsId), update); |
|
104 |
if (updateResult.isModifiedCountAvailable()) { |
|
105 |
log.debug("Matched / Modified " + updateResult.getMatchedCount() + " / " + updateResult.getModifiedCount()); |
|
106 |
} |
|
107 |
return true; |
|
108 |
} |
|
109 |
|
|
110 |
@Override |
|
111 |
public boolean deleteObjectStore(String obsId) throws ObjectStoreServiceException { |
|
112 |
final Document objectStoreMetadata = getObjectStoreMetadata(obsId); |
|
113 |
if (objectStoreMetadata== null) |
|
114 |
throw new ObjectStoreServiceException("ObjectStore not found with Identifier "+obsId); |
|
115 |
log.debug("Start to deleting all the object on the ObjectStore in teh bucket"); |
|
116 |
getObjectStore(obsId.substring(0,36)).dropContent(); |
|
117 |
log.debug("All object Deleted"); |
|
118 |
log.debug("Deleting mongo collection"); |
|
119 |
MongoCollection<Document> objectStoreCollection = db.getCollection(objectStoreMetadata.getString(OBJECTSTORE_ID_FIELD)); |
|
120 |
objectStoreCollection.drop(); |
|
121 |
log.debug("Deleting item on mongo metadata Collection"); |
|
122 |
final MongoCollection<Document> metadata = getDb().getCollection(OBJECTSTORE_METADATA_NAME_FIELD); |
|
123 |
DeleteResult deleteResult = metadata.deleteOne(Filters.eq(OBJECTSTORE_ID_FIELD, obsId)); |
|
124 |
if (deleteResult.getDeletedCount() != 1) |
|
125 |
throw new ObjectStoreServiceException("Unexpected number of Deleting object on ObjectStoreMetadata, should be 1 instead of"+deleteResult.getDeletedCount()); |
|
126 |
return true; |
|
127 |
} |
|
128 |
|
|
129 |
@Override |
|
130 |
public boolean dropContent(String obsId) throws ObjectStoreServiceException { |
|
131 |
return getObjectStore(obsId).dropContent(); |
|
132 |
} |
|
133 |
|
|
134 |
public MongoDatabase getDb() { |
|
135 |
return db; |
|
136 |
} |
|
137 |
|
|
138 |
public void setDb(MongoDatabase db) { |
|
139 |
this.db = db; |
|
140 |
} |
|
141 |
} |
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/src/main/resources/eu/dnetlib/data/objectsore/s3/applicationContext-s3-objectstore.properties | ||
---|---|---|
1 |
services.objectStore.mongodb.host=localhost |
|
2 |
services.objectStore.mongodb.port=27017 |
|
3 |
services.objectStore.mongodb.db=objectStore |
|
4 |
services.objectStore.mongodb.connectionsPerHost=20 |
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/src/main/resources/eu/dnetlib/data/objectsore/s3/applicationContext-s3-objectstore.xml | ||
---|---|---|
1 |
<?xml version="1.0" encoding="UTF-8"?> |
|
2 |
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
3 |
xmlns:p="http://www.springframework.org/schema/p" |
|
4 |
xmlns="http://www.springframework.org/schema/beans" |
|
5 |
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
6 |
|
|
7 |
<bean id="S3ObjectstoreDao" |
|
8 |
class="eu.dnetlib.data.objectstore.s3.S3ObjectStoreDao" |
|
9 |
p:db-ref="objectstoreMongoDB"/> |
|
10 |
|
|
11 |
|
|
12 |
|
|
13 |
<bean id="objectStoreMongoClient" class="com.mongodb.MongoClient"> |
|
14 |
<constructor-arg index="0" type="com.mongodb.ServerAddress"> |
|
15 |
<bean class="com.mongodb.ServerAddress"> |
|
16 |
<constructor-arg index="0" value="${services.objectStore.mongodb.host}"/> |
|
17 |
<constructor-arg index="1" value="${services.objectStore.mongodb.port}"/> |
|
18 |
</bean> |
|
19 |
</constructor-arg> |
|
20 |
<constructor-arg index="1" type="com.mongodb.MongoClientOptions"> |
|
21 |
<bean class="eu.dnetlib.data.objectstore.s3.MongoS3OptionsFactory" |
|
22 |
p:connectionsPerHost="${services.objectStore.mongodb.connectionsPerHost}"/> |
|
23 |
</constructor-arg> |
|
24 |
</bean> |
|
25 |
|
|
26 |
<bean id="objectstoreMongoDB" factory-bean="objectStoreMongoClient" factory-method="getDatabase"> |
|
27 |
<constructor-arg index="0" type="java.lang.String" value="${services.objectStore.mongodb.db}"/> |
|
28 |
</bean> |
|
29 |
|
|
30 |
</beans> |
modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0/pom.xml | ||
---|---|---|
1 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|
2 |
<modelVersion>4.0.0</modelVersion> |
|
3 |
<parent> |
|
4 |
<groupId>eu.dnetlib</groupId> |
|
5 |
<artifactId>dnet45-parent</artifactId> |
|
6 |
<version>1.0.0</version> |
|
7 |
</parent> |
|
8 |
<groupId>eu.dnetlib</groupId> |
|
9 |
<artifactId>dnet-s3-objectStore</artifactId> |
|
10 |
<version>1.0.0</version> |
|
11 |
<scm> |
|
12 |
<developerConnection> |
|
13 |
scm:svn:https://svn.driver.research-infrastructures.eu/driver/dnet45/modules/dnet-s3-objectStore/tags/dnet-s3-objectStore-1.0.0 |
|
14 |
</developerConnection> |
|
15 |
</scm> |
|
16 |
<dependencies> |
|
17 |
<dependency> |
|
18 |
<groupId>com.amazonaws</groupId> |
|
19 |
<artifactId>aws-java-sdk</artifactId> |
|
20 |
<version>1.11.478</version> |
|
21 |
</dependency> |
|
22 |
|
|
23 |
<dependency> |
|
24 |
<groupId>eu.dnetlib</groupId> |
|
25 |
<artifactId>dnet-modular-objectstore-service</artifactId> |
|
26 |
<version>[4.2.0,5.0.0)</version> |
|
27 |
</dependency> |
|
28 |
<dependency> |
|
29 |
<groupId>eu.dnetlib</groupId> |
|
30 |
<artifactId>cnr-resultset-service</artifactId> |
|
31 |
<version>[2.0.0,3.0.0)</version> |
|
32 |
</dependency> |
|
33 |
<dependency> |
|
34 |
<groupId>eu.dnetlib</groupId> |
|
35 |
<artifactId>cnr-resultset-client</artifactId> |
|
36 |
<version>[2.0.0,3.0.0)</version> |
|
37 |
</dependency> |
|
38 |
<dependency> |
|
39 |
<groupId>junit</groupId> |
|
40 |
<artifactId>junit</artifactId> |
|
41 |
<version>${junit.version}</version> |
|
42 |
<scope>test</scope> |
|
43 |
</dependency> |
|
44 |
<dependency> |
|
45 |
<groupId>org.mongodb</groupId> |
|
46 |
<artifactId>mongo-java-driver</artifactId> |
|
47 |
<version>${mongodb.driver.version}</version> |
|
48 |
</dependency> |
|
49 |
<dependency> |
|
50 |
<groupId>javax.servlet</groupId> |
|
51 |
<artifactId>javax.servlet-api</artifactId> |
|
52 |
<version>${javax.servlet.version}</version> |
|
53 |
<scope>provided</scope> |
|
54 |
</dependency> |
|
55 |
|
|
56 |
</dependencies> |
|
57 |
</project> |
Also available in: Unified diff
[maven-release-plugin] copy for tag dnet-s3-objectStore-1.0.0