View Javadoc
1   package fr.ifremer.tutti.persistence.service;
2   
3   /*
4    * #%L
5    * Tutti :: Persistence
6    * %%
7    * Copyright (C) 2012 - 2014 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  import com.google.common.base.Preconditions;
26  import com.google.common.collect.Lists;
27  import fr.ifremer.adagio.core.dao.data.measure.file.MeasurementFile;
28  import fr.ifremer.adagio.core.dao.data.measure.file.MeasurementFileDao;
29  import fr.ifremer.adagio.core.dao.referential.ObjectTypeCode;
30  import fr.ifremer.adagio.core.dao.referential.ObjectTypeImpl;
31  import fr.ifremer.adagio.core.dao.referential.QualityFlagCode;
32  import fr.ifremer.adagio.core.dao.referential.QualityFlagImpl;
33  import fr.ifremer.tutti.persistence.entities.data.Attachment;
34  import fr.ifremer.tutti.persistence.entities.data.Attachments;
35  import org.apache.commons.lang3.ObjectUtils;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.hibernate.type.IntegerType;
39  import org.hibernate.type.StringType;
40  import org.nuiton.jaxx.application.ApplicationIOUtil;
41  import org.springframework.dao.DataRetrievalFailureException;
42  import org.springframework.dao.InvalidDataAccessResourceUsageException;
43  import org.springframework.stereotype.Service;
44  
45  import javax.annotation.Resource;
46  import java.io.File;
47  import java.util.Collections;
48  import java.util.Iterator;
49  import java.util.List;
50  import java.util.Set;
51  
52  import static org.nuiton.i18n.I18n.t;
53  
54  /**
55   * Default implementation of the service {@link AttachmentPersistenceService}.
56   *
57   * @author Tony Chemit - chemit@codelutin.com
58   * @since 1.0.2
59   */
60  @Service("attachmentPersistenceService")
61  public class AttachmentPersistenceServiceImpl extends AbstractPersistenceService implements AttachmentPersistenceService {
62  
63      /** Logger. */
64      private static final Log log =
65              LogFactory.getLog(AttachmentPersistenceServiceImpl.class);
66  
67      protected static final String ATTACHMENT_PATH_FORMAT = "%1$s/OBJ%2$s/%1$s-OBJ%2$s-%3$s.%4$s";
68  
69      @Resource(name = "measurementFileDao")
70      protected MeasurementFileDao measurementFileDao;
71  
72      protected File dbAttachmentDirectory;
73  
74      @Override
75      public void init() {
76          super.init();
77          dbAttachmentDirectory = config.getDbAttachmentDirectory();
78  
79          if (log.isDebugEnabled()) {
80              log.debug("Db Attachment storage: " + dbAttachmentDirectory);
81          }
82      }
83  
84      //------------------------------------------------------------------------//
85      //-- AttachmentPersistenceService implementation                        --//
86      //------------------------------------------------------------------------//
87  
88      @Override
89      public List<Attachment> getAllAttachments(ObjectTypeCode objectType,
90                                                Integer objectId) {
91          Iterator<Object[]> list = queryList(
92                  "allAttachment",
93                  "objectId", IntegerType.INSTANCE, objectId,
94                  "objectTypeCode", StringType.INSTANCE, objectType.getValue()
95          );
96  
97          List<Attachment> result = Lists.newArrayList();
98          while (list.hasNext()) {
99              Object[] source = list.next();
100             Attachment target = Attachments.newAttachment();
101             loadAttachment(source, target);
102             result.add(target);
103         }
104         return Collections.unmodifiableList(result);
105     }
106 
107     @Override
108     public File getAttachmentFile(String attachmentId) {
109         Object[] source = queryUnique(
110                 "attachment",
111                 "attachmentId", IntegerType.INSTANCE, Integer.valueOf(attachmentId));
112 
113         if (source == null) {
114             throw new DataRetrievalFailureException(
115                     "Could not retrieve Attachment with id=" + attachmentId);
116         }
117         Attachment target = Attachments.newAttachment();
118         loadAttachment(source, target);
119 
120         return getFile(target);
121     }
122 
123     @Override
124     public Attachment createAttachment(Attachment attachment, File file) {
125         Preconditions.checkNotNull(attachment);
126         Preconditions.checkNotNull(attachment.getObjectType());
127         Preconditions.checkNotNull(attachment.getObjectId());
128         Preconditions.checkArgument(
129                 attachment.getId() == null,
130                 "Attachment 'id' must be null to call createAttachment().");
131         Preconditions.checkNotNull(file);
132 
133         // Create measurement file
134         MeasurementFile measurementFile = MeasurementFile.Factory.newInstance();
135 
136         // no usage of pmfm (since version 1.5)
137         measurementFile.setPmfm(null);
138 
139         // set not qualifed flag
140         measurementFile.setQualityFlag(load(QualityFlagImpl.class, QualityFlagCode.NOTQUALIFIED.getValue()));
141 
142         // set objectType
143         measurementFile.setObjectType(load(ObjectTypeImpl.class, attachment.getObjectType().getValue()));
144 
145         // set objectId
146         measurementFile.setObjectId(attachment.getObjectId());
147 
148         // Use first a fake filePath (we don't know the id of attachment entity)
149         measurementFile.setPath("FAKE-" + System.nanoTime());
150 
151         // copy our property
152         attachmentToEntity(attachment, measurementFile);
153         measurementFileDao.create(measurementFile);
154 
155         // get created id
156         attachment.setId(String.valueOf(measurementFile.getId()));
157 
158         // Build now the real path
159         String filePath = String.format(ATTACHMENT_PATH_FORMAT,
160                                         measurementFile.getObjectType().getCode(),
161                                         measurementFile.getObjectId(),
162                                         measurementFile.getId(),
163                                         ApplicationIOUtil.getExtension(file.getName()));
164 
165         // store the path
166         attachment.setPath(filePath);
167         measurementFile.setPath(filePath);
168 
169         if (log.isDebugEnabled()) {
170             log.debug("Created attachment: " + attachment.getId() +
171                       ", path: " + filePath);
172         }
173 
174         // update measurementFile with correct path
175         measurementFileDao.update(measurementFile);
176 
177         // copy file to disk local storage
178         File targetFile = getFile(attachment);
179 
180         ApplicationIOUtil.copyFile(
181                 file, targetFile,
182                 t("tutti.persistence.attachment.copyFile.error", file, targetFile));
183 
184         return attachment;
185     }
186 
187     @Override
188     public Attachment saveAttachment(Attachment attachment) {
189         Preconditions.checkNotNull(attachment);
190         Preconditions.checkNotNull(attachment.getObjectType());
191         Preconditions.checkNotNull(attachment.getObjectId());
192         Preconditions.checkNotNull(
193                 attachment.getId(),
194                 "Attachment 'id' must not be null or empty to be saved.");
195 
196         MeasurementFile measurementFile = measurementFileDao.load(Integer.valueOf(attachment.getId()));
197         if (measurementFile == null) {
198             throw new DataRetrievalFailureException("Could not retrieve Attachment with id=" + attachment.getId());
199         }
200 
201         // can't change the objectType
202         String oldObjectTypeCode = measurementFile.getObjectType().getCode();
203         if (ObjectUtils.notEqual(attachment.getObjectType().getValue(),
204                                  oldObjectTypeCode)) {
205             throw new InvalidDataAccessResourceUsageException(
206                     "Can't change objectType, was before " + oldObjectTypeCode);
207         }
208 
209         // can't change either objectId
210         Integer oldObjectId = measurementFile.getObjectId();
211         if (ObjectUtils.notEqual(attachment.getObjectId(),
212                                  oldObjectId)) {
213             throw new InvalidDataAccessResourceUsageException(
214                     "Can't change objectId, was before " + oldObjectId);
215         }
216 
217         attachmentToEntity(attachment, measurementFile);
218         measurementFileDao.update(measurementFile);
219         return attachment;
220     }
221 
222     @Override
223     public void deleteAttachment(String attachmentId) {
224         Integer id = Integer.valueOf(attachmentId);
225         Object[] source = queryUnique(
226                 "attachment",
227                 "attachmentId", IntegerType.INSTANCE, id);
228 
229         if (source == null) {
230             throw new DataRetrievalFailureException(
231                     "Could not retrieve Attachment with id=" + attachmentId);
232         }
233         Attachment target = Attachments.newAttachment();
234         loadAttachment(source, target);
235 
236         delete(target);
237 
238         getCurrentSession().flush();
239     }
240 
241     @Override
242     public void deleteAllAttachment(ObjectTypeCode objectType, Integer objectId) {
243 
244         List<Attachment> attachments = getAllAttachments(objectType, objectId);
245         attachments.forEach(this::delete);
246 
247     }
248 
249     @Override
250     public void deleteAllAttachment(ObjectTypeCode objectType, Set<Integer> objectIds) {
251 
252         for (Integer objectId : objectIds) {
253             List<Attachment> attachments = getAllAttachments(objectType, objectId);
254             attachments.forEach(this::delete);
255         }
256 
257     }
258 
259     //------------------------------------------------------------------------//
260     //-- Internal methods                                                   --//
261     //------------------------------------------------------------------------//
262 
263     protected void loadAttachment(Object[] source,
264                                   Attachment target) {
265 
266         target.setObjectType(ObjectTypeCode.valueOf((String) source[0]));
267         target.setObjectId((Integer) source[1]);
268         target.setId(String.valueOf(source[2]));
269         target.setPath((String) source[3]);
270         target.setName((String) source[4]);
271         target.setComment((String) source[5]);
272     }
273 
274     protected void attachmentToEntity(Attachment attachment,
275                                       MeasurementFile measurementFile) {
276         measurementFile.setName(attachment.getName());
277         measurementFile.setComments(attachment.getComment());
278     }
279 
280     protected File getFile(Attachment attachment) {
281         return new File(dbAttachmentDirectory, attachment.getPath());
282     }
283 
284     protected void delete(Attachment target) {
285 
286         File file = getFile(target);
287 
288         if (log.isDebugEnabled()) {
289             log.debug("Will delete attachment: " + target.getName() + " -- " + file);
290         }
291         measurementFileDao.remove(target.getIdAsInt());
292 
293         if (file.exists()) {
294             ApplicationIOUtil.deleteFile(file, t("tutti.persistence.attachment.deleteFile.error", file));
295         } else {
296             if (log.isWarnEnabled()) {
297                 log.warn("COULD NOT FIND Attachement at " + file);
298             }
299         }
300 
301         File parentFile = file.getParentFile();
302         while (!parentFile.equals(dbAttachmentDirectory)) {
303 
304             File[] files = parentFile.listFiles();
305             if (files != null && files.length == 0) {
306 
307                 // can delete this directory
308                 if (log.isDebugEnabled()) {
309                     log.debug("Remove empty directory: " + parentFile);
310                 }
311                 ApplicationIOUtil.deleteDirectory(parentFile, "Could not clean directory");
312                 parentFile = parentFile.getParentFile();
313             } else {
314                 break;
315             }
316 
317         }
318 
319     }
320 }