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