1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions 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.search.SearchException;
29  import com.liferay.portal.kernel.util.GetterUtil;
30  import com.liferay.portal.kernel.util.StringMaker;
31  import com.liferay.portal.kernel.util.StringPool;
32  import com.liferay.portal.kernel.util.Validator;
33  import com.liferay.portal.lucene.LuceneUtil;
34  import com.liferay.portal.util.PropsUtil;
35  import com.liferay.util.FileUtil;
36  import com.liferay.util.SystemProperties;
37  import com.liferay.util.servlet.ServletResponseUtil;
38  
39  import java.io.File;
40  import java.io.FileInputStream;
41  import java.io.IOException;
42  import java.io.InputStream;
43  
44  import java.util.ArrayList;
45  import java.util.Arrays;
46  import java.util.HashSet;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Set;
50  
51  import org.apache.commons.id.uuid.UUID;
52  import org.apache.commons.logging.Log;
53  import org.apache.commons.logging.LogFactory;
54  import org.apache.lucene.document.Document;
55  import org.apache.lucene.index.IndexWriter;
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   */
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(long companyId, long repositoryId, String dirName)
84          throws PortalException, SystemException {
85      }
86  
87      public void addFile(
88              long companyId, String portletId, long groupId, long repositoryId,
89              String fileName, String properties, String[] tagsEntries,
90              InputStream is)
91          throws PortalException, 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, tagsEntries);
105         }
106         catch (IOException ioe) {
107             throw new SystemException(ioe);
108         }
109         catch (S3ServiceException s3se) {
110             throw new SystemException(s3se);
111         }
112     }
113 
114     public void checkRoot(long companyId) throws SystemException {
115     }
116 
117     public void deleteDirectory(
118             long companyId, String portletId, long repositoryId, String dirName)
119         throws PortalException, 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 PortalException, 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 (IOException ioe) {
154             throw new SystemException(ioe);
155         }
156         catch (S3ServiceException s3se) {
157             throw new SystemException(s3se);
158         }
159     }
160 
161     public void deleteFile(
162             long companyId, String portletId, long repositoryId,
163             String fileName, double versionNumber)
164         throws PortalException, 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 PortalException, 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 PortalException, 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) throws SystemException {
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         IndexWriter writer = null;
283 
284         try {
285             writer = LuceneUtil.getWriter(companyId);
286 
287             S3Object[] searchObjects = _s3Service.listObjects(
288                 _s3Bucket, getKey(companyId, repositoryId), null);
289 
290             Set<String> fileNameSet = new HashSet<String>();
291 
292             for (int i = 0; i < searchObjects.length; i++) {
293                 S3Object currentObject = searchObjects[i];
294 
295                 String fileName = getFileName(currentObject.getKey());
296 
297                 fileNameSet.add(fileName);
298             }
299 
300             Iterator<String> itr = fileNameSet.iterator();
301 
302             while (itr.hasNext()) {
303                 String fileName = itr.next();
304 
305                 try {
306                     Document doc = Indexer.getAddFileDocument(
307                         companyId, portletId, groupId, repositoryId, fileName);
308 
309                     writer.addDocument(doc);
310                 }
311                 catch (Exception e) {
312                     _log.error("Reindexing " + fileName, e);
313                 }
314             }
315         }
316         catch (IOException ioe) {
317             throw new SearchException(ioe);
318         }
319         catch (S3ServiceException s3se) {
320             throw new SearchException(s3se);
321         }
322         finally {
323             try {
324                 if (writer != null) {
325                     LuceneUtil.write(companyId);
326                 }
327             }
328             catch (Exception e) {
329                 _log.error(e);
330             }
331         }
332     }
333 
334     public void updateFile(
335             long companyId, String portletId, long groupId, long repositoryId,
336             String fileName, double versionNumber, String sourceFileName,
337             String properties, String[] tagsEntries, InputStream is)
338         throws PortalException, SystemException {
339 
340         try {
341             S3Object s3Object = new S3Object(
342                 _s3Bucket,
343                 getKey(companyId, repositoryId, fileName, versionNumber));
344 
345             s3Object.setDataInputStream(is);
346 
347             _s3Service.putObject(_s3Bucket, s3Object);
348 
349             Indexer.updateFile(
350                 companyId, portletId, groupId, repositoryId, fileName,
351                 properties, tagsEntries);
352         }
353         catch (IOException ioe) {
354             throw new SystemException(ioe);
355         }
356         catch (S3ServiceException s3se) {
357             throw new SystemException(s3se);
358         }
359     }
360 
361     public void updateFile(
362             long companyId, String portletId, long groupId, long repositoryId,
363             long newRepositoryId, String fileName)
364         throws PortalException, SystemException {
365 
366         try {
367             S3Object[] s3Objects = _s3Service.listObjects(
368                 _s3Bucket, getKey(companyId, repositoryId, fileName), null);
369 
370             for (int i = 0; i < s3Objects.length; i++) {
371                 S3Object oldS3Object = s3Objects[i];
372 
373                 String oldKey = oldS3Object.getKey();
374 
375                 oldS3Object = _s3Service.getObject(_s3Bucket, oldKey);
376 
377                 File tempFile = new File(
378                     SystemProperties.get(SystemProperties.TMP_DIR) +
379                         File.separator + UUID.timeUUID());
380 
381                 InputStream is = null;
382 
383                 try {
384                     is = oldS3Object.getDataInputStream();
385 
386                     FileUtil.write(tempFile, is);
387                 }
388                 catch (Exception e) {
389                 }
390                 finally {
391                     ServletResponseUtil.cleanUp(is);
392                 }
393 
394                 is = new FileInputStream(tempFile);
395 
396                 String newPrefix = getKey(companyId, newRepositoryId);
397 
398                 int x = oldKey.indexOf(StringPool.SLASH);
399 
400                 x = oldKey.indexOf(StringPool.SLASH, x + 1);
401 
402                 String newKey =
403                     newPrefix + oldKey.substring(x + 1, oldKey.length());
404 
405                 S3Object newS3Object = new S3Object(
406                     _s3Bucket, newKey);
407 
408                 newS3Object.setDataInputStream(is);
409 
410                 _s3Service.putObject(_s3Bucket, newS3Object);
411                 _s3Service.deleteObject(_s3Bucket, oldKey);
412 
413                 FileUtil.delete(tempFile);
414             }
415 
416             Indexer.deleteFile(
417                 companyId, portletId, repositoryId, fileName);
418 
419             Indexer.addFile(
420                 companyId, portletId, groupId, newRepositoryId, fileName);
421         }
422         catch (IOException ioe) {
423             throw new SystemException(ioe);
424         }
425         catch (S3ServiceException s3se) {
426             throw new SystemException(s3se);
427         }
428     }
429 
430     protected AWSCredentials getAWSCredentials() throws S3ServiceException {
431         if (Validator.isNull(_ACCESS_KEY) || Validator.isNull(_SECRET_KEY)) {
432             throw new S3ServiceException(
433                 "S3 access and secret keys are not set");
434         }
435         else {
436             return new AWSCredentials(_ACCESS_KEY, _SECRET_KEY);
437         }
438     }
439 
440     protected String getFileName(String key) {
441         int x = key.indexOf(StringPool.SLASH);
442 
443         x = key.indexOf(StringPool.SLASH, x + 1);
444 
445         int y = key.lastIndexOf(StringPool.SLASH);
446 
447         return key.substring(x + 1, y);
448     }
449 
450     protected double getHeadVersionNumber(
451             long companyId, long repositoryId, String fileName)
452         throws PortalException, S3ServiceException {
453 
454         S3Object[] s3Objects = _s3Service.listObjects(
455             _s3Bucket, getKey(companyId, repositoryId, fileName), null);
456 
457         String[] keys = new String[s3Objects.length];
458 
459         for (int i = 0; i < s3Objects.length; i++) {
460             S3Object s3Object = s3Objects[i];
461 
462             keys[i] = s3Object.getKey();
463         }
464 
465         if (keys.length > 0) {
466             Arrays.sort(keys);
467 
468             String headKey = keys[keys.length - 1];
469 
470             int x = headKey.lastIndexOf(StringPool.SLASH);
471 
472             return GetterUtil.getDouble(
473                 headKey.substring(x + 1, headKey.length()));
474         }
475         else {
476             throw new NoSuchFileException(fileName);
477         }
478     }
479 
480     protected String getKey(long companyId, long repositoryId) {
481         StringMaker sm = new StringMaker();
482 
483         sm.append(companyId);
484         sm.append(StringPool.SLASH);
485         sm.append(repositoryId);
486         sm.append(StringPool.SLASH);
487 
488         return sm.toString();
489     }
490 
491     protected String getKey(
492         long companyId, long repositoryId, String fileName) {
493 
494         StringMaker sm = new StringMaker();
495 
496         sm.append(companyId);
497         sm.append(StringPool.SLASH);
498         sm.append(repositoryId);
499         sm.append(StringPool.SLASH);
500         sm.append(fileName);
501         sm.append(StringPool.SLASH);
502 
503         return sm.toString();
504     }
505 
506     protected String getKey(
507         long companyId, long repositoryId, String fileName,
508         double versionNumber) {
509 
510         StringMaker sm = new StringMaker();
511 
512         sm.append(companyId);
513         sm.append(StringPool.SLASH);
514         sm.append(repositoryId);
515         sm.append(StringPool.SLASH);
516         sm.append(fileName);
517         sm.append(StringPool.SLASH);
518         sm.append(versionNumber);
519 
520         return sm.toString();
521     }
522 
523     protected S3Bucket getS3Bucket() throws S3ServiceException {
524         if (Validator.isNull(_BUCKET_NAME)) {
525             throw new S3ServiceException("S3 bucket name is not set");
526         }
527         else {
528             return getS3Service().createBucket(_BUCKET_NAME);
529         }
530     }
531 
532     protected S3Service getS3Service() throws S3ServiceException {
533         AWSCredentials credentials = getAWSCredentials();
534 
535         return new RestS3Service(credentials);
536     }
537 
538     private static final String _ACCESS_KEY = PropsUtil.get(
539         PropsUtil.DL_HOOK_S3_ACCESS_KEY);
540 
541     private static final String _SECRET_KEY = PropsUtil.get(
542         PropsUtil.DL_HOOK_S3_SECRET_KEY);
543 
544     private static final String _BUCKET_NAME = PropsUtil.get(
545         PropsUtil.DL_HOOK_S3_BUCKET_NAME);
546 
547     private static Log _log = LogFactory.getLog(S3Hook.class);
548 
549     private S3Bucket _s3Bucket;
550     private S3Service _s3Service;
551 
552 }