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.StringPool;
37  import com.liferay.portal.kernel.util.Validator;
38  import com.liferay.portal.util.PropsKeys;
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, String properties, Date modifiedDate,
90              String[] tagsEntries, 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             Indexer.addFile(
103                 companyId, portletId, groupId, repositoryId, fileName,
104                 properties, modifiedDate, 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             String properties, Date modifiedDate, String[] tagsEntries,
322             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                 properties, modifiedDate, tagsEntries);
337         }
338         catch (S3ServiceException s3se) {
339             throw new SystemException(s3se);
340         }
341         catch (SearchException se) {
342             throw new SystemException(se);
343         }
344     }
345 
346     public void updateFile(
347             long companyId, String portletId, long groupId, long repositoryId,
348             long newRepositoryId, String fileName)
349         throws PortalException, SystemException {
350 
351         try {
352             S3Object[] s3Objects = _s3Service.listObjects(
353                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
354 
355             for (int i = 0; i < s3Objects.length; i++) {
356                 S3Object oldS3Object = s3Objects[i];
357 
358                 String oldKey = oldS3Object.getKey();
359 
360                 oldS3Object = _s3Service.getObject(_s3Bucket, oldKey);
361 
362                 File tempFile = new File(
363                     SystemProperties.get(SystemProperties.TMP_DIR) +
364                         File.separator + UUID.timeUUID());
365 
366                 InputStream is = null;
367 
368                 try {
369                     is = oldS3Object.getDataInputStream();
370 
371                     FileUtil.write(tempFile, is);
372                 }
373                 catch (Exception e) {
374                 }
375                 finally {
376                     ServletResponseUtil.cleanUp(is);
377                 }
378 
379                 is = new FileInputStream(tempFile);
380 
381                 String newPrefix = getKey(companyId, newRepositoryId);
382 
383                 int x = oldKey.indexOf(StringPool.SLASH);
384 
385                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
386 
387                 String newKey =
388                     newPrefix + oldKey.substring(x + 1, oldKey.length());
389 
390                 S3Object newS3Object = new S3Object(
391                     _s3Bucket, newKey);
392 
393                 newS3Object.setDataInputStream(is);
394 
395                 _s3Service.putObject(_s3Bucket, newS3Object);
396                 _s3Service.deleteObject(_s3Bucket, oldKey);
397 
398                 FileUtil.delete(tempFile);
399             }
400 
401             Indexer.deleteFile(
402                 companyId, portletId, repositoryId, fileName);
403 
404             Indexer.addFile(
405                 companyId, portletId, groupId, newRepositoryId, fileName);
406         }
407         catch (IOException ioe) {
408             throw new SystemException(ioe);
409         }
410         catch (S3ServiceException s3se) {
411             throw new SystemException(s3se);
412         }
413     }
414 
415     protected AWSCredentials getAWSCredentials() throws S3ServiceException {
416         if (Validator.isNull(_ACCESS_KEY) || Validator.isNull(_SECRET_KEY)) {
417             throw new S3ServiceException(
418                 "S3 access and secret keys are not set");
419         }
420         else {
421             return new AWSCredentials(_ACCESS_KEY, _SECRET_KEY);
422         }
423     }
424 
425     protected String getFileName(String key) {
426         int x = key.indexOf(StringPool.SLASH);
427 
428         x = key.indexOf(StringPool.SLASH, x + 1);
429 
430         int y = key.lastIndexOf(StringPool.SLASH);
431 
432         return key.substring(x + 1, y);
433     }
434 
435     protected double getHeadVersionNumber(
436             long companyId, long repositoryId, String fileName)
437         throws PortalException, S3ServiceException {
438 
439         S3Object[] s3Objects = _s3Service.listObjects(
440             _s3Bucket, getKey(companyId, repositoryId, fileName), null);
441 
442         String[] keys = new String[s3Objects.length];
443 
444         for (int i = 0; i < s3Objects.length; i++) {
445             S3Object s3Object = s3Objects[i];
446 
447             keys[i] = s3Object.getKey();
448         }
449 
450         if (keys.length > 0) {
451             Arrays.sort(keys);
452 
453             String headKey = keys[keys.length - 1];
454 
455             int x = headKey.lastIndexOf(StringPool.SLASH);
456 
457             return GetterUtil.getDouble(
458                 headKey.substring(x + 1, headKey.length()));
459         }
460         else {
461             throw new NoSuchFileException(fileName);
462         }
463     }
464 
465     protected String getKey(long companyId, long repositoryId) {
466         StringBuilder sb = new StringBuilder();
467 
468         sb.append(companyId);
469         sb.append(StringPool.SLASH);
470         sb.append(repositoryId);
471         sb.append(StringPool.SLASH);
472 
473         return sb.toString();
474     }
475 
476     protected String getKey(
477         long companyId, long repositoryId, String fileName) {
478 
479         StringBuilder sb = new StringBuilder();
480 
481         sb.append(companyId);
482         sb.append(StringPool.SLASH);
483         sb.append(repositoryId);
484         sb.append(StringPool.SLASH);
485         sb.append(fileName);
486         sb.append(StringPool.SLASH);
487 
488         return sb.toString();
489     }
490 
491     protected String getKey(
492         long companyId, long repositoryId, String fileName,
493         double versionNumber) {
494 
495         StringBuilder sb = new StringBuilder();
496 
497         sb.append(companyId);
498         sb.append(StringPool.SLASH);
499         sb.append(repositoryId);
500         sb.append(StringPool.SLASH);
501         sb.append(fileName);
502         sb.append(StringPool.SLASH);
503         sb.append(versionNumber);
504 
505         return sb.toString();
506     }
507 
508     protected S3Bucket getS3Bucket() throws S3ServiceException {
509         if (Validator.isNull(_BUCKET_NAME)) {
510             throw new S3ServiceException("S3 bucket name is not set");
511         }
512         else {
513             return getS3Service().createBucket(_BUCKET_NAME);
514         }
515     }
516 
517     protected S3Service getS3Service() throws S3ServiceException {
518         AWSCredentials credentials = getAWSCredentials();
519 
520         return new RestS3Service(credentials);
521     }
522 
523     private static final String _ACCESS_KEY = PropsUtil.get(
524         PropsKeys.DL_HOOK_S3_ACCESS_KEY);
525 
526     private static final String _SECRET_KEY = PropsUtil.get(
527         PropsKeys.DL_HOOK_S3_SECRET_KEY);
528 
529     private static final String _BUCKET_NAME = PropsUtil.get(
530         PropsKeys.DL_HOOK_S3_BUCKET_NAME);
531 
532     private static Log _log = LogFactoryUtil.getLog(S3Hook.class);
533 
534     private S3Bucket _s3Bucket;
535     private S3Service _s3Service;
536 
537 }