1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.assetmanager.aws;
23
24 import static org.apache.commons.lang3.exception.ExceptionUtils.getMessage;
25
26 import org.opencastproject.assetmanager.api.storage.AssetStore;
27 import org.opencastproject.assetmanager.api.storage.AssetStoreException;
28 import org.opencastproject.assetmanager.api.storage.DeletionSelector;
29 import org.opencastproject.assetmanager.api.storage.Source;
30 import org.opencastproject.assetmanager.api.storage.StoragePath;
31 import org.opencastproject.assetmanager.aws.persistence.AwsAssetDatabase;
32 import org.opencastproject.assetmanager.aws.persistence.AwsAssetDatabaseException;
33 import org.opencastproject.assetmanager.aws.persistence.AwsAssetMapping;
34 import org.opencastproject.assetmanager.impl.VersionImpl;
35 import org.opencastproject.util.ConfigurationException;
36 import org.opencastproject.util.MimeType;
37 import org.opencastproject.util.NotFoundException;
38 import org.opencastproject.util.OsgiUtil;
39 import org.opencastproject.workspace.api.Workspace;
40
41 import org.apache.commons.io.FilenameUtils;
42 import org.apache.commons.lang3.StringUtils;
43 import org.osgi.service.component.ComponentContext;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import java.io.File;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.util.List;
51 import java.util.Optional;
52
53 public abstract class AwsAbstractArchive implements AssetStore {
54
55
56 private static final Logger logger = LoggerFactory.getLogger(AwsAbstractArchive.class);
57
58 protected Workspace workspace;
59 protected AwsAssetDatabase database;
60
61
62 protected String storeType = null;
63
64 protected String regionName = null;
65
66 protected String getAWSConfigKey(ComponentContext cc, String key) {
67 try {
68 String value = StringUtils.trimToEmpty(OsgiUtil.getComponentContextProperty(cc, key));
69 if (StringUtils.isNotBlank(value)) {
70 return value;
71 }
72 throw new ConfigurationException(key + " is invalid");
73 } catch (RuntimeException e) {
74 throw new ConfigurationException(key + " is missing or invalid", e);
75 }
76 }
77
78 public Optional<Long> getUsedSpace() {
79 throw new UnsupportedOperationException("Not implemented");
80 }
81
82 public Optional<Long> getUsableSpace() {
83 throw new UnsupportedOperationException("Not implemented");
84 }
85
86 public Optional<Long> getTotalSpace() {
87 throw new UnsupportedOperationException("Not implemented");
88 }
89
90 public String getStoreType() {
91 return this.storeType;
92 }
93
94 public String getRegion() {
95 return this.regionName;
96 }
97
98
99 public void setWorkspace(Workspace workspace) {
100 this.workspace = workspace;
101 }
102
103
104 public void setDatabase(AwsAssetDatabase db) {
105 this.database = db;
106 }
107
108
109 public boolean copy(final StoragePath from, final StoragePath to) throws AssetStoreException {
110 try {
111 AwsAssetMapping map = database.findMapping(from);
112 if (!contains(from)) {
113 logger.warn("Origin file mapping not found in database: {}", from);
114 return false;
115 }
116
117 logger.debug("Adding AWS {} link mapping to database: {} points to {}, version {}", getStoreType(),
118 to, map.getObjectKey(), map.getObjectVersion());
119 database.storeMapping(to, map.getObjectKey(), map.getObjectVersion());
120 return true;
121 } catch (AwsAssetDatabaseException e) {
122 throw new AssetStoreException(e);
123 }
124 }
125
126 public boolean contains(StoragePath path) throws AssetStoreException {
127 try {
128 AwsAssetMapping map = database.findMapping(path);
129 return (map != null);
130 } catch (AwsAssetDatabaseException e) {
131 throw new AssetStoreException(e);
132 }
133 }
134
135 protected File getFileFromWorkspace(Source source) {
136 try {
137 return workspace.get(source.getUri());
138 } catch (NotFoundException e) {
139 logger.error("Source file '{}' does not exist", source.getUri());
140 throw new AssetStoreException(e);
141 } catch (IOException e) {
142 logger.error("Error while getting file '{}' from workspace: {}", source.getUri(), getMessage(e));
143 throw new AssetStoreException(e);
144 }
145 }
146
147 public String buildObjectName(File origin, StoragePath storagePath) {
148
149
150
151
152
153 String fileExt = FilenameUtils.getExtension(origin.getName());
154 return buildFilename(storagePath, fileExt.isEmpty() ? "" : "." + fileExt);
155 }
156
157
158
159
160 protected String buildFilename(StoragePath path, String ext) {
161
162 return StringUtils.join(new String[] { path.getOrganizationId(), path.getMediaPackageId(),
163 path.getVersion().toString(), path.getMediaPackageElementId() + ext }, "/");
164 }
165
166
167
168
169 public void put(StoragePath storagePath, Source source) throws AssetStoreException {
170
171
172 final File origin = getFileFromWorkspace(source);
173
174 String objectName = buildObjectName(origin, storagePath);
175 String objectVersion = null;
176 try {
177
178 AwsUploadOperationResult result = uploadObject(storagePath.getOrganizationId(), origin, objectName,
179 source.getMimeType());
180 objectName = result.getObjectName();
181 objectVersion = result.getObjectVersion();
182 } catch (Exception e) {
183 throw new AssetStoreException(e);
184 }
185
186 try {
187
188 logger.debug("Adding AWS {} mapping to database: {} points to {}, object version {}", getStoreType(),
189 storagePath, objectName, objectVersion);
190 database.storeMapping(storagePath, objectName, objectVersion);
191 } catch (AwsAssetDatabaseException e) {
192 throw new AssetStoreException(e);
193 }
194 }
195
196 protected abstract AwsUploadOperationResult uploadObject(String orgId, File origin, String objectName,
197 Optional<MimeType> mimeType) throws AssetStoreException;
198
199
200 public Optional<InputStream> get(final StoragePath path) throws AssetStoreException {
201 try {
202 AwsAssetMapping map = database.findMapping(path);
203 if (map == null) {
204 logger.warn("File mapping not found in database: {}", path);
205 return Optional.empty();
206 }
207
208 logger.debug("Getting archive object from AWS {}: {}", getStoreType(), map.getObjectKey());
209 return Optional.of(getObject(map));
210
211 } catch (AssetStoreException e) {
212 throw e;
213 } catch (AwsAssetDatabaseException e) {
214 throw new AssetStoreException(e);
215 }
216 }
217
218 protected abstract InputStream getObject(AwsAssetMapping map) throws AssetStoreException;
219
220
221 public boolean delete(DeletionSelector sel) throws AssetStoreException {
222
223 StoragePath path = new StoragePath(
224 sel.getOrganizationId(), sel.getMediaPackageId(), sel.getVersion().orElse(null), null);
225 try {
226 List<AwsAssetMapping> list = database.findMappingsByMediaPackageAndVersion(path);
227
228 for (AwsAssetMapping map : list) {
229
230 List<AwsAssetMapping> links = database.findMappingsByKey(map.getObjectKey());
231 if (links.size() == 1) {
232
233 logger.debug("Deleting archive object from AWS {}: {}, version {}",
234 getStoreType(), map.getObjectKey(), map.getObjectVersion());
235 deleteObject(map);
236 logger.info("Archive object deleted from AWS {}: {}, version {}",
237 getStoreType(), map.getObjectKey(), map.getObjectVersion());
238 }
239
240 database.deleteMapping(new StoragePath(
241 map.getOrganizationId(),
242 map.getMediaPackageId(),
243 new VersionImpl(map.getVersion()),
244 map.getMediaPackageElementId()));
245 }
246 return true;
247 } catch (AwsAssetDatabaseException e) {
248 throw new AssetStoreException(e);
249 }
250 }
251
252 protected abstract void deleteObject(AwsAssetMapping map) throws AssetStoreException;
253 }