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