← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 20905: Implemented fix for mysterious file resource save race condition + some refactoring.

 

------------------------------------------------------------
revno: 20905
committer: Halvdan Hoem Grelland <halvdanhg@xxxxxxxxx>
branch nick: dhis2
timestamp: Fri 2015-10-30 15:43:11 +0100
message:
  Implemented fix for mysterious file resource save race condition + some refactoring.
removed:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceStore.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceUploadCallbackProvider.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/hibernate/
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/hibernate/HibernateFileResourceStore.java
added:
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceUploadCallback.java
modified:
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java
  dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== removed file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceStore.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceStore.java	2015-10-26 16:01:20 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceStore.java	1970-01-01 00:00:00 +0000
@@ -1,40 +0,0 @@
-package org.hisp.dhis.fileresource;
-
-/*
- * Copyright (c) 2004-2015, University of Oslo
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * Neither the name of the HISP project nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-import org.hisp.dhis.common.GenericIdentifiableObjectStore;
-
-/**
- * @author Halvdan Hoem Grelland
- */
-public interface FileResourceStore
-    extends GenericIdentifiableObjectStore<FileResource>
-{
-    void saveInTransaction( FileResource fileResource );
-}

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java	2015-10-26 16:01:20 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java	2015-10-30 14:43:11 +0000
@@ -29,14 +29,14 @@
  */
 
 import com.google.common.io.ByteSource;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.hisp.dhis.common.GenericIdentifiableObjectStore;
 import org.hisp.dhis.system.scheduling.Scheduler;
 import org.joda.time.DateTime;
 import org.joda.time.Duration;
 import org.joda.time.Hours;
-import org.joda.time.Minutes;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.support.TransactionSynchronizationAdapter;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
 import org.springframework.util.concurrent.ListenableFuture;
 
 import javax.annotation.PostConstruct;
@@ -52,12 +52,9 @@
 public class DefaultFileResourceService
     implements FileResourceService
 {
-    private static final Log log = LogFactory.getLog( DefaultFileResourceService.class );
-
     private static final String KEY_FILE_CLEANUP_TASK = "fileResourceCleanupTask";
 
     private static final Duration IS_ORPHAN_TIME_DELTA = Hours.TWO.toStandardDuration();
-    private static final Duration LONG_STORAGE_DURATION_TIME_DELTA = Minutes.TWO.toStandardDuration();
 
     private static final Predicate<FileResource> IS_ORPHAN_PREDICATE =
         ( fr -> !fr.isAssigned() || fr.getStorageStatus() != FileResourceStorageStatus.STORED );
@@ -65,17 +62,10 @@
     // -------------------------------------------------------------------------
     // Dependencies
     // -------------------------------------------------------------------------
-//
-//    private GenericIdentifiableObjectStore<FileResource> fileResourceStore;
-//
-//    public void setFileResourceStore( GenericIdentifiableObjectStore<FileResource> fileResourceStore )
-//    {
-//        this.fileResourceStore = fileResourceStore;
-//    }
-
-    private FileResourceStore fileResourceStore;
-
-    public void setFileResourceStore( FileResourceStore fileResourceStore )
+
+    private GenericIdentifiableObjectStore<FileResource> fileResourceStore;
+
+    public void setFileResourceStore( GenericIdentifiableObjectStore<FileResource> fileResourceStore )
     {
         this.fileResourceStore = fileResourceStore;
     }
@@ -94,11 +84,11 @@
         this.scheduler = scheduler;
     }
 
-    private FileResourceUploadCallbackProvider uploadCallbackProvider;
+    private FileResourceUploadCallback uploadCallback;
 
-    public void setUploadCallbackProvider( FileResourceUploadCallbackProvider uploadCallbackProvider )
+    public void setUploadCallback( FileResourceUploadCallback uploadCallback )
     {
-        this.uploadCallbackProvider = uploadCallbackProvider;
+        this.uploadCallback = uploadCallback;
     }
 
     private FileResourceCleanUpTask fileResourceCleanUpTask;
@@ -126,12 +116,9 @@
     // FileResourceService implementation
     // -------------------------------------------------------------------------
 
-    @Transactional
     @Override
     public FileResource getFileResource( String uid )
     {
-        // TODO Consider need for ensureStorageStatus
-//        return ensureStorageStatus( fileResourceStore.getByUid( uid ) );
         return fileResourceStore.getByUid( uid );
     }
 
@@ -141,7 +128,6 @@
         return fileResourceStore.getByUid( uids );
     }
 
-    @Transactional
     @Override
     public List<FileResource> getOrphanedFileResources( )
     {
@@ -149,38 +135,39 @@
             .stream().filter( IS_ORPHAN_PREDICATE ).collect( Collectors.toList() );
     }
 
+    @Transactional
     @Override
     public String saveFileResource( FileResource fileResource, File file )
     {
         fileResource.setStorageStatus( FileResourceStorageStatus.PENDING );
-        fileResourceStore.saveInTransaction( fileResource );
+        fileResourceStore.save( fileResource );
 
-        ListenableFuture<String> saveContentTask =
+        final ListenableFuture<String> saveContentTask =
             scheduler.executeTask( () -> fileResourceContentStore.saveFileResourceContent( fileResource, file ) );
 
-        String uid = fileResource.getUid();
-
-        saveContentTask.addCallback( uploadCallbackProvider.getCallback( uid ) );
+        final String uid = fileResource.getUid();
+
+        // Ensures callback is registered after this transaction is committed.
+        // Works as a safeguard against the unlikely race condition which
+        // could occur when the callback is executed before the FileResource
+        // object has been written to the db. We should consider exposing the
+        // locking mechanisms of Hibernate which would offer a cleaner solution
+        // to this very issue.
+
+        TransactionSynchronizationManager.registerSynchronization(
+            new TransactionSynchronizationAdapter()
+            {
+                @Override
+                public void afterCommit()
+                {
+                    super.afterCommit();
+                    saveContentTask.addCallback( uploadCallback.newInstance( uid ) );
+                }
+            }
+        );
 
         return uid;
     }
-//
-//    @Transactional
-//    @Override
-//    public String saveFileResource( FileResource fileResource, File file )
-//    {
-//        fileResource.setStorageStatus( FileResourceStorageStatus.PENDING );
-//        fileResourceStore.save( fileResource );
-//
-//        ListenableFuture<String> saveContentTask =
-//            scheduler.executeTask( () -> fileResourceContentStore.saveFileResourceContent( fileResource, file ) );
-//
-//        String uid = fileResource.getUid();
-//
-//        saveContentTask.addCallback( uploadCallbackProvider.getCallback( uid ) );
-//
-//        return uid;
-//    }
 
     @Transactional
     @Override
@@ -214,6 +201,7 @@
         return fileResourceStore.getByUid( uid ) != null;
     }
 
+    @Transactional
     @Override
     public void updateFileResource( FileResource fileResource )
     {
@@ -232,43 +220,4 @@
 
         return fileResourceContentStore.getSignedGetContentUri( fileResource.getStorageKey() );
     }
-
-    // -------------------------------------------------------------------------
-    // Supportive methods
-    // -------------------------------------------------------------------------
-
-    /**
-     * Ensures that the storageStatus of the FileResource is correct.
-     * If it has been pending for more than two minutes existance of the content is
-     * 'double checked' in the file store.
-     * If the content is actually present the storageStatus is updated to reflect this.
-     *
-     * TODO Should not be necessary but needs to be in place as a fail-safe due to mysterious issues with saving.
-     */
-    private FileResource ensureStorageStatus( FileResource fileResource )
-    {
-        if ( FileResourceStorageStatus.PENDING == fileResource.getStorageStatus() )
-        {
-            Duration pendingDuration = new Duration( new DateTime( fileResource.getLastUpdated() ), DateTime.now() );
-
-            if ( pendingDuration.isLongerThan( LONG_STORAGE_DURATION_TIME_DELTA ) )
-            {
-                // Upload has been running for 2+ minutes and is still PENDING.
-                // Check if content has actually been stored and correct to STORED if this is the case.
-
-                boolean contentIsStored = fileResourceContentStore.fileResourceContentExists( fileResource.getStorageKey() );
-
-                if ( contentIsStored )
-                {
-                    // Status is PENDING but content is actually stored. Fix it.
-                    fileResource.setStorageStatus( FileResourceStorageStatus.STORED );
-                    fileResourceStore.update( fileResource );
-                    log.warn( "Corrected issue: File resource '" + fileResource.getUid() +
-                        "' had storageStatus PENDING but content was fully stored." );
-                }
-            }
-        }
-
-        return fileResource;
-    }
 }

=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceUploadCallback.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceUploadCallback.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceUploadCallback.java	2015-10-30 14:43:11 +0000
@@ -0,0 +1,93 @@
+package org.hisp.dhis.fileresource;
+
+/*
+ * Copyright (c) 2004-2015, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hisp.dhis.common.IdentifiableObjectManager;
+import org.joda.time.DateTime;
+import org.joda.time.Period;
+import org.joda.time.format.PeriodFormat;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.concurrent.ListenableFutureCallback;
+
+/**
+ * @author Halvdan Hoem Grelland
+ */
+public class FileResourceUploadCallback
+{
+    Log log = LogFactory.getLog( FileResourceUploadCallback.class );
+
+    @Autowired
+    private IdentifiableObjectManager idObjectManager;
+
+    public ListenableFutureCallback<String> newInstance( String fileResourceUid )
+    {
+        return new ListenableFutureCallback<String>()
+        {
+            DateTime startTime = DateTime.now();
+
+            @Override
+            public void onFailure( Throwable ex )
+            {
+                log.error( "Saving content for file resource '" + fileResourceUid + "' failed", ex );
+
+                FileResource fileResource = idObjectManager.get( FileResource.class, fileResourceUid );
+
+                if ( fileResource != null )
+                {
+                    log.info( "File resource '" + fileResource.getUid() + "' storageStatus set to FAILED." );
+
+                    fileResource.setStorageStatus( FileResourceStorageStatus.FAILED );
+                    idObjectManager.update( fileResource );
+                }
+            }
+
+            @Override
+            public void onSuccess( String result )
+            {
+                Period timeDiff = new Period( startTime, DateTime.now() );
+
+                log.info( "File stored with key: '" + result + "'. Upload finished in " + timeDiff.toString( PeriodFormat.getDefault() ) );
+
+                FileResource fileResource = idObjectManager.get( FileResource.class, fileResourceUid );
+
+                if ( result != null && fileResource != null )
+                {
+                    fileResource.setStorageStatus( FileResourceStorageStatus.STORED );
+                    idObjectManager.update( fileResource );
+                }
+                else
+                {
+                    log.error( "Conflict: content was stored but FileResource with uid '" + fileResourceUid + "' could not be found." );
+                }
+            }
+        };
+    }
+}

=== removed file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceUploadCallbackProvider.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceUploadCallbackProvider.java	2015-10-26 16:01:20 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceUploadCallbackProvider.java	1970-01-01 00:00:00 +0000
@@ -1,96 +0,0 @@
-package org.hisp.dhis.fileresource;
-
-/*
- * Copyright (c) 2004-2015, University of Oslo
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * Neither the name of the HISP project nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.hisp.dhis.common.IdentifiableObjectManager;
-import org.joda.time.DateTime;
-import org.joda.time.Period;
-import org.joda.time.format.PeriodFormat;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.concurrent.ListenableFutureCallback;
-
-/**
- * @author Halvdan Hoem Grelland
- */
-public class FileResourceUploadCallbackProvider
-{
-    Log log = LogFactory.getLog( FileResourceUploadCallbackProvider.class );
-
-    @Autowired
-    private IdentifiableObjectManager idObjectManager;
-
-    public ListenableFutureCallback<String> getCallback( String fileResourceUid )
-    {
-        return new ListenableFutureCallback<String>()
-        {
-            DateTime startTime = DateTime.now();
-
-            @Transactional
-            @Override
-            public void onFailure( Throwable ex )
-            {
-                log.error( "Saving content for file resource '" + fileResourceUid + "' failed", ex );
-
-                FileResource fileResource = idObjectManager.get( FileResource.class, fileResourceUid );
-
-                if ( fileResource != null )
-                {
-                    log.info( "File resource '" + fileResource.getUid() + "' storageStatus set to FAILED." );
-
-                    fileResource.setStorageStatus( FileResourceStorageStatus.FAILED );
-                    idObjectManager.update( fileResource );
-                }
-            }
-
-            @Transactional
-            @Override
-            public void onSuccess( String result )
-            {
-                Period timeDiff = new Period( startTime, DateTime.now() );
-
-                log.info( "File stored with key: '" + result + "'. Upload finished in " + timeDiff.toString( PeriodFormat.getDefault() ) );
-
-                FileResource fileResource = idObjectManager.get( FileResource.class, fileResourceUid );
-
-                if ( result != null && fileResource != null )
-                {
-                    fileResource.setStorageStatus( FileResourceStorageStatus.STORED );
-                    idObjectManager.update( fileResource );
-                }
-                else
-                {
-                    log.error( "Conflict: content was stored but FileResource with uid '" + fileResourceUid + "' could not be found." );
-                }
-            }
-        };
-    }
-}

=== removed directory 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/hibernate'
=== removed file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/hibernate/HibernateFileResourceStore.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/hibernate/HibernateFileResourceStore.java	2015-10-26 16:01:20 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/hibernate/HibernateFileResourceStore.java	1970-01-01 00:00:00 +0000
@@ -1,50 +0,0 @@
-package org.hisp.dhis.fileresource.hibernate;
-
-/*
- * Copyright (c) 2004-2015, University of Oslo
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * Neither the name of the HISP project nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-import org.hisp.dhis.common.hibernate.HibernateIdentifiableObjectStore;
-import org.hisp.dhis.fileresource.FileResource;
-import org.hisp.dhis.fileresource.FileResourceStore;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * @author Halvdan Hoem Grelland
- */
-public class HibernateFileResourceStore
-    extends HibernateIdentifiableObjectStore<FileResource>
-    implements FileResourceStore
-{
-    @Transactional( propagation = Propagation.REQUIRES_NEW )
-    @Override
-    public void saveInTransaction( FileResource fileResource )
-    {
-        save( fileResource );
-    }
-}

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2015-10-30 10:18:36 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2015-10-30 14:43:11 +0000
@@ -570,12 +570,7 @@
     <property name="sessionFactory" ref="sessionFactory" />
   </bean>
 
-  <!--<bean id="org.hisp.dhis.fileresource.FileResourceStore" class="org.hisp.dhis.common.hibernate.HibernateIdentifiableObjectStore">-->
-    <!--<property name="clazz" value="org.hisp.dhis.fileresource.FileResource" />-->
-    <!--<property name="sessionFactory" ref="sessionFactory" />-->
-  <!--</bean>-->
-
-  <bean id="org.hisp.dhis.fileresource.FileResourceStore" class="org.hisp.dhis.fileresource.hibernate.HibernateFileResourceStore">
+  <bean id="org.hisp.dhis.fileresource.FileResourceStore" class="org.hisp.dhis.common.hibernate.HibernateIdentifiableObjectStore">
     <property name="clazz" value="org.hisp.dhis.fileresource.FileResource" />
     <property name="sessionFactory" ref="sessionFactory" />
   </bean>
@@ -588,9 +583,7 @@
 
   <!-- File resource tasks -->
 
-  <bean id="org.hisp.dhis.fileresource.FileResourceUploadCallbackProvider"
-    class="org.hisp.dhis.fileresource.FileResourceUploadCallbackProvider"
-    scope="prototype" />
+  <bean id="org.hisp.dhis.fileresource.FileResourceUploadCallback" class="org.hisp.dhis.fileresource.FileResourceUploadCallback" />
 
   <bean id="org.hisp.dhis.fileresource.FileResourceCleanUpTask"
     class="org.hisp.dhis.fileresource.FileResourceCleanUpTask"
@@ -602,7 +595,7 @@
     <property name="fileResourceStore" ref="org.hisp.dhis.fileresource.FileResourceStore" />
     <property name="fileResourceContentStore" ref="org.hisp.dhis.fileresource.FileResourceContentStore" />
     <property name="scheduler" ref="scheduler" />
-    <property name="uploadCallbackProvider" ref="org.hisp.dhis.fileresource.FileResourceUploadCallbackProvider" />
+    <property name="uploadCallback" ref="org.hisp.dhis.fileresource.FileResourceUploadCallback" />
     <property name="fileResourceCleanUpTask" ref="org.hisp.dhis.fileresource.FileResourceCleanUpTask" />
   </bean>