1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.documentlibrary.util;
16  
17  import com.liferay.documentlibrary.NoSuchFileException;
18  import com.liferay.documentlibrary.model.FileModel;
19  import com.liferay.portal.kernel.exception.PortalException;
20  import com.liferay.portal.kernel.exception.SystemException;
21  import com.liferay.portal.kernel.log.Log;
22  import com.liferay.portal.kernel.log.LogFactoryUtil;
23  import com.liferay.portal.kernel.search.Document;
24  import com.liferay.portal.kernel.search.Indexer;
25  import com.liferay.portal.kernel.search.IndexerRegistryUtil;
26  import com.liferay.portal.kernel.search.SearchEngineUtil;
27  import com.liferay.portal.kernel.search.SearchException;
28  import com.liferay.portal.kernel.util.FileUtil;
29  import com.liferay.portal.kernel.util.GetterUtil;
30  import com.liferay.portal.kernel.util.PropsKeys;
31  import com.liferay.portal.kernel.util.StringBundler;
32  import com.liferay.portal.kernel.util.StringPool;
33  import com.liferay.portal.kernel.util.Validator;
34  import com.liferay.portal.kernel.uuid.PortalUUIDUtil;
35  import com.liferay.portal.service.ServiceContext;
36  import com.liferay.portal.util.PropsUtil;
37  import com.liferay.util.SystemProperties;
38  
39  import java.io.File;
40  import java.io.FileInputStream;
41  import java.io.IOException;
42  import java.io.InputStream;
43  
44  import java.util.ArrayList;
45  import java.util.Arrays;
46  import java.util.Collection;
47  import java.util.Date;
48  import java.util.HashSet;
49  import java.util.Iterator;
50  import java.util.List;
51  import java.util.Set;
52  
53  import org.jets3t.service.S3Service;
54  import org.jets3t.service.S3ServiceException;
55  import org.jets3t.service.impl.rest.httpclient.RestS3Service;
56  import org.jets3t.service.model.S3Bucket;
57  import org.jets3t.service.model.S3Object;
58  import org.jets3t.service.security.AWSCredentials;
59  
60  /**
61   * <a href="S3Hook.java.html"><b><i>View Source</i></b></a>
62   *
63   * @author Brian Wing Shun Chan
64   * @author Sten Martinez
65   */
66  public class S3Hook extends BaseHook {
67  
68      public S3Hook() {
69          try {
70              _s3Service = getS3Service();
71              _s3Bucket = getS3Bucket();
72          }
73          catch (S3ServiceException s3se) {
74              _log.error(s3se.getMessage());
75          }
76      }
77  
78      public void addDirectory(
79          long companyId, long repositoryId, String dirName) {
80      }
81  
82      public void addFile(
83              long companyId, String portletId, long groupId, long repositoryId,
84              String fileName, long fileEntryId, String properties,
85              Date modifiedDate, ServiceContext serviceContext, InputStream is)
86          throws PortalException, SystemException {
87  
88          try {
89              S3Object s3Object = new S3Object(
90                  _s3Bucket,
91                  getKey(companyId, repositoryId, fileName, DEFAULT_VERSION));
92  
93              s3Object.setDataInputStream(is);
94  
95              _s3Service.putObject(_s3Bucket, s3Object);
96  
97              Indexer indexer = IndexerRegistryUtil.getIndexer(
98                  FileModel.class);
99  
100             FileModel fileModel = new FileModel();
101 
102             fileModel.setAssetCategoryIds(serviceContext.getAssetCategoryIds());
103             fileModel.setAssetTagNames(serviceContext.getAssetTagNames());
104             fileModel.setCompanyId(companyId);
105             fileModel.setFileEntryId(fileEntryId);
106             fileModel.setFileName(fileName);
107             fileModel.setGroupId(groupId);
108             fileModel.setModifiedDate(modifiedDate);
109             fileModel.setPortletId(portletId);
110             fileModel.setProperties(properties);
111             fileModel.setRepositoryId(repositoryId);
112 
113             indexer.reindex(fileModel);
114         }
115         catch (S3ServiceException s3se) {
116             throw new SystemException(s3se);
117         }
118     }
119 
120     public void checkRoot(long companyId) {
121     }
122 
123     public void deleteDirectory(
124             long companyId, String portletId, long repositoryId, String dirName)
125         throws SystemException {
126 
127         try {
128             S3Object[] s3Objects = _s3Service.listObjects(
129                 _s3Bucket, getKey(companyId, repositoryId, dirName), null);
130 
131             for (int i = 0; i < s3Objects.length; i++) {
132                 S3Object s3Object = s3Objects[i];
133 
134                 _s3Service.deleteObject(_s3Bucket, s3Object.getKey());
135             }
136         }
137         catch (S3ServiceException s3se) {
138             throw new SystemException(s3se);
139         }
140     }
141 
142     public void deleteFile(
143             long companyId, String portletId, long repositoryId,
144             String fileName)
145         throws PortalException, SystemException {
146 
147         try {
148             S3Object[] s3Objects = _s3Service.listObjects(
149                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
150 
151             for (int i = 0; i < s3Objects.length; i++) {
152                 S3Object s3Object = s3Objects[i];
153 
154                 _s3Service.deleteObject(_s3Bucket, s3Object.getKey());
155             }
156 
157             FileModel fileModel = new FileModel();
158 
159             fileModel.setCompanyId(companyId);
160             fileModel.setFileName(fileName);
161             fileModel.setPortletId(portletId);
162             fileModel.setRepositoryId(repositoryId);
163 
164             Indexer indexer = IndexerRegistryUtil.getIndexer(FileModel.class);
165 
166             indexer.delete(fileModel);
167         }
168         catch (S3ServiceException s3se) {
169             throw new SystemException(s3se);
170         }
171     }
172 
173     public void deleteFile(
174             long companyId, String portletId, long repositoryId,
175             String fileName, String versionNumber)
176         throws SystemException {
177 
178         try {
179             _s3Service.deleteObject(
180                 _s3Bucket,
181                 getKey(companyId, repositoryId, fileName, versionNumber));
182         }
183         catch (S3ServiceException s3se) {
184             throw new SystemException(s3se);
185         }
186     }
187 
188     public InputStream getFileAsStream(
189             long companyId, long repositoryId, String fileName,
190             String versionNumber)
191         throws PortalException, SystemException {
192 
193         try {
194             if (Validator.isNull(versionNumber)) {
195                 versionNumber = getHeadVersionNumber(
196                     companyId, repositoryId, fileName);
197             }
198 
199             S3Object s3Object = _s3Service.getObject(
200                 _s3Bucket,
201                 getKey(companyId, repositoryId, fileName, versionNumber));
202 
203             return s3Object.getDataInputStream();
204         }
205         catch (S3ServiceException s3se) {
206             throw new SystemException(s3se);
207         }
208     }
209 
210     public String[] getFileNames(
211             long companyId, long repositoryId, String dirName)
212         throws SystemException {
213 
214         try {
215             List<String> list = new ArrayList<String>();
216 
217             S3Object[] s3Objects = _s3Service.listObjects(
218                 _s3Bucket, getKey(companyId, repositoryId, dirName), null);
219 
220             for (int i = 0; i < s3Objects.length; i++) {
221                 S3Object s3Object = s3Objects[i];
222 
223                 // Convert /${companyId}/${repositoryId}/${dirName}/${fileName}
224                 // /${versionNumber} to /${dirName}/${fileName}
225 
226                 String key = s3Object.getKey();
227 
228                 int x = key.indexOf(StringPool.SLASH);
229 
230                 x = key.indexOf(StringPool.SLASH, x + 1);
231 
232                 int y = key.lastIndexOf(StringPool.SLASH);
233 
234                 list.add(key.substring(x, y));
235             }
236 
237             return list.toArray(new String[list.size()]);
238         }
239         catch (S3ServiceException s3se) {
240             throw new SystemException(s3se);
241         }
242     }
243 
244     public long getFileSize(
245             long companyId, long repositoryId, String fileName)
246         throws PortalException, SystemException {
247 
248         try {
249             String versionNumber = getHeadVersionNumber(
250                 companyId, repositoryId, fileName);
251 
252             S3Object objectDetails = _s3Service.getObjectDetails(
253                 _s3Bucket,
254                 getKey(companyId, repositoryId, fileName, versionNumber));
255 
256             return objectDetails.getContentLength();
257         }
258         catch (S3ServiceException s3se) {
259             throw new SystemException(s3se);
260         }
261     }
262 
263     public boolean hasFile(
264             long companyId, long repositoryId, String fileName,
265             String versionNumber)
266         throws SystemException {
267 
268         try {
269             S3Object[] s3Objects = _s3Service.listObjects(
270                 _s3Bucket,
271                 getKey(companyId, repositoryId, fileName, versionNumber), null);
272 
273             if (s3Objects.length == 0) {
274                 return false;
275             }
276             else {
277                 return true;
278             }
279         }
280         catch (S3ServiceException s3se) {
281             throw new SystemException(s3se);
282         }
283     }
284 
285     public void move(String srcDir, String destDir) {
286     }
287 
288     public void reindex(String[] ids) throws SearchException {
289         long companyId = GetterUtil.getLong(ids[0]);
290         String portletId = ids[1];
291         long groupId = GetterUtil.getLong(ids[2]);
292         long repositoryId = GetterUtil.getLong(ids[3]);
293 
294         Collection<Document> documents = new ArrayList<Document>();
295 
296         try {
297             S3Object[] searchObjects = _s3Service.listObjects(
298                 _s3Bucket, getKey(companyId, repositoryId), null);
299 
300             Set<String> fileNameSet = new HashSet<String>();
301 
302             for (int i = 0; i < searchObjects.length; i++) {
303                 S3Object currentObject = searchObjects[i];
304 
305                 String fileName = getFileName(currentObject.getKey());
306 
307                 fileNameSet.add(fileName);
308             }
309 
310             Iterator<String> itr = fileNameSet.iterator();
311 
312             while (itr.hasNext()) {
313                 String fileName = itr.next();
314 
315                 Indexer indexer = IndexerRegistryUtil.getIndexer(
316                     FileModel.class);
317 
318                 FileModel fileModel = new FileModel();
319 
320                 fileModel.setCompanyId(companyId);
321                 fileModel.setFileName(fileName);
322                 fileModel.setGroupId(groupId);
323                 fileModel.setPortletId(portletId);
324                 fileModel.setRepositoryId(repositoryId);
325 
326                 Document document = indexer.getDocument(fileModel);
327 
328                 if (document == null) {
329                     continue;
330                 }
331 
332                 documents.add(document);
333             }
334         }
335         catch (S3ServiceException s3se) {
336             throw new SearchException(s3se);
337         }
338 
339         SearchEngineUtil.updateDocuments(companyId, documents);
340     }
341 
342     public void updateFile(
343             long companyId, String portletId, long groupId, long repositoryId,
344             long newRepositoryId, String fileName, long fileEntryId)
345         throws PortalException, SystemException {
346 
347         try {
348             S3Object[] s3Objects = _s3Service.listObjects(
349                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
350 
351             for (int i = 0; i < s3Objects.length; i++) {
352                 S3Object oldS3Object = s3Objects[i];
353 
354                 String oldKey = oldS3Object.getKey();
355 
356                 oldS3Object = _s3Service.getObject(_s3Bucket, oldKey);
357 
358                 File tempFile = new File(
359                     SystemProperties.get(SystemProperties.TMP_DIR) +
360                         File.separator + PortalUUIDUtil.generate());
361 
362                 FileUtil.write(tempFile, oldS3Object.getDataInputStream());
363 
364                 InputStream is = new FileInputStream(tempFile);
365 
366                 String newPrefix = getKey(companyId, newRepositoryId);
367 
368                 int x = oldKey.indexOf(StringPool.SLASH);
369 
370                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
371 
372                 String newKey =
373                     newPrefix + oldKey.substring(x + 1, oldKey.length());
374 
375                 S3Object newS3Object = new S3Object(
376                     _s3Bucket, newKey);
377 
378                 newS3Object.setDataInputStream(is);
379 
380                 _s3Service.putObject(_s3Bucket, newS3Object);
381                 _s3Service.deleteObject(_s3Bucket, oldKey);
382 
383                 FileUtil.delete(tempFile);
384             }
385 
386             Indexer indexer = IndexerRegistryUtil.getIndexer(
387                 FileModel.class);
388 
389             FileModel fileModel = new FileModel();
390 
391             fileModel.setCompanyId(companyId);
392             fileModel.setFileName(fileName);
393             fileModel.setPortletId(portletId);
394             fileModel.setRepositoryId(repositoryId);
395 
396             indexer.delete(fileModel);
397 
398             fileModel.setRepositoryId(newRepositoryId);
399             fileModel.setGroupId(groupId);
400 
401             indexer.reindex(fileModel);
402         }
403         catch (IOException ioe) {
404             throw new SystemException(ioe);
405         }
406         catch (S3ServiceException s3se) {
407             throw new SystemException(s3se);
408         }
409     }
410 
411     public void updateFile(
412             long companyId, String portletId, long groupId, long repositoryId,
413             String fileName, String newFileName, boolean reindex)
414         throws PortalException, SystemException {
415 
416         try {
417             S3Object[] s3Objects = _s3Service.listObjects(
418                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
419 
420             for (int i = 0; i < s3Objects.length; i++) {
421                 S3Object oldS3Object = s3Objects[i];
422 
423                 String oldKey = oldS3Object.getKey();
424 
425                 oldS3Object = _s3Service.getObject(_s3Bucket, oldKey);
426 
427                 File tempFile = new File(
428                     SystemProperties.get(SystemProperties.TMP_DIR) +
429                         File.separator + PortalUUIDUtil.generate());
430 
431                 FileUtil.write(tempFile, oldS3Object.getDataInputStream());
432 
433                 InputStream is = new FileInputStream(tempFile);
434 
435                 String newPrefix = getKey(companyId, repositoryId, newFileName);
436 
437                 int x = oldKey.indexOf(StringPool.SLASH);
438 
439                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
440 
441                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
442 
443                 String newKey =
444                     newPrefix + oldKey.substring(x + 1, oldKey.length());
445 
446                 S3Object newS3Object = new S3Object(
447                     _s3Bucket, newKey);
448 
449                 newS3Object.setDataInputStream(is);
450 
451                 _s3Service.putObject(_s3Bucket, newS3Object);
452                 _s3Service.deleteObject(_s3Bucket, oldKey);
453 
454                 FileUtil.delete(tempFile);
455             }
456 
457             if (reindex) {
458                 Indexer indexer = IndexerRegistryUtil.getIndexer(
459                     FileModel.class);
460 
461                 FileModel fileModel = new FileModel();
462 
463                 fileModel.setCompanyId(companyId);
464                 fileModel.setFileName(fileName);
465                 fileModel.setPortletId(portletId);
466                 fileModel.setRepositoryId(repositoryId);
467 
468                 indexer.delete(fileModel);
469 
470                 fileModel.setFileName(newFileName);
471                 fileModel.setGroupId(groupId);
472 
473                 indexer.reindex(fileModel);
474             }
475         }
476         catch (IOException ioe) {
477             throw new SystemException(ioe);
478         }
479         catch (S3ServiceException s3se) {
480             throw new SystemException(s3se);
481         }
482     }
483 
484     public void updateFile(
485             long companyId, String portletId, long groupId, long repositoryId,
486             String fileName, String versionNumber, String sourceFileName,
487             long fileEntryId, String properties, Date modifiedDate,
488             ServiceContext serviceContext, InputStream is)
489         throws PortalException, SystemException {
490 
491         try {
492             S3Object s3Object = new S3Object(
493                 _s3Bucket,
494                 getKey(companyId, repositoryId, fileName, versionNumber));
495 
496             s3Object.setDataInputStream(is);
497 
498             _s3Service.putObject(_s3Bucket, s3Object);
499 
500             Indexer indexer = IndexerRegistryUtil.getIndexer(
501                 FileModel.class);
502 
503             FileModel fileModel = new FileModel();
504 
505             fileModel.setAssetCategoryIds(serviceContext.getAssetCategoryIds());
506             fileModel.setAssetTagNames(serviceContext.getAssetTagNames());
507             fileModel.setCompanyId(companyId);
508             fileModel.setFileEntryId(fileEntryId);
509             fileModel.setFileName(fileName);
510             fileModel.setGroupId(groupId);
511             fileModel.setModifiedDate(modifiedDate);
512             fileModel.setPortletId(portletId);
513             fileModel.setProperties(properties);
514             fileModel.setRepositoryId(repositoryId);
515 
516             indexer.reindex(fileModel);
517         }
518         catch (S3ServiceException s3se) {
519             throw new SystemException(s3se);
520         }
521     }
522 
523     protected AWSCredentials getAWSCredentials() throws S3ServiceException {
524         if (Validator.isNull(_ACCESS_KEY) || Validator.isNull(_SECRET_KEY)) {
525             throw new S3ServiceException(
526                 "S3 access and secret keys are not set");
527         }
528         else {
529             return new AWSCredentials(_ACCESS_KEY, _SECRET_KEY);
530         }
531     }
532 
533     protected String getFileName(String key) {
534         int x = key.indexOf(StringPool.SLASH);
535 
536         x = key.indexOf(StringPool.SLASH, x + 1);
537 
538         int y = key.lastIndexOf(StringPool.SLASH);
539 
540         return key.substring(x + 1, y);
541     }
542 
543     protected String getHeadVersionNumber(
544             long companyId, long repositoryId, String fileName)
545         throws PortalException, S3ServiceException {
546 
547         S3Object[] s3Objects = _s3Service.listObjects(
548             _s3Bucket, getKey(companyId, repositoryId, fileName), null);
549 
550         String[] keys = new String[s3Objects.length];
551 
552         for (int i = 0; i < s3Objects.length; i++) {
553             S3Object s3Object = s3Objects[i];
554 
555             keys[i] = s3Object.getKey();
556         }
557 
558         if (keys.length > 0) {
559             Arrays.sort(keys);
560 
561             String headKey = keys[keys.length - 1];
562 
563             int x = headKey.lastIndexOf(StringPool.SLASH);
564 
565             return headKey.substring(x + 1, headKey.length());
566         }
567         else {
568             throw new NoSuchFileException(fileName);
569         }
570     }
571 
572     protected String getKey(long companyId, long repositoryId) {
573         StringBundler sb = new StringBundler(4);
574 
575         sb.append(companyId);
576         sb.append(StringPool.SLASH);
577         sb.append(repositoryId);
578         sb.append(StringPool.SLASH);
579 
580         return sb.toString();
581     }
582 
583     protected String getKey(
584         long companyId, long repositoryId, String fileName) {
585 
586         StringBundler sb = new StringBundler(6);
587 
588         sb.append(companyId);
589         sb.append(StringPool.SLASH);
590         sb.append(repositoryId);
591         sb.append(StringPool.SLASH);
592         sb.append(fileName);
593         sb.append(StringPool.SLASH);
594 
595         return sb.toString();
596     }
597 
598     protected String getKey(
599         long companyId, long repositoryId, String fileName,
600         String versionNumber) {
601 
602         StringBundler sb = new StringBundler(7);
603 
604         sb.append(companyId);
605         sb.append(StringPool.SLASH);
606         sb.append(repositoryId);
607         sb.append(StringPool.SLASH);
608         sb.append(fileName);
609         sb.append(StringPool.SLASH);
610         sb.append(versionNumber);
611 
612         return sb.toString();
613     }
614 
615     protected S3Bucket getS3Bucket() throws S3ServiceException {
616         if (Validator.isNull(_BUCKET_NAME)) {
617             throw new S3ServiceException("S3 bucket name is not set");
618         }
619         else {
620             return getS3Service().createBucket(_BUCKET_NAME);
621         }
622     }
623 
624     protected S3Service getS3Service() throws S3ServiceException {
625         AWSCredentials credentials = getAWSCredentials();
626 
627         return new RestS3Service(credentials);
628     }
629 
630     private static final String _ACCESS_KEY = PropsUtil.get(
631         PropsKeys.DL_HOOK_S3_ACCESS_KEY);
632 
633     private static final String _BUCKET_NAME = PropsUtil.get(
634         PropsKeys.DL_HOOK_S3_BUCKET_NAME);
635 
636     private static final String _SECRET_KEY = PropsUtil.get(
637         PropsKeys.DL_HOOK_S3_SECRET_KEY);
638 
639     private static Log _log = LogFactoryUtil.getLog(S3Hook.class);
640 
641     private S3Bucket _s3Bucket;
642     private S3Service _s3Service;
643 
644 }