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