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