1   /**
2    * Copyright (c) 2000-2009 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   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17   * SOFTWARE.
18   */
19  
20  package com.liferay.documentlibrary.util;
21  
22  import com.liferay.documentlibrary.NoSuchFileException;
23  import com.liferay.portal.PortalException;
24  import com.liferay.portal.SystemException;
25  import com.liferay.portal.kernel.log.Log;
26  import com.liferay.portal.kernel.log.LogFactoryUtil;
27  import com.liferay.portal.kernel.search.Document;
28  import com.liferay.portal.kernel.search.Field;
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.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.apache.commons.id.uuid.UUID;
54  
55  import org.jets3t.service.S3Service;
56  import org.jets3t.service.S3ServiceException;
57  import org.jets3t.service.impl.rest.httpclient.RestS3Service;
58  import org.jets3t.service.model.S3Bucket;
59  import org.jets3t.service.model.S3Object;
60  import org.jets3t.service.security.AWSCredentials;
61  
62  /**
63   * <a href="S3Hook.java.html"><b><i>View Source</i></b></a>
64   *
65   * @author Brian Wing Shun Chan
66   * @author Sten Martinez
67   *
68   */
69  public class S3Hook extends BaseHook {
70  
71      public S3Hook() {
72          try {
73              _s3Service = getS3Service();
74              _s3Bucket = getS3Bucket();
75          }
76          catch (S3ServiceException s3se) {
77              _log.error(s3se.getMessage());
78          }
79      }
80  
81      public void addDirectory(
82          long companyId, long repositoryId, String dirName) {
83      }
84  
85      public void addFile(
86              long companyId, String portletId, long groupId, long repositoryId,
87              String fileName, long fileEntryId, String properties,
88              Date modifiedDate, String[] tagsCategories, 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                 fileEntryId, properties, modifiedDate, tagsCategories,
104                 tagsEntries);
105         }
106         catch (S3ServiceException s3se) {
107             throw new SystemException(s3se);
108         }
109         catch (SearchException se) {
110             throw new SystemException(se);
111         }
112     }
113 
114     public void checkRoot(long companyId) {
115     }
116 
117     public void deleteDirectory(
118             long companyId, String portletId, long repositoryId, String dirName)
119         throws SystemException {
120 
121         try {
122             S3Object[] s3Objects = _s3Service.listObjects(
123                 _s3Bucket, getKey(companyId, repositoryId, dirName), null);
124 
125             for (int i = 0; i < s3Objects.length; i++) {
126                 S3Object s3Object = s3Objects[i];
127 
128                 _s3Service.deleteObject(_s3Bucket, s3Object.getKey());
129             }
130         }
131         catch (S3ServiceException s3se) {
132             throw new SystemException(s3se);
133         }
134     }
135 
136     public void deleteFile(
137             long companyId, String portletId, long repositoryId,
138             String fileName)
139         throws SystemException {
140 
141         try {
142             S3Object[] s3Objects = _s3Service.listObjects(
143                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
144 
145             for (int i = 0; i < s3Objects.length; i++) {
146                 S3Object s3Object = s3Objects[i];
147 
148                 _s3Service.deleteObject(_s3Bucket, s3Object.getKey());
149             }
150 
151             Indexer.deleteFile(companyId, portletId, repositoryId, fileName);
152         }
153         catch (S3ServiceException s3se) {
154             throw new SystemException(s3se);
155         }
156         catch (SearchException se) {
157             throw new SystemException(se);
158         }
159     }
160 
161     public void deleteFile(
162             long companyId, String portletId, long repositoryId,
163             String fileName, double versionNumber)
164         throws SystemException {
165 
166         try {
167             _s3Service.deleteObject(
168                 _s3Bucket,
169                 getKey(companyId, repositoryId, fileName, versionNumber));
170         }
171         catch (S3ServiceException s3se) {
172             throw new SystemException(s3se);
173         }
174     }
175 
176     public InputStream getFileAsStream(
177             long companyId, long repositoryId, String fileName,
178             double versionNumber)
179         throws PortalException, SystemException {
180 
181         try {
182             if (versionNumber == 0) {
183                 versionNumber = getHeadVersionNumber(
184                     companyId, repositoryId, fileName);
185             }
186 
187             S3Object s3Object = _s3Service.getObject(
188                 _s3Bucket,
189                 getKey(companyId, repositoryId, fileName, versionNumber));
190 
191             return s3Object.getDataInputStream();
192         }
193         catch (S3ServiceException s3se) {
194             throw new SystemException(s3se);
195         }
196     }
197 
198     public String[] getFileNames(
199             long companyId, long repositoryId, String dirName)
200         throws SystemException {
201 
202         try {
203             List<String> list = new ArrayList<String>();
204 
205             S3Object[] s3Objects = _s3Service.listObjects(
206                 _s3Bucket, getKey(companyId, repositoryId, dirName), null);
207 
208             for (int i = 0; i < s3Objects.length; i++) {
209                 S3Object s3Object = s3Objects[i];
210 
211                 // Convert /${companyId}/${repositoryId}/${dirName}/${fileName}
212                 // /${versionNumber} to /${dirName}/${fileName}
213 
214                 String key = s3Object.getKey();
215 
216                 int x = key.indexOf(StringPool.SLASH);
217 
218                 x = key.indexOf(StringPool.SLASH, x + 1);
219 
220                 int y = key.lastIndexOf(StringPool.SLASH);
221 
222                 list.add(key.substring(x, y));
223             }
224 
225             return list.toArray(new String[list.size()]);
226         }
227         catch (S3ServiceException s3se) {
228             throw new SystemException(s3se);
229         }
230     }
231 
232     public long getFileSize(
233             long companyId, long repositoryId, String fileName)
234         throws PortalException, SystemException {
235 
236         try {
237             double versionNumber = getHeadVersionNumber(
238                 companyId, repositoryId, fileName);
239 
240             S3Object objectDetails = _s3Service.getObjectDetails(
241                 _s3Bucket,
242                 getKey(companyId, repositoryId, fileName, versionNumber));
243 
244             return objectDetails.getContentLength();
245         }
246         catch (S3ServiceException s3se) {
247             throw new SystemException(s3se);
248         }
249     }
250 
251     public boolean hasFile(
252             long companyId, long repositoryId, String fileName,
253             double versionNumber)
254         throws SystemException {
255 
256         try {
257             S3Object[] s3Objects = _s3Service.listObjects(
258                 _s3Bucket,
259                 getKey(companyId, repositoryId, fileName, versionNumber), null);
260 
261             if (s3Objects.length == 0) {
262                 return false;
263             }
264             else {
265                 return true;
266             }
267         }
268         catch (S3ServiceException s3se) {
269             throw new SystemException(s3se);
270         }
271     }
272 
273     public void move(String srcDir, String destDir) {
274     }
275 
276     public void reIndex(String[] ids) throws SearchException {
277         long companyId = GetterUtil.getLong(ids[0]);
278         String portletId = ids[1];
279         long groupId = GetterUtil.getLong(ids[2]);
280         long repositoryId = GetterUtil.getLong(ids[3]);
281 
282         try {
283             S3Object[] searchObjects = _s3Service.listObjects(
284                 _s3Bucket, getKey(companyId, repositoryId), null);
285 
286             Set<String> fileNameSet = new HashSet<String>();
287 
288             for (int i = 0; i < searchObjects.length; i++) {
289                 S3Object currentObject = searchObjects[i];
290 
291                 String fileName = getFileName(currentObject.getKey());
292 
293                 fileNameSet.add(fileName);
294             }
295 
296             Iterator<String> itr = fileNameSet.iterator();
297 
298             while (itr.hasNext()) {
299                 String fileName = itr.next();
300 
301                 try {
302                     Document doc = Indexer.getFileDocument(
303                         companyId, portletId, groupId, repositoryId, fileName);
304 
305                     SearchEngineUtil.updateDocument(
306                         companyId, doc.get(Field.UID), doc);
307                 }
308                 catch (Exception e) {
309                     _log.error("Reindexing " + fileName, e);
310                 }
311             }
312         }
313         catch (S3ServiceException s3se) {
314             throw new SearchException(s3se);
315         }
316     }
317 
318     public void updateFile(
319             long companyId, String portletId, long groupId, long repositoryId,
320             String fileName, double versionNumber, String sourceFileName,
321             long fileEntryId, String properties, Date modifiedDate,
322             String[] tagsCategories, String[] tagsEntries, InputStream is)
323         throws SystemException {
324 
325         try {
326             S3Object s3Object = new S3Object(
327                 _s3Bucket,
328                 getKey(companyId, repositoryId, fileName, versionNumber));
329 
330             s3Object.setDataInputStream(is);
331 
332             _s3Service.putObject(_s3Bucket, s3Object);
333 
334             Indexer.updateFile(
335                 companyId, portletId, groupId, repositoryId, fileName,
336                 fileEntryId, properties, modifiedDate, tagsCategories,
337                 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 }