View Javadoc
1   package org.exoplatform.commons.file.services.impl;
2   
3   import org.apache.commons.lang.StringUtils;
4   import org.exoplatform.commons.api.persistence.ExoTransactional;
5   import org.exoplatform.commons.file.model.NameSpace;
6   import org.exoplatform.commons.file.resource.BinaryProvider;
7   import org.exoplatform.commons.file.model.FileItem;
8   import org.exoplatform.commons.file.model.FileInfo;
9   import org.exoplatform.commons.file.services.FileService;
10  import org.exoplatform.commons.file.services.FileStorageException;
11  import org.exoplatform.commons.file.services.NameSpaceService;
12  import org.exoplatform.commons.file.storage.DataStorage;
13  import org.exoplatform.services.log.ExoLogger;
14  import org.exoplatform.services.log.Log;
15  
16  import java.io.IOException;
17  import java.io.InputStream;
18  
19  /**
20   * File Service which stores the file metadata in a database, and uses a
21   * BinaryProvider to store the file binary. Created by The eXo Platform SAS
22   * Author : eXoPlatform exo@exoplatform.com
23   */
24  public class FileServiceImpl implements FileService {
25  
26    private static final Log    LOG             = ExoLogger.getLogger(FileServiceImpl.class);
27  
28    private DataStorage         dataStorage;
29  
30    private BinaryProvider      binaryProvider;
31  
32    private NameSpaceService    nameSpaceService;
33  
34    public FileServiceImpl(DataStorage dataStorage, BinaryProvider resourceProvider, NameSpaceService nameSpaceService) throws Exception {
35      this.dataStorage = dataStorage;
36      this.binaryProvider = resourceProvider;
37      this.nameSpaceService = nameSpaceService;
38    }
39  
40    @Override
41    public FileInfo getFileInfo(long id){
42      return dataStorage.getFileInfo(id);
43    }
44  
45    @Override
46    public FileItem getFile(long id) throws FileStorageException {
47      FileItem fileItem;
48      FileInfo fileInfo = getFileInfo(id);
49  
50      if (fileInfo == null || StringUtils.isEmpty(fileInfo.getChecksum())) {
51        return null;
52      }
53      try {
54        fileItem = new FileItem(fileInfo, null);
55        InputStream inputStream = binaryProvider.getStream(fileInfo.getChecksum());
56        fileItem.setInputStream(inputStream);
57      } catch (Exception e) {
58        throw new FileStorageException("Cannot get File Item ID=" + id, e);
59      }
60  
61      return fileItem;
62    }
63  
64    @Override
65    @ExoTransactional
66    public FileItem writeFile(FileItem file) throws FileStorageException, IOException {
67      if (file.getFileInfo() == null || StringUtils.isEmpty(file.getFileInfo().getChecksum())) {
68        throw new IllegalArgumentException("Checksum is required to persist the binary");
69      }
70      FileInfo fileInfo = file.getFileInfo();
71      NameSpace nSpace;
72      if (fileInfo.getNameSpace() != null && !fileInfo.getNameSpace().isEmpty()) {
73        nSpace = dataStorage.getNameSpace(fileInfo.getNameSpace());
74      } else {
75        nSpace = dataStorage.getNameSpace(nameSpaceService.getDefaultNameSpace());
76      }
77      FileStorageTransaction transaction = new FileStorageTransaction(fileInfo, nSpace);
78      FileInfo createdFileInfoEntity = transaction.twoPhaseCommit(2, file.getAsStream());
79      if (createdFileInfoEntity != null) {
80        fileInfo.setId(createdFileInfoEntity.getId());
81        file.setFileInfo(fileInfo);
82        return file;
83      }
84      return null;
85    }
86  
87    @Override
88    @ExoTransactional
89    public FileItem updateFile(FileItem file) throws FileStorageException, IOException {
90      if (file.getFileInfo() == null || StringUtils.isEmpty(file.getFileInfo().getChecksum())) {
91        throw new IllegalArgumentException("Checksum is required to persist the binary");
92      }
93      FileInfo fileInfo = file.getFileInfo();
94      NameSpace nSpace;
95      if (fileInfo.getNameSpace() != null && !fileInfo.getNameSpace().isEmpty()) {
96        nSpace = dataStorage.getNameSpace(fileInfo.getNameSpace());
97      } else {
98        nSpace = dataStorage.getNameSpace(nameSpaceService.getDefaultNameSpace());
99      }
100     FileStorageTransaction transaction = new FileStorageTransaction(fileInfo, nSpace);
101     FileInfo createdFileInfoEntity = transaction.twoPhaseCommit(0, file.getAsStream());
102     if (createdFileInfoEntity != null) {
103       fileInfo.setId(createdFileInfoEntity.getId());
104       file.setFileInfo(fileInfo);
105       return file;
106     }
107     return null;
108   }
109 
110   @Override
111   public FileInfo deleteFile(long id) {
112     FileInfo fileInfo = dataStorage.getFileInfo(id);
113     if (fileInfo != null) {
114       fileInfo.setDeleted(true);
115     }
116     return dataStorage.updateFileInfo(fileInfo);
117   }
118 
119   /* Manage two phase commit :file storage and datasource */
120   private class FileStorageTransaction {
121     /**
122      * Update Operation.
123      */
124     final int         UPDATE = 0;
125 
126     /**
127      * Remove Operation.
128      */
129     final int         REMOVE = 1;
130 
131     /**
132      * Insert Operation.
133      */
134     final int         INSERT = 2;
135 
136     private FileInfo  fileInfo;
137 
138     private NameSpace nameSpace;
139 
140     public FileStorageTransaction(FileInfo fileInfo, NameSpace nameSpace) {
141       this.fileInfo = fileInfo;
142       this.nameSpace = nameSpace;
143     }
144 
145     public FileInfo twoPhaseCommit(int operation, InputStream inputStream) throws FileStorageException {
146       FileInfo createdFileInfoEntity = null;
147       if (operation == INSERT) {
148         boolean created = false;
149         try {
150           if (!binaryProvider.exists(fileInfo.getChecksum())) {
151             binaryProvider.put(fileInfo.getChecksum(), inputStream);
152             created = true;
153           }
154           if (binaryProvider.exists(fileInfo.getChecksum())) {
155             createdFileInfoEntity = dataStorage.create(fileInfo, nameSpace);
156             return createdFileInfoEntity;
157           } else {
158             throw new FileStorageException("Error while writing file " + fileInfo.getName());
159           }
160         } catch (Exception e) {
161           try {
162             if (created) {
163               binaryProvider.remove(fileInfo.getChecksum());
164             }
165           } catch (IOException e1) {
166             LOG.error("Error while rollback writing file");
167           }
168           throw new FileStorageException("Error while writing file " + fileInfo.getName(), e);
169         }
170 
171       } else if (operation == REMOVE) {
172         fileInfo.setDeleted(true);
173         dataStorage.updateFileInfo(fileInfo);
174       } else if (operation == UPDATE) {
175         try {
176           boolean updated = false;
177           FileInfo oldFile = dataStorage.getFileInfo(fileInfo.getId());
178           if (oldFile == null || oldFile.getChecksum().isEmpty()
179               || !oldFile.getChecksum().equals(fileInfo.getChecksum())) {
180             if (!binaryProvider.exists(fileInfo.getChecksum())) {
181               binaryProvider.put(fileInfo.getChecksum(), inputStream);
182             }
183             updated = true;
184           }
185           if (updated && dataStorage.sharedChecksum(oldFile.getChecksum()) ==1) {
186             dataStorage.createOrphanFile(oldFile);
187           }
188           if (binaryProvider.exists(fileInfo.getChecksum())) {
189             createdFileInfoEntity = dataStorage.updateFileInfo(fileInfo);
190             return createdFileInfoEntity;
191           } else {
192             throw new FileStorageException("Error while writing file " + fileInfo.getName());
193           }
194         } catch (Exception e) {
195           try {
196             binaryProvider.remove(fileInfo.getChecksum());
197           } catch (IOException e1) {
198             LOG.error("Error while rollback writing file");
199           }
200           throw new FileStorageException("Error while writing file " + fileInfo.getName(), e);
201         }
202       }
203       return null;
204     }
205   }
206 
207 }