1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.assetmanager.storage.impl.fs;
22
23 import static org.apache.commons.io.FilenameUtils.EXTENSION_SEPARATOR;
24 import static org.apache.commons.io.FilenameUtils.getExtension;
25 import static org.apache.commons.lang3.exception.ExceptionUtils.getMessage;
26 import static org.opencastproject.util.FileSupport.link;
27 import static org.opencastproject.util.IoSupport.file;
28 import static org.opencastproject.util.PathSupport.path;
29 import static org.opencastproject.util.data.functions.Strings.trimToNone;
30
31 import org.opencastproject.assetmanager.api.storage.AssetStore;
32 import org.opencastproject.assetmanager.api.storage.AssetStoreException;
33 import org.opencastproject.assetmanager.api.storage.DeletionSelector;
34 import org.opencastproject.assetmanager.api.storage.Source;
35 import org.opencastproject.assetmanager.api.storage.StoragePath;
36 import org.opencastproject.util.FileSupport;
37 import org.opencastproject.util.NotFoundException;
38 import org.opencastproject.util.data.Option;
39 import org.opencastproject.workspace.api.Workspace;
40
41 import org.apache.commons.io.FileUtils;
42 import org.apache.commons.io.FilenameUtils;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.FileNotFoundException;
49 import java.io.FilenameFilter;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.net.MalformedURLException;
53 import java.net.URI;
54 import java.util.Optional;
55
56 public abstract class AbstractFileSystemAssetStore implements AssetStore {
57
58 private static final Logger logger = LoggerFactory.getLogger(AbstractFileSystemAssetStore.class);
59
60
61 protected String storeType = null;
62
63 protected abstract Workspace getWorkspace();
64
65 protected abstract String getRootDirectory();
66 protected abstract String getRootDirectory(String orgId, String mpId);
67
68
69
70
71
72
73
74 protected abstract void onDeleteMediaPackage(String orgId, String mpId);
75
76 @Override
77 public void put(StoragePath storagePath, Source source) throws AssetStoreException {
78
79
80
81
82 final File origin = getUniqueFileFromWorkspace(source);
83 final File destination = createFile(storagePath, source);
84 try {
85 mkParent(destination);
86 link(origin, destination);
87 } catch (IOException e) {
88 logger.error("Error while linking/copying file {} to {}: {}", origin, destination, getMessage(e));
89 throw new AssetStoreException(e);
90 } finally {
91 if (origin != null) {
92 FileUtils.deleteQuietly(origin);
93 }
94 }
95 }
96
97 private File getUniqueFileFromWorkspace(Source source) {
98 try {
99 return getWorkspace().get(source.getUri(), true);
100 } catch (NotFoundException e) {
101 logger.error("Source file '{}' does not exist", source.getUri());
102 throw new AssetStoreException(e);
103 } catch (IOException e) {
104 logger.error("Error while getting file '{}' from workspace: {}", source.getUri(), getMessage(e));
105 throw new AssetStoreException(e);
106 }
107 }
108
109 @Override
110 public boolean copy(final StoragePath from, final StoragePath to) throws AssetStoreException {
111 var file = findStoragePathFile(from);
112 if (file.isPresent()) {
113 var f = file.get();
114
115 final File t = createFile(to, f);
116 mkParent(t);
117 logger.debug("Copying {} to {}", f.getAbsolutePath(), t.getAbsolutePath());
118 try {
119 link(f, t, true);
120 } catch (IOException e) {
121 logger.error("Error copying archive file {} to {}", f, t);
122 throw new AssetStoreException(e);
123 }
124 return true;
125 }
126 return false;
127 }
128
129 @Override
130 public Optional<InputStream> get(final StoragePath path) throws AssetStoreException {
131 var file = findStoragePathFile(path);
132 if (file.isPresent()) {
133 try {
134 return Optional.of(new FileInputStream(file.get()));
135 } catch (FileNotFoundException e) {
136 logger.error("Error getting archive file {}", file);
137 throw new AssetStoreException(e);
138 }
139 }
140 return Optional.empty();
141 }
142
143 @Override
144 public boolean contains(StoragePath path) throws AssetStoreException {
145 return findStoragePathFile(path).isPresent();
146 }
147
148 @Override
149 public boolean delete(DeletionSelector sel) throws AssetStoreException {
150 File dir = getDeletionSelectorDir(sel);
151 if (dir == null) {
152
153
154
155
156
157
158 return false;
159 }
160 try {
161 FileUtils.deleteDirectory(dir);
162
163 boolean mpDirDeleted = FileSupport.deleteHierarchyIfEmpty(file(path(
164 getRootDirectory(sel.getOrganizationId(), sel.getMediaPackageId()), sel.getOrganizationId())),
165 dir.getParentFile());
166 if (mpDirDeleted) {
167 onDeleteMediaPackage(sel.getOrganizationId(), sel.getMediaPackageId());
168 }
169 return true;
170 } catch (IOException e) {
171 logger.error("Error deleting directory from archive {}", dir);
172 throw new AssetStoreException(e);
173 }
174 }
175
176
177
178
179
180
181
182
183 private File getDeletionSelectorDir(DeletionSelector sel) {
184 final String rootPath = getRootDirectory(sel.getOrganizationId(), sel.getMediaPackageId());
185 if (rootPath == null) {
186 return null;
187 }
188 final String basePath = path(rootPath, sel.getOrganizationId(), sel.getMediaPackageId());
189 if (sel.getVersion().isPresent()) {
190 return file(basePath, sel.getVersion().get().toString());
191 }
192 return file(basePath);
193 }
194
195
196 private void mkParent(File f) {
197 mkDirs(f.getParentFile());
198 }
199
200
201 protected void mkDirs(File d) {
202 if (d == null) {
203 return;
204 }
205
206 if (!d.mkdirs() && !d.exists()) {
207 final String msg = "Cannot create directory " + d;
208 logger.error(msg);
209 throw new AssetStoreException(msg);
210 }
211 }
212
213
214 private Optional<String> extension(File f) {
215 Option<String> opt = trimToNone(getExtension(f.getAbsolutePath()));
216 return opt.isSome()
217 ? Optional.of(opt.get())
218 : Optional.empty();
219 }
220
221
222 private Optional<String> extension(URI uri) {
223 try {
224 Option<String> opt = trimToNone(getExtension(uri.toURL().getPath()));
225 return opt.isSome()
226 ? Optional.of(opt.get())
227 : Optional.empty();
228 } catch (MalformedURLException e) {
229 throw new Error(e);
230 }
231 }
232
233
234 private File createFile(StoragePath p, File f) {
235 return createFile(p, extension(f));
236 }
237
238
239 private File createFile(StoragePath p, Source s) {
240 return createFile(p, extension(s.getUri()));
241 }
242
243
244 private File createFile(StoragePath p, Optional<String> extension) {
245 String rootDirectory = getRootDirectory(p.getOrganizationId(), p.getMediaPackageId());
246 if (rootDirectory == null) {
247 rootDirectory = getRootDirectory();
248 }
249 return file(
250 rootDirectory,
251 p.getOrganizationId(),
252 p.getMediaPackageId(),
253 p.getVersion().toString(),
254 extension.isPresent() ? p.getMediaPackageElementId() + EXTENSION_SEPARATOR + extension.get() : p
255 .getMediaPackageElementId());
256 }
257
258
259 private File getExistingFile(StoragePath p, Optional<String> extension) {
260 String rootDirectory = getRootDirectory(p.getOrganizationId(), p.getMediaPackageId());
261 if (rootDirectory == null) {
262 return null;
263 }
264 return file(
265 rootDirectory,
266 p.getOrganizationId(),
267 p.getMediaPackageId(),
268 p.getVersion().toString(),
269 extension.isPresent() ? p.getMediaPackageElementId() + EXTENSION_SEPARATOR + extension.get() : p
270 .getMediaPackageElementId());
271 }
272
273
274
275
276
277
278
279
280 private Optional<File> findStoragePathFile(final StoragePath storagePath) {
281 final FilenameFilter filter = new FilenameFilter() {
282 @Override
283 public boolean accept(File dir, String name) {
284 return FilenameUtils.getBaseName(name).equals(storagePath.getMediaPackageElementId());
285 }
286 };
287 final File containerDir = getExistingFile(storagePath, Optional.empty()).getParentFile();
288
289 var files = containerDir.listFiles(filter);
290 if (files == null) {
291 return Optional.empty();
292 }
293 switch (files.length) {
294 case 0:
295 return Optional.empty();
296 case 1:
297 return Optional.of(files[0]);
298 default:
299 throw new AssetStoreException("Storage path " + files[0].getParent()
300 + "contains multiple files with the same element id!: " + storagePath.getMediaPackageElementId());
301 }
302 }
303
304 @Override
305 public String getStoreType() {
306 return storeType;
307 }
308
309 }