1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.documentlibrary.util;
24  
25  import com.liferay.documentlibrary.NoSuchFileException;
26  import com.liferay.portal.PortalException;
27  import com.liferay.portal.SystemException;
28  import com.liferay.portal.kernel.search.Document;
29  import com.liferay.portal.kernel.search.Field;
30  import com.liferay.portal.kernel.search.SearchEngineUtil;
31  import com.liferay.portal.kernel.search.SearchException;
32  import com.liferay.portal.kernel.util.FileUtil;
33  import com.liferay.portal.kernel.util.GetterUtil;
34  import com.liferay.portal.kernel.util.StringPool;
35  import com.liferay.portal.kernel.util.Validator;
36  import com.liferay.portal.util.PropsKeys;
37  import com.liferay.portal.util.PropsUtil;
38  import com.liferay.util.SystemProperties;
39  import com.liferay.util.servlet.ServletResponseUtil;
40  
41  import java.io.File;
42  import java.io.FileInputStream;
43  import java.io.IOException;
44  import java.io.InputStream;
45  
46  import java.util.ArrayList;
47  import java.util.Arrays;
48  import java.util.HashSet;
49  import java.util.Iterator;
50  import java.util.List;
51  import java.util.Set;
52  
53  import org.apache.commons.id.uuid.UUID;
54  import org.apache.commons.logging.Log;
55  import org.apache.commons.logging.LogFactory;
56  
57  import org.jets3t.service.S3Service;
58  import org.jets3t.service.S3ServiceException;
59  import org.jets3t.service.impl.rest.httpclient.RestS3Service;
60  import org.jets3t.service.model.S3Bucket;
61  import org.jets3t.service.model.S3Object;
62  import org.jets3t.service.security.AWSCredentials;
63  
64  /**
65   * <a href="S3Hook.java.html"><b><i>View Source</i></b></a>
66   *
67   * @author Brian Wing Shun Chan
68   * @author Sten Martinez
69   *
70   */
71  public class S3Hook extends BaseHook {
72  
73      public S3Hook() {
74          try {
75              _s3Service = getS3Service();
76              _s3Bucket = getS3Bucket();
77          }
78          catch (S3ServiceException s3se) {
79              _log.error(s3se.getMessage());
80          }
81      }
82  
83      public void addDirectory(
84          long companyId, long repositoryId, String dirName) {
85      }
86  
87      public void addFile(
88              long companyId, String portletId, long groupId, long repositoryId,
89              String fileName, String properties, String[] tagsEntries,
90              InputStream is)
91          throws SystemException {
92  
93          try {
94              S3Object s3Object = new S3Object(
95                  _s3Bucket,
96                  getKey(companyId, repositoryId, fileName, DEFAULT_VERSION));
97  
98              s3Object.setDataInputStream(is);
99  
100             _s3Service.putObject(_s3Bucket, s3Object);
101 
102             Indexer.addFile(
103                 companyId, portletId, groupId, repositoryId, fileName,
104                 properties, tagsEntries);
105         }
106         catch (S3ServiceException s3se) {
107             throw new SystemException(s3se);
108         }
109         catch (SearchException se) {
110             throw new SystemException(se);
111         }
112     }
113 
114     public void checkRoot(long companyId) {
115     }
116 
117     public void deleteDirectory(
118             long companyId, String portletId, long repositoryId, String dirName)
119         throws SystemException {
120 
121         try {
122             S3Object[] s3Objects = _s3Service.listObjects(
123                 _s3Bucket, getKey(companyId, repositoryId, dirName), null);
124 
125             for (int i = 0; i < s3Objects.length; i++) {
126                 S3Object s3Object = s3Objects[i];
127 
128                 _s3Service.deleteObject(_s3Bucket, s3Object.getKey());
129             }
130         }
131         catch (S3ServiceException s3se) {
132             throw new SystemException(s3se);
133         }
134     }
135 
136     public void deleteFile(
137             long companyId, String portletId, long repositoryId,
138             String fileName)
139         throws SystemException {
140 
141         try {
142             S3Object[] s3Objects = _s3Service.listObjects(
143                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
144 
145             for (int i = 0; i < s3Objects.length; i++) {
146                 S3Object s3Object = s3Objects[i];
147 
148                 _s3Service.deleteObject(_s3Bucket, s3Object.getKey());
149             }
150 
151             Indexer.deleteFile(companyId, portletId, repositoryId, fileName);
152         }
153         catch (S3ServiceException s3se) {
154             throw new SystemException(s3se);
155         }
156         catch (SearchException se) {
157             throw new SystemException(se);
158         }
159     }
160 
161     public void deleteFile(
162             long companyId, String portletId, long repositoryId,
163             String fileName, double versionNumber)
164         throws SystemException {
165 
166         try {
167             _s3Service.deleteObject(
168                 _s3Bucket,
169                 getKey(companyId, repositoryId, fileName, versionNumber));
170         }
171         catch (S3ServiceException s3se) {
172             throw new SystemException(s3se);
173         }
174     }
175 
176     public InputStream getFileAsStream(
177             long companyId, long repositoryId, String fileName,
178             double versionNumber)
179         throws PortalException, SystemException {
180 
181         try {
182             if (versionNumber == 0) {
183                 versionNumber = getHeadVersionNumber(
184                     companyId, repositoryId, fileName);
185             }
186 
187             S3Object s3Object = _s3Service.getObject(
188                 _s3Bucket,
189                 getKey(companyId, repositoryId, fileName, versionNumber));
190 
191             return s3Object.getDataInputStream();
192         }
193         catch (S3ServiceException s3se) {
194             throw new SystemException(s3se);
195         }
196     }
197 
198     public String[] getFileNames(
199             long companyId, long repositoryId, String dirName)
200         throws SystemException {
201 
202         try {
203             List<String> list = new ArrayList<String>();
204 
205             S3Object[] s3Objects = _s3Service.listObjects(
206                 _s3Bucket, getKey(companyId, repositoryId, dirName), null);
207 
208             for (int i = 0; i < s3Objects.length; i++) {
209                 S3Object s3Object = s3Objects[i];
210 
211                 // Convert /${companyId}/${repositoryId}/${dirName}/${fileName}
212                 // /${versionNumber} to /${dirName}/${fileName}
213 
214                 String key = s3Object.getKey();
215 
216                 int x = key.indexOf(StringPool.SLASH);
217 
218                 x = key.indexOf(StringPool.SLASH, x + 1);
219 
220                 int y = key.lastIndexOf(StringPool.SLASH);
221 
222                 list.add(key.substring(x, y));
223             }
224 
225             return list.toArray(new String[list.size()]);
226         }
227         catch (S3ServiceException s3se) {
228             throw new SystemException(s3se);
229         }
230     }
231 
232     public long getFileSize(
233             long companyId, long repositoryId, String fileName)
234         throws PortalException, SystemException {
235 
236         try {
237             double versionNumber = getHeadVersionNumber(
238                 companyId, repositoryId, fileName);
239 
240             S3Object objectDetails = _s3Service.getObjectDetails(
241                 _s3Bucket,
242                 getKey(companyId, repositoryId, fileName, versionNumber));
243 
244             return objectDetails.getContentLength();
245         }
246         catch (S3ServiceException s3se) {
247             throw new SystemException(s3se);
248         }
249     }
250 
251     public boolean hasFile(
252             long companyId, long repositoryId, String fileName,
253             double versionNumber)
254         throws SystemException {
255 
256         try {
257             S3Object[] s3Objects = _s3Service.listObjects(
258                 _s3Bucket,
259                 getKey(companyId, repositoryId, fileName, versionNumber), null);
260 
261             if (s3Objects.length == 0) {
262                 return false;
263             }
264             else {
265                 return true;
266             }
267         }
268         catch (S3ServiceException s3se) {
269             throw new SystemException(s3se);
270         }
271     }
272 
273     public void move(String srcDir, String destDir) {
274     }
275 
276     public void reIndex(String[] ids) throws SearchException {
277         long companyId = GetterUtil.getLong(ids[0]);
278         String portletId = ids[1];
279         long groupId = GetterUtil.getLong(ids[2]);
280         long repositoryId = GetterUtil.getLong(ids[3]);
281 
282         try {
283             S3Object[] searchObjects = _s3Service.listObjects(
284                 _s3Bucket, getKey(companyId, repositoryId), null);
285 
286             Set<String> fileNameSet = new HashSet<String>();
287 
288             for (int i = 0; i < searchObjects.length; i++) {
289                 S3Object currentObject = searchObjects[i];
290 
291                 String fileName = getFileName(currentObject.getKey());
292 
293                 fileNameSet.add(fileName);
294             }
295 
296             Iterator<String> itr = fileNameSet.iterator();
297 
298             while (itr.hasNext()) {
299                 String fileName = itr.next();
300 
301                 try {
302                     Document doc = Indexer.getFileDocument(
303                         companyId, portletId, groupId, repositoryId, fileName);
304 
305                     SearchEngineUtil.updateDocument(
306                         companyId, doc.get(Field.UID), doc);
307                 }
308                 catch (Exception e) {
309                     _log.error("Reindexing " + fileName, e);
310                 }
311             }
312         }
313         catch (S3ServiceException s3se) {
314             throw new SearchException(s3se);
315         }
316     }
317 
318     public void updateFile(
319             long companyId, String portletId, long groupId, long repositoryId,
320             String fileName, double versionNumber, String sourceFileName,
321             String properties, String[] tagsEntries, InputStream is)
322         throws SystemException {
323 
324         try {
325             S3Object s3Object = new S3Object(
326                 _s3Bucket,
327                 getKey(companyId, repositoryId, fileName, versionNumber));
328 
329             s3Object.setDataInputStream(is);
330 
331             _s3Service.putObject(_s3Bucket, s3Object);
332 
333             Indexer.updateFile(
334                 companyId, portletId, groupId, repositoryId, fileName,
335                 properties, tagsEntries);
336         }
337         catch (S3ServiceException s3se) {
338             throw new SystemException(s3se);
339         }
340         catch (SearchException se) {
341             throw new SystemException(se);
342         }
343     }
344 
345     public void updateFile(
346             long companyId, String portletId, long groupId, long repositoryId,
347             long newRepositoryId, String fileName)
348         throws PortalException, SystemException {
349 
350         try {
351             S3Object[] s3Objects = _s3Service.listObjects(
352                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
353 
354             for (int i = 0; i < s3Objects.length; i++) {
355                 S3Object oldS3Object = s3Objects[i];
356 
357                 String oldKey = oldS3Object.getKey();
358 
359                 oldS3Object = _s3Service.getObject(_s3Bucket, oldKey);
360 
361                 File tempFile = new File(
362                     SystemProperties.get(SystemProperties.TMP_DIR) +
363                         File.separator + UUID.timeUUID());
364 
365                 InputStream is = null;
366 
367                 try {
368                     is = oldS3Object.getDataInputStream();
369 
370                     FileUtil.write(tempFile, is);
371                 }
372                 catch (Exception e) {
373                 }
374                 finally {
375                     ServletResponseUtil.cleanUp(is);
376                 }
377 
378                 is = new FileInputStream(tempFile);
379 
380                 String newPrefix = getKey(companyId, newRepositoryId);
381 
382                 int x = oldKey.indexOf(StringPool.SLASH);
383 
384                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
385 
386                 String newKey =
387                     newPrefix + oldKey.substring(x + 1, oldKey.length());
388 
389                 S3Object newS3Object = new S3Object(
390                     _s3Bucket, newKey);
391 
392                 newS3Object.setDataInputStream(is);
393 
394                 _s3Service.putObject(_s3Bucket, newS3Object);
395                 _s3Service.deleteObject(_s3Bucket, oldKey);
396 
397                 FileUtil.delete(tempFile);
398             }
399 
400             Indexer.deleteFile(
401                 companyId, portletId, repositoryId, fileName);
402 
403             Indexer.addFile(
404                 companyId, portletId, groupId, newRepositoryId, fileName);
405         }
406         catch (IOException ioe) {
407             throw new SystemException(ioe);
408         }
409         catch (S3ServiceException s3se) {
410             throw new SystemException(s3se);
411         }
412     }
413 
414     protected AWSCredentials getAWSCredentials() throws S3ServiceException {
415         if (Validator.isNull(_ACCESS_KEY) || Validator.isNull(_SECRET_KEY)) {
416             throw new S3ServiceException(
417                 "S3 access and secret keys are not set");
418         }
419         else {
420             return new AWSCredentials(_ACCESS_KEY, _SECRET_KEY);
421         }
422     }
423 
424     protected String getFileName(String key) {
425         int x = key.indexOf(StringPool.SLASH);
426 
427         x = key.indexOf(StringPool.SLASH, x + 1);
428 
429         int y = key.lastIndexOf(StringPool.SLASH);
430 
431         return key.substring(x + 1, y);
432     }
433 
434     protected double getHeadVersionNumber(
435             long companyId, long repositoryId, String fileName)
436         throws PortalException, S3ServiceException {
437 
438         S3Object[] s3Objects = _s3Service.listObjects(
439             _s3Bucket, getKey(companyId, repositoryId, fileName), null);
440 
441         String[] keys = new String[s3Objects.length];
442 
443         for (int i = 0; i < s3Objects.length; i++) {
444             S3Object s3Object = s3Objects[i];
445 
446             keys[i] = s3Object.getKey();
447         }
448 
449         if (keys.length > 0) {
450             Arrays.sort(keys);
451 
452             String headKey = keys[keys.length - 1];
453 
454             int x = headKey.lastIndexOf(StringPool.SLASH);
455 
456             return GetterUtil.getDouble(
457                 headKey.substring(x + 1, headKey.length()));
458         }
459         else {
460             throw new NoSuchFileException(fileName);
461         }
462     }
463 
464     protected String getKey(long companyId, long repositoryId) {
465         StringBuilder sb = new StringBuilder();
466 
467         sb.append(companyId);
468         sb.append(StringPool.SLASH);
469         sb.append(repositoryId);
470         sb.append(StringPool.SLASH);
471 
472         return sb.toString();
473     }
474 
475     protected String getKey(
476         long companyId, long repositoryId, String fileName) {
477 
478         StringBuilder sb = new StringBuilder();
479 
480         sb.append(companyId);
481         sb.append(StringPool.SLASH);
482         sb.append(repositoryId);
483         sb.append(StringPool.SLASH);
484         sb.append(fileName);
485         sb.append(StringPool.SLASH);
486 
487         return sb.toString();
488     }
489 
490     protected String getKey(
491         long companyId, long repositoryId, String fileName,
492         double versionNumber) {
493 
494         StringBuilder sb = new StringBuilder();
495 
496         sb.append(companyId);
497         sb.append(StringPool.SLASH);
498         sb.append(repositoryId);
499         sb.append(StringPool.SLASH);
500         sb.append(fileName);
501         sb.append(StringPool.SLASH);
502         sb.append(versionNumber);
503 
504         return sb.toString();
505     }
506 
507     protected S3Bucket getS3Bucket() throws S3ServiceException {
508         if (Validator.isNull(_BUCKET_NAME)) {
509             throw new S3ServiceException("S3 bucket name is not set");
510         }
511         else {
512             return getS3Service().createBucket(_BUCKET_NAME);
513         }
514     }
515 
516     protected S3Service getS3Service() throws S3ServiceException {
517         AWSCredentials credentials = getAWSCredentials();
518 
519         return new RestS3Service(credentials);
520     }
521 
522     private static final String _ACCESS_KEY = PropsUtil.get(
523         PropsKeys.DL_HOOK_S3_ACCESS_KEY);
524 
525     private static final String _SECRET_KEY = PropsUtil.get(
526         PropsKeys.DL_HOOK_S3_SECRET_KEY);
527 
528     private static final String _BUCKET_NAME = PropsUtil.get(
529         PropsKeys.DL_HOOK_S3_BUCKET_NAME);
530 
531     private static Log _log = LogFactory.getLog(S3Hook.class);
532 
533     private S3Bucket _s3Bucket;
534     private S3Service _s3Service;
535 
536 }