1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.store.legacy.store.internal

File FilesystemAttachmentStore.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart7.png
64% of files have more coverage

Code metrics

30
89
19
3
501
302
38
0.43
4.68
6.33
2

Classes

Class Line # Actions
FilesystemAttachmentStore 60 49 0% 25 33
0.576923157.7%
FilesystemAttachmentStore.AttachmentSaveTransactionRunnable 322 19 0% 6 7
0.7575%
FilesystemAttachmentStore.AttachmentDeleteTransactionRunnable 410 21 0% 7 5
0.8437584.4%
 

Contributing tests

This file is covered by 6 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.store.legacy.store.internal;
21   
22    import java.io.File;
23    import java.util.List;
24    import java.util.concurrent.locks.ReadWriteLock;
25   
26    import javax.inject.Inject;
27    import javax.inject.Named;
28    import javax.inject.Provider;
29    import javax.inject.Singleton;
30   
31    import org.hibernate.Session;
32    import org.xwiki.component.annotation.Component;
33    import org.xwiki.store.FileDeleteTransactionRunnable;
34    import org.xwiki.store.FileSaveTransactionRunnable;
35    import org.xwiki.store.StreamProvider;
36    import org.xwiki.store.TransactionRunnable;
37    import org.xwiki.store.filesystem.internal.FilesystemStoreTools;
38    import org.xwiki.store.legacy.doc.internal.FilesystemAttachmentContent;
39    import org.xwiki.store.legacy.doc.internal.ListAttachmentArchive;
40   
41    import com.xpn.xwiki.XWikiContext;
42    import com.xpn.xwiki.XWikiException;
43    import com.xpn.xwiki.doc.XWikiAttachment;
44    import com.xpn.xwiki.doc.XWikiAttachmentArchive;
45    import com.xpn.xwiki.doc.XWikiAttachmentContent;
46    import com.xpn.xwiki.doc.XWikiDocument;
47    import com.xpn.xwiki.store.AttachmentVersioningStore;
48    import com.xpn.xwiki.store.XWikiAttachmentStoreInterface;
49    import com.xpn.xwiki.store.XWikiStoreInterface;
50   
51    /**
52    * Filesystem based implementation of XWikiAttachmentStoreInterface.
53    *
54    * @version $Id: 4178412eeead75241e402b3a7bda034d8237ffe8 $
55    * @since 3.0M2
56    */
57    @Component
58    @Named("file")
59    @Singleton
 
60    public class FilesystemAttachmentStore implements XWikiAttachmentStoreInterface
61    {
62    @Inject
63    private Provider<FilesystemStoreTools> fileToolsProvider;
64   
65    /**
66    * Tools for getting files to store given content in.
67    */
68    private FilesystemStoreTools fileTools;
69   
70    /**
71    * Testing Constructor.
72    *
73    * @param fileTools tools for getting files to store given content in and locks.
74    */
 
75  6 toggle public FilesystemAttachmentStore(final FilesystemStoreTools fileTools)
76    {
77  6 this.fileTools = fileTools;
78    }
79   
80    /**
81    * Constructor for component manager.
82    */
 
83  0 toggle public FilesystemAttachmentStore()
84    {
85    }
86   
 
87  13 toggle private FilesystemStoreTools getFilesystemStoreTools()
88    {
89  13 if (fileTools != null) {
90  13 return fileTools;
91    }
92  0 return fileToolsProvider.get();
93    }
94   
95    /**
96    * {@inheritDoc}
97    * <p>
98    * This implementation cannot operate in a larger transaction so it starts a new transaction no matter
99    * whether bTransaction is true or false.
100    * </p>
101    *
102    * @see com.xpn.xwiki.store.XWikiAttachmentStoreInterface#saveAttachmentContent(
103    *XWikiAttachment, XWikiContext, boolean)
104    */
 
105  0 toggle public void saveAttachmentContent(final XWikiAttachment attachment,
106    final XWikiContext context,
107    final boolean bTransaction)
108    throws XWikiException
109    {
110  0 this.saveAttachmentContent(attachment, true, context, bTransaction);
111    }
112   
113    /**
114    * {@inheritDoc}
115    * <p>
116    * This implementation cannot operate in a larger transaction so it starts a new transaction no matter
117    * whether bTransaction is true or false.
118    * </p>
119    *
120    * @see com.xpn.xwiki.store.XWikiAttachmentStoreInterface#saveAttachmentContent(
121    *XWikiAttachment, boolean, XWikiContext, boolean)
122    */
 
123  2 toggle public void saveAttachmentContent(final XWikiAttachment attachment,
124    final boolean updateDocument,
125    final XWikiContext context,
126    final boolean bTransaction)
127    throws XWikiException
128    {
129  2 final XWikiHibernateTransaction transaction = new XWikiHibernateTransaction(context);
130  2 this.getAttachmentContentSaveRunnable(attachment, updateDocument, context).runIn(transaction);
131  2 try {
132  2 transaction.start();
133    } catch (Exception e) {
134  0 if (e instanceof XWikiException) {
135  0 throw (XWikiException) e;
136    }
137  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
138    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_ATTACHMENT,
139    "Exception while saving attachment.", e);
140    }
141    }
142   
143    /**
144    * Get a TransactionRunnable for saving the attachment content.
145    * If {@link XWikiAttachment#getAttachment_content()} yields null, this runnable will do nothing.
146    *
147    * @param attachment the XWikiAttachment whose content should be saved.
148    * @param updateDocument whether or not to update the document at the same time.
149    * @param context the XWikiContext for the request.
150    * @return a TransactionRunnable for saving the attachment content in an XWikiHibernateTransaction.
151    * @throws XWikiException if thrown by AttachmentSaveTransactionRunnable()
152    */
 
153  4 toggle private TransactionRunnable<XWikiHibernateTransaction> getAttachmentContentSaveRunnable(
154    final XWikiAttachment attachment,
155    final boolean updateDocument,
156    final XWikiContext context)
157    throws XWikiException
158    {
159  4 final XWikiAttachmentContent content = attachment.getAttachment_content();
160   
161  4 if (content == null) {
162    // If content does not exist we should not blank the attachment.
163  0 return new TransactionRunnable<XWikiHibernateTransaction>();
164    }
165   
166    // This is the permanent location where the attachment content will go.
167  4 final File attachFile =
168    getFilesystemStoreTools().getAttachmentFileProvider(attachment).getAttachmentContentFile();
169  4 final FilesystemStoreTools ft = getFilesystemStoreTools();
170   
171  4 return new AttachmentSaveTransactionRunnable(attachment,
172    updateDocument,
173    context,
174    attachFile,
175    ft.getTempFile(attachFile),
176    ft.getBackupFile(attachFile),
177    ft.getLockForFile(attachFile));
178    }
179   
180    /**
181    * {@inheritDoc}
182    * <p>
183    * This implementation cannot operate in a larger transaction so it starts a new transaction no matter
184    * whether bTransaction is true or false.
185    * </p>
186    *
187    * @see com.xpn.xwiki.store.XWikiAttachmentStoreInterface#saveAttachmentsContent(
188    *List, XWikiDocument, boolean, XWikiContext, boolean)
189    */
 
190  1 toggle public void saveAttachmentsContent(final List<XWikiAttachment> attachments,
191    final XWikiDocument doc,
192    final boolean updateDocument,
193    final XWikiContext context,
194    final boolean bTransaction) throws XWikiException
195    {
196  1 if (attachments == null || attachments.size() == 0) {
197  0 return;
198    }
199   
200  1 try {
201  1 final XWikiHibernateTransaction transaction = new XWikiHibernateTransaction(context);
202   
203  1 for (XWikiAttachment attach : attachments) {
204  2 this.getAttachmentContentSaveRunnable(attach, false, context).runIn(transaction);
205    }
206   
207    // Save the parent document only once.
208  1 if (updateDocument) {
209  0 new TransactionRunnable<XWikiHibernateTransaction>()
210    {
 
211  0 toggle protected void onRun() throws Exception
212    {
213  0 context.getWiki().getStore().saveXWikiDoc(doc, context, false);
214    }
215    } .runIn(transaction);
216    }
217   
218  1 transaction.start();
219    } catch (Exception e) {
220  0 if (e instanceof XWikiException) {
221  0 throw (XWikiException) e;
222    }
223  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
224    XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_ATTACHMENT,
225    "Exception while saving attachments", e);
226    }
227    }
228   
 
229  1 toggle @Override
230    public void loadAttachmentContent(final XWikiAttachment attachment,
231    final XWikiContext context,
232    final boolean bTransaction)
233    throws XWikiException
234    {
235  1 final File attachFile =
236    getFilesystemStoreTools().getAttachmentFileProvider(attachment).getAttachmentContentFile();
237   
238  1 if (attachFile.exists()) {
239  1 FilesystemAttachmentContent content = new FilesystemAttachmentContent(attachFile);
240  1 content.setContentDirty(false);
241  1 attachment.setAttachment_content(content);
242  1 return;
243    }
244   
245  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
246    XWikiException.ERROR_XWIKI_STORE_FILENOTFOUND,
247    "The attachment could not be found in the filesystem attachment store.\n"
248    + "This can happen if attachment storage is switched from database to "
249    + "filesystem without first moving all of the database attachments over "
250    + "to the filesystem using a script.");
251    }
252   
 
253  0 toggle @Override
254    public void deleteXWikiAttachment(final XWikiAttachment attachment,
255    final XWikiContext context,
256    final boolean bTransaction)
257    throws XWikiException
258    {
259  0 this.deleteXWikiAttachment(attachment, true, context, bTransaction);
260    }
261   
 
262  2 toggle @Override
263    public void deleteXWikiAttachment(final XWikiAttachment attachment,
264    final boolean parentUpdate,
265    final XWikiContext context,
266    final boolean bTransaction)
267    throws XWikiException
268    {
269  2 final XWikiHibernateTransaction transaction = new XWikiHibernateTransaction(context);
270  2 this.getAttachmentDeleteRunnable(attachment, parentUpdate, context).runIn(transaction);
271  2 try {
272  2 transaction.start();
273    } catch (Exception e) {
274  0 if (e instanceof XWikiException) {
275  0 throw (XWikiException) e;
276    }
277  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
278    XWikiException.ERROR_XWIKI_UNKNOWN,
279    "Exception while deleting attachment.", e);
280    }
281    }
282   
283    /**
284    * Get a TransactionRunnable for deleting an attachment.
285    *
286    * @param attachment the XWikiAttachment to delete.
287    * @param updateDocument whether or not to update the document at the same time.
288    * @param context the XWikiContext for the request.
289    * @return a TransactionRunnable for deleting the attachment which must be run inside of an
290    * XWikiHibernateTransaction
291    * @throws XWikiException if unable to load the attachment archive to delete.
292    */
 
293  2 toggle private TransactionRunnable<XWikiHibernateTransaction> getAttachmentDeleteRunnable(
294    final XWikiAttachment attachment,
295    final boolean updateDocument,
296    final XWikiContext context)
297    throws XWikiException
298    {
299  2 final File attachFile =
300    getFilesystemStoreTools().getAttachmentFileProvider(attachment).getAttachmentContentFile();
301  2 final FilesystemStoreTools ft = getFilesystemStoreTools();
302   
303  2 return new AttachmentDeleteTransactionRunnable(attachment,
304    updateDocument,
305    context,
306    attachFile,
307    ft.getBackupFile(attachFile),
308    ft.getLockForFile(attachFile));
309    }
310   
 
311  0 toggle @Override
312    public void cleanUp(XWikiContext context)
313    {
314    // Do nothing.
315    }
316   
317    /* ---------------------------- Nested Classes. ---------------------------- */
318   
319    /**
320    * A TransactionRunnable for saving an attachment.
321    */
 
322    private static class AttachmentSaveTransactionRunnable
323    extends TransactionRunnable<XWikiHibernateTransaction>
324    {
325    /**
326    * The XWikiAttachment whose content should be saved.
327    */
328    private final XWikiAttachment attachment;
329   
330    /**
331    * Whether or not to update the document at the same time.
332    */
333    private final boolean updateDocument;
334   
335    /**
336    * The XWikiContext for the request.
337    */
338    private final XWikiContext context;
339   
340    /**
341    * Construct a TransactionRunnable for saving the attachment content.
342    *
343    * @param attachment the XWikiAttachment whose content should be saved.
344    * @param updateDocument whether or not to update the document at the same time.
345    * @param context the XWikiContext for the request.
346    * @param attachFile the File to store the attachment in.
347    * @param tempFile the File to put the attachment content in until the transaction is complete.
348    * @param backupFile the File to backup the content of the existing attachment in.
349    * @param lock this Lock will be locked while the attachment file is being written to.
350    * @throws XWikiException if thrown by {@link XWikiAttachment#updateContentArchive(XWikiContext)}
351    * or {@link FilesystemAttachmentVersioningStore#
352    * getArchiveSaveRunnable(XWikiAttachmentArchive, XWikiContext)
353    */
 
354  4 toggle AttachmentSaveTransactionRunnable(final XWikiAttachment attachment,
355    final boolean updateDocument,
356    final XWikiContext context,
357    final File attachFile,
358    final File tempFile,
359    final File backupFile,
360    final ReadWriteLock lock)
361    throws XWikiException
362    {
363  4 final StreamProvider provider = new AttachmentContentStreamProvider(attachment, context);
364  4 new FileSaveTransactionRunnable(attachFile, tempFile, backupFile, lock, provider).runIn(this);
365   
366    // If the versioning store supports TransactionRunnable then use it, otherwise don't.
367  4 final AttachmentVersioningStore avs = context.getWiki().getAttachmentVersioningStore();
368  4 final XWikiAttachmentArchive archive = attachment.getAttachment_archive();
369  4 if (avs instanceof FilesystemAttachmentVersioningStore) {
370  0 final FilesystemAttachmentVersioningStore favs = (FilesystemAttachmentVersioningStore) avs;
371   
372    // If first save then create a new archive.
373  0 if (archive == null) {
374  0 favs.getArchiveSaveRunnable(new ListAttachmentArchive(attachment), context).runIn(this);
375    } else {
376  0 favs.getArchiveSaveRunnable(archive, context).runIn(this);
377    }
378    } else {
379  4 new TransactionRunnable<XWikiHibernateTransaction>()
380    {
 
381  4 toggle protected void onRun() throws XWikiException
382    {
383  4 avs.saveArchive(archive, context, false);
384    }
385    } .runIn(this);
386    }
387   
388    // If updating of the parent document is required then add a TransactionRunnable to do that.
389  4 if (updateDocument) {
390  1 final XWikiStoreInterface store = context.getWiki().getStore();
391  1 final XWikiDocument doc = attachment.getDoc();
392  1 new TransactionRunnable<XWikiHibernateTransaction>()
393    {
 
394  1 toggle protected void onRun() throws XWikiException
395    {
396  1 store.saveXWikiDoc(doc, context, false);
397    }
398    } .runIn(this);
399    }
400   
401  4 this.attachment = attachment;
402  4 this.updateDocument = updateDocument;
403  4 this.context = context;
404    }
405    }
406   
407    /**
408    * A TransactionRunnable for deleting an attachment.
409    */
 
410    private static class AttachmentDeleteTransactionRunnable
411    extends TransactionRunnable<XWikiHibernateTransaction>
412    {
413    /**
414    * The XWikiAttachment whose content should be saved.
415    */
416    private final XWikiAttachment attachment;
417   
418    /**
419    * Whether or not to update the document at the same time.
420    */
421    private final boolean updateDocument;
422   
423    /**
424    * The XWikiContext for the request.
425    */
426    private final XWikiContext context;
427   
428    /**
429    * Construct a TransactionRunnable for deleting the attachment.
430    *
431    * @param attachment the XWikiAttachment to delete
432    * @param updateDocument whether or not to update the document at the same time.
433    * @param context the XWikiContext for the request.
434    * @param attachFile the file to where the attachment content is stored.
435    * @param tempFile the file to to move the attachment content to temporarily.
436    * @param lock this Lock will be locked while the attachment file is being written to.
437    * @throws XWikiException if unable to load the archive for the attachment to delete.
438    */
 
439  2 toggle AttachmentDeleteTransactionRunnable(final XWikiAttachment attachment,
440    final boolean updateDocument,
441    final XWikiContext context,
442    final File attachFile,
443    final File tempFile,
444    final ReadWriteLock lock)
445    throws XWikiException
446    {
447  2 new FileDeleteTransactionRunnable(attachFile, tempFile, lock).runIn(this);
448   
449    // If the store supports deleting in the same transaction then do it.
450  2 final AttachmentVersioningStore avs = context.getWiki().getAttachmentVersioningStore();
451   
452  2 if (avs instanceof FilesystemAttachmentVersioningStore) {
453  0 final FilesystemAttachmentVersioningStore favs = (FilesystemAttachmentVersioningStore) avs;
454  0 favs.getArchiveDeleteRunnable(attachment.loadArchive(context)).runIn(this);
455    } else {
456  2 new TransactionRunnable<HibernateTransaction>()
457    {
 
458  2 toggle protected void onRun() throws XWikiException
459    {
460  2 avs.deleteArchive(attachment, context, false);
461    }
462    } .runIn(this);
463    }
464   
465  2 this.context = context;
466  2 this.attachment = attachment;
467  2 this.updateDocument = updateDocument;
468    }
469   
 
470  2 toggle @Override
471    protected void onRun() throws Exception
472    {
473    // TODO: When the rest of storage is rewritten using TransactionRunnable,
474    // this method should be disolved.
475   
476  2 final Session session = this.context.getWiki().getHibernateStore().getSession(this.context);
477   
478    // Delete the content from the attachment.
479    // In case it was stored in the database by XWikiHibernateAttachmentStore.
480  2 session.delete(new XWikiAttachmentContent(this.attachment));
481   
482    // Update the document if required.
483  2 if (this.updateDocument) {
484  1 final String filename = this.attachment.getFilename();
485  1 final List<XWikiAttachment> list = attachment.getDoc().getAttachmentList();
486  1 for (int i = 0; i < list.size(); i++) {
487  1 if (filename.equals(list.get(i).getFilename())) {
488  1 list.remove(i);
489  1 break;
490    }
491    }
492  1 this.context.getWiki().getStore().saveXWikiDoc(this.attachment.getDoc(),
493    this.context,
494    false);
495    }
496   
497    // Delete the attachment metadata.
498  2 session.delete(this.attachment);
499    }
500    }
501    }