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                 try {
316                     Indexer indexer = IndexerRegistryUtil.getIndexer(
317                         FileModel.class);
318 
319                     FileModel fileModel = new FileModel();
320 
321                     fileModel.setCompanyId(companyId);
322                     fileModel.setFileName(fileName);
323                     fileModel.setGroupId(groupId);
324                     fileModel.setPortletId(portletId);
325                     fileModel.setRepositoryId(repositoryId);
326 
327                     Document document = indexer.getDocument(fileModel);
328 
329                     if (document == null) {
330                         continue;
331                     }
332 
333                     documents.add(document);
334                 }
335                 catch (Exception e) {
336                     _log.error("Reindexing " + fileName, e);
337                 }
338             }
339         }
340         catch (S3ServiceException s3se) {
341             throw new SearchException(s3se);
342         }
343 
344         SearchEngineUtil.updateDocuments(companyId, documents);
345     }
346 
347     public void updateFile(
348             long companyId, String portletId, long groupId, long repositoryId,
349             long newRepositoryId, String fileName, long fileEntryId)
350         throws PortalException, SystemException {
351 
352         try {
353             S3Object[] s3Objects = _s3Service.listObjects(
354                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
355 
356             for (int i = 0; i < s3Objects.length; i++) {
357                 S3Object oldS3Object = s3Objects[i];
358 
359                 String oldKey = oldS3Object.getKey();
360 
361                 oldS3Object = _s3Service.getObject(_s3Bucket, oldKey);
362 
363                 File tempFile = new File(
364                     SystemProperties.get(SystemProperties.TMP_DIR) +
365                         File.separator + PortalUUIDUtil.generate());
366 
367                 FileUtil.write(tempFile, oldS3Object.getDataInputStream());
368 
369                 InputStream is = new FileInputStream(tempFile);
370 
371                 String newPrefix = getKey(companyId, newRepositoryId);
372 
373                 int x = oldKey.indexOf(StringPool.SLASH);
374 
375                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
376 
377                 String newKey =
378                     newPrefix + oldKey.substring(x + 1, oldKey.length());
379 
380                 S3Object newS3Object = new S3Object(
381                     _s3Bucket, newKey);
382 
383                 newS3Object.setDataInputStream(is);
384 
385                 _s3Service.putObject(_s3Bucket, newS3Object);
386                 _s3Service.deleteObject(_s3Bucket, oldKey);
387 
388                 FileUtil.delete(tempFile);
389             }
390 
391             Indexer indexer = IndexerRegistryUtil.getIndexer(
392                 FileModel.class);
393 
394             FileModel fileModel = new FileModel();
395 
396             fileModel.setCompanyId(companyId);
397             fileModel.setFileName(fileName);
398             fileModel.setPortletId(portletId);
399             fileModel.setRepositoryId(repositoryId);
400 
401             indexer.delete(fileModel);
402 
403             fileModel.setRepositoryId(newRepositoryId);
404             fileModel.setGroupId(groupId);
405 
406             indexer.reindex(fileModel);
407         }
408         catch (IOException ioe) {
409             throw new SystemException(ioe);
410         }
411         catch (S3ServiceException s3se) {
412             throw new SystemException(s3se);
413         }
414     }
415 
416     public void updateFile(
417             long companyId, String portletId, long groupId, long repositoryId,
418             String fileName, String newFileName, boolean reindex)
419         throws PortalException, SystemException {
420 
421         try {
422             S3Object[] s3Objects = _s3Service.listObjects(
423                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
424 
425             for (int i = 0; i < s3Objects.length; i++) {
426                 S3Object oldS3Object = s3Objects[i];
427 
428                 String oldKey = oldS3Object.getKey();
429 
430                 oldS3Object = _s3Service.getObject(_s3Bucket, oldKey);
431 
432                 File tempFile = new File(
433                     SystemProperties.get(SystemProperties.TMP_DIR) +
434                         File.separator + PortalUUIDUtil.generate());
435 
436                 FileUtil.write(tempFile, oldS3Object.getDataInputStream());
437 
438                 InputStream is = new FileInputStream(tempFile);
439 
440                 String newPrefix = getKey(companyId, repositoryId, newFileName);
441 
442                 int x = oldKey.indexOf(StringPool.SLASH);
443 
444                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
445 
446                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
447 
448                 String newKey =
449                     newPrefix + oldKey.substring(x + 1, oldKey.length());
450 
451                 S3Object newS3Object = new S3Object(
452                     _s3Bucket, newKey);
453 
454                 newS3Object.setDataInputStream(is);
455 
456                 _s3Service.putObject(_s3Bucket, newS3Object);
457                 _s3Service.deleteObject(_s3Bucket, oldKey);
458 
459                 FileUtil.delete(tempFile);
460             }
461 
462             if (reindex) {
463                 Indexer indexer = IndexerRegistryUtil.getIndexer(
464                     FileModel.class);
465 
466                 FileModel fileModel = new FileModel();
467 
468                 fileModel.setCompanyId(companyId);
469                 fileModel.setFileName(fileName);
470                 fileModel.setPortletId(portletId);
471                 fileModel.setRepositoryId(repositoryId);
472 
473                 indexer.delete(fileModel);
474 
475                 fileModel.setFileName(newFileName);
476                 fileModel.setGroupId(groupId);
477 
478                 indexer.reindex(fileModel);
479             }
480         }
481         catch (IOException ioe) {
482             throw new SystemException(ioe);
483         }
484         catch (S3ServiceException s3se) {
485             throw new SystemException(s3se);
486         }
487     }
488 
489     public void updateFile(
490             long companyId, String portletId, long groupId, long repositoryId,
491             String fileName, String versionNumber, String sourceFileName,
492             long fileEntryId, String properties, Date modifiedDate,
493             ServiceContext serviceContext, InputStream is)
494         throws PortalException, SystemException {
495 
496         try {
497             S3Object s3Object = new S3Object(
498                 _s3Bucket,
499                 getKey(companyId, repositoryId, fileName, versionNumber));
500 
501             s3Object.setDataInputStream(is);
502 
503             _s3Service.putObject(_s3Bucket, s3Object);
504 
505             Indexer indexer = IndexerRegistryUtil.getIndexer(
506                 FileModel.class);
507 
508             FileModel fileModel = new FileModel();
509 
510             fileModel.setAssetCategoryIds(serviceContext.getAssetCategoryIds());
511             fileModel.setAssetTagNames(serviceContext.getAssetTagNames());
512             fileModel.setCompanyId(companyId);
513             fileModel.setFileEntryId(fileEntryId);
514             fileModel.setFileName(fileName);
515             fileModel.setGroupId(groupId);
516             fileModel.setModifiedDate(modifiedDate);
517             fileModel.setPortletId(portletId);
518             fileModel.setProperties(properties);
519             fileModel.setRepositoryId(repositoryId);
520 
521             indexer.reindex(fileModel);
522         }
523         catch (S3ServiceException s3se) {
524             throw new SystemException(s3se);
525         }
526     }
527 
528     protected AWSCredentials getAWSCredentials() throws S3ServiceException {
529         if (Validator.isNull(_ACCESS_KEY) || Validator.isNull(_SECRET_KEY)) {
530             throw new S3ServiceException(
531                 "S3 access and secret keys are not set");
532         }
533         else {
534             return new AWSCredentials(_ACCESS_KEY, _SECRET_KEY);
535         }
536     }
537 
538     protected String getFileName(String key) {
539         int x = key.indexOf(StringPool.SLASH);
540 
541         x = key.indexOf(StringPool.SLASH, x + 1);
542 
543         int y = key.lastIndexOf(StringPool.SLASH);
544 
545         return key.substring(x + 1, y);
546     }
547 
548     protected String getHeadVersionNumber(
549             long companyId, long repositoryId, String fileName)
550         throws PortalException, S3ServiceException {
551 
552         S3Object[] s3Objects = _s3Service.listObjects(
553             _s3Bucket, getKey(companyId, repositoryId, fileName), null);
554 
555         String[] keys = new String[s3Objects.length];
556 
557         for (int i = 0; i < s3Objects.length; i++) {
558             S3Object s3Object = s3Objects[i];
559 
560             keys[i] = s3Object.getKey();
561         }
562 
563         if (keys.length > 0) {
564             Arrays.sort(keys);
565 
566             String headKey = keys[keys.length - 1];
567 
568             int x = headKey.lastIndexOf(StringPool.SLASH);
569 
570             return headKey.substring(x + 1, headKey.length());
571         }
572         else {
573             throw new NoSuchFileException(fileName);
574         }
575     }
576 
577     protected String getKey(long companyId, long repositoryId) {
578         StringBundler sb = new StringBundler(4);
579 
580         sb.append(companyId);
581         sb.append(StringPool.SLASH);
582         sb.append(repositoryId);
583         sb.append(StringPool.SLASH);
584 
585         return sb.toString();
586     }
587 
588     protected String getKey(
589         long companyId, long repositoryId, String fileName) {
590 
591         StringBundler sb = new StringBundler(6);
592 
593         sb.append(companyId);
594         sb.append(StringPool.SLASH);
595         sb.append(repositoryId);
596         sb.append(StringPool.SLASH);
597         sb.append(fileName);
598         sb.append(StringPool.SLASH);
599 
600         return sb.toString();
601     }
602 
603     protected String getKey(
604         long companyId, long repositoryId, String fileName,
605         String versionNumber) {
606 
607         StringBundler sb = new StringBundler(7);
608 
609         sb.append(companyId);
610         sb.append(StringPool.SLASH);
611         sb.append(repositoryId);
612         sb.append(StringPool.SLASH);
613         sb.append(fileName);
614         sb.append(StringPool.SLASH);
615         sb.append(versionNumber);
616 
617         return sb.toString();
618     }
619 
620     protected S3Bucket getS3Bucket() throws S3ServiceException {
621         if (Validator.isNull(_BUCKET_NAME)) {
622             throw new S3ServiceException("S3 bucket name is not set");
623         }
624         else {
625             return getS3Service().createBucket(_BUCKET_NAME);
626         }
627     }
628 
629     protected S3Service getS3Service() throws S3ServiceException {
630         AWSCredentials credentials = getAWSCredentials();
631 
632         return new RestS3Service(credentials);
633     }
634 
635     private static final String _ACCESS_KEY = PropsUtil.get(
636         PropsKeys.DL_HOOK_S3_ACCESS_KEY);
637 
638     private static final String _BUCKET_NAME = PropsUtil.get(
639         PropsKeys.DL_HOOK_S3_BUCKET_NAME);
640 
641     private static final String _SECRET_KEY = PropsUtil.get(
642         PropsKeys.DL_HOOK_S3_SECRET_KEY);
643 
644     private static Log _log = LogFactoryUtil.getLog(S3Hook.class);
645 
646     private S3Bucket _s3Bucket;
647     private S3Service _s3Service;
648 
649 }