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