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