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