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  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  public class S3Hook extends BaseHook {
72  
73      public S3Hook() {
74          try {
75              _s3Service = getS3Service();
76              _s3Bucket = getS3Bucket();
77          }
78          catch (S3ServiceException s3se) {
79              _log.error(s3se.getMessage());
80          }
81      }
82  
83      public void addDirectory(
84          long companyId, long repositoryId, String dirName) {
85      }
86  
87      public void addFile(
88              long companyId, String portletId, long groupId, long repositoryId,
89              String fileName, long fileEntryId, String properties,
90              Date modifiedDate, String[] tagsCategories, String[] tagsEntries,
91              InputStream is)
92          throws SystemException {
93  
94          try {
95              S3Object s3Object = new S3Object(
96                  _s3Bucket,
97                  getKey(companyId, repositoryId, fileName, DEFAULT_VERSION));
98  
99              s3Object.setDataInputStream(is);
100 
101             _s3Service.putObject(_s3Bucket, s3Object);
102 
103             DLIndexerUtil.addFile(
104                 companyId, portletId, groupId, repositoryId, fileName,
105                 fileEntryId, properties, modifiedDate, tagsCategories,
106                 tagsEntries);
107         }
108         catch (S3ServiceException s3se) {
109             throw new SystemException(s3se);
110         }
111         catch (SearchException se) {
112             throw new SystemException(se);
113         }
114     }
115 
116     public void checkRoot(long companyId) {
117     }
118 
119     public void deleteDirectory(
120             long companyId, String portletId, long repositoryId, String dirName)
121         throws SystemException {
122 
123         try {
124             S3Object[] s3Objects = _s3Service.listObjects(
125                 _s3Bucket, getKey(companyId, repositoryId, dirName), null);
126 
127             for (int i = 0; i < s3Objects.length; i++) {
128                 S3Object s3Object = s3Objects[i];
129 
130                 _s3Service.deleteObject(_s3Bucket, s3Object.getKey());
131             }
132         }
133         catch (S3ServiceException s3se) {
134             throw new SystemException(s3se);
135         }
136     }
137 
138     public void deleteFile(
139             long companyId, String portletId, long repositoryId,
140             String fileName)
141         throws SystemException {
142 
143         try {
144             S3Object[] s3Objects = _s3Service.listObjects(
145                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
146 
147             for (int i = 0; i < s3Objects.length; i++) {
148                 S3Object s3Object = s3Objects[i];
149 
150                 _s3Service.deleteObject(_s3Bucket, s3Object.getKey());
151             }
152 
153             DLIndexerUtil.deleteFile(
154                 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 = DLIndexerUtil.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             DLIndexerUtil.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             DLIndexerUtil.deleteFile(
406                 companyId, portletId, repositoryId, fileName);
407 
408             DLIndexerUtil.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 }