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