NPE при повторном помещении файла в хранилище

Добрый день!
Есть следующий контроллер:

@Subscribe
    public void onInit(InitEvent event) {
        uploadFile.addFileUploadSucceedListener(e -> {
            notifications.create().withCaption("Файл загружен").withType(Notifications.NotificationType.TRAY).show();
        });
        uploadFile.addFileUploadErrorListener(e -> {
            notifications.create().withCaption("Ошибка загрузки файла").withType(Notifications.NotificationType.ERROR).show();
        });
    }




    public void onImportButtonClick() {
        if (uploadFile.getFileName() == null) {
            notifications.create().withCaption("Файл не загружен").withType(Notifications.NotificationType.TRAY).show();
        } else {
            UUID fileId = uploadFile.getFileId();
            String fileName = uploadFile.getFileName();
            File file = fileUploadingAPI.getFile(fileId);
            BackgroundWorkWindow.show(
                    new BackgroundTask<Void, File>(3600, this) {
                        @Override
                        public File run(TaskLifeCycle<Void> taskLifeCycle) {
                            FileDescriptor fileDescriptor = fileUploadingAPI.getFileDescriptor(fileId, fileName);

                            try {
                                ImportInfo importInfo = metadata.create(ImportInfo.class);
                                importInfo.setImportType(importTypesField.getValue());
                                importInfo.setFile(dataManager.commit(fileDescriptor));                  
                                fileUploadingAPI.putFileIntoStorage(fileId, fileDescriptor);

                                dataManager.commit(importInfo);
                            } catch (FileStorageException e) {
                                notifications.create().withCaption("Ошибка при импорте файла").withType(Notifications.NotificationType.ERROR).show();
                                throw new RuntimeException(e);
                            } catch (IOException e){
                                e.printStackTrace();
                            }
                            return null;
                        }

                        @Override
                        public boolean handleException(Exception ex) {
                            notifications.create().withCaption("Ошибка при импорте файла").withType(Notifications.NotificationType.ERROR).show();
                            return super.handleException(ex);
                        }

                        @Override
                        public void done(File result) {
                            super.done(result);
                            if(result == null){
                                notifications.create().withCaption("Импорт успешно выполнен").withType(Notifications.NotificationType.TRAY).show();
                                importInfoesDl.load();
                            }
                        }
                    },
                    "Обработка файла",
                    "Это может занять некоторое время",
                    true);
        }
    }

При загрузке файла в uploadField и первом нажати на кнопку importButton, все хорошо. Но затем, если сразу же нажать еще раз кнопку importButton, то вылетает ошибка:

java.lang.NullPointerException: null
	at com.haulmont.cuba.core.app.DataManagerBean.commit(DataManagerBean.java:152) ~[cuba-core-7.1.2.jar:7.1.2]
	at com.haulmont.cuba.core.app.DataServiceBean.commit(DataServiceBean.java:41) ~[cuba-core-7.1.2.jar:7.1.2]
	at jdk.internal.reflect.GeneratedMethodAccessor359.invoke(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at com.haulmont.cuba.core.sys.ServiceInterceptor.aroundInvoke(ServiceInterceptor.java:116) ~[cuba-core-7.1.2.jar:7.1.2]
	at jdk.internal.reflect.GeneratedMethodAccessor149.invoke(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at com.sun.proxy.$Proxy297.commit(Unknown Source) ~[na:na]
	at jdk.internal.reflect.GeneratedMethodAccessor358.invoke(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at com.haulmont.cuba.core.sys.remoting.LocalServiceInvokerImpl.invoke(LocalServiceInvokerImpl.java:94) ~[cuba-core-7.1.2.jar:7.1.2]
	at com.haulmont.cuba.web.sys.remoting.LocalServiceProxy$LocalServiceInvocationHandler.invoke(LocalServiceProxy.java:154) ~[na:na]
	at com.sun.proxy.$Proxy38.commit(Unknown Source) ~[na:na]
	at com.haulmont.cuba.client.sys.DataManagerClientImpl.commit(DataManagerClientImpl.java:100) ~[na:na]
	at com.haulmont.cuba.client.sys.DataManagerClientImpl.commit(DataManagerClientImpl.java:105) ~[na:na]
	at com.haulmont.cuba.client.sys.DataManagerClientImpl.commit(DataManagerClientImpl.java:128) ~[na:na]
	at com.company.enerstroymain.web.screens.importinfo.ImportInfoBrowse$1.run(ImportInfoBrowse.java:83) ~[na:na]
	at com.company.enerstroymain.web.screens.importinfo.ImportInfoBrowse$1.run(ImportInfoBrowse.java:75) ~[na:na]
	at com.haulmont.cuba.gui.backgroundwork.LocalizedTaskWrapper.run(LocalizedTaskWrapper.java:57) ~[na:na]
	at com.haulmont.cuba.web.gui.executors.impl.WebBackgroundWorker$WebTaskExecutor.call(WebBackgroundWorker.java:205) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
	at com.haulmont.cuba.web.gui.executors.impl.WebBackgroundWorker$WebTaskExecutor.lambda$startExecution$1(WebBackgroundWorker.java:376) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
13:28:53.081 ERROR c.h.c.gui.executors.BackgroundWorker    - Exception occurred in background task
com.haulmont.cuba.core.global.RemoteException: null
	at com.haulmont.cuba.core.sys.ServiceInterceptor.aroundInvoke(ServiceInterceptor.java:124) ~[na:na]
	at jdk.internal.reflect.GeneratedMethodAccessor149.invoke(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at com.sun.proxy.$Proxy297.commit(Unknown Source) ~[na:na]
	at jdk.internal.reflect.GeneratedMethodAccessor358.invoke(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at com.haulmont.cuba.core.sys.remoting.LocalServiceInvokerImpl.invoke(LocalServiceInvokerImpl.java:94) ~[na:na]
	at com.haulmont.cuba.web.sys.remoting.LocalServiceProxy$LocalServiceInvocationHandler.invoke(LocalServiceProxy.java:154) ~[cuba-web-7.1.2.jar:7.1.2]
	at com.sun.proxy.$Proxy38.commit(Unknown Source) ~[na:na]
	at com.haulmont.cuba.client.sys.DataManagerClientImpl.commit(DataManagerClientImpl.java:100) ~[cuba-client-7.1.2.jar:7.1.2]
	at com.haulmont.cuba.client.sys.DataManagerClientImpl.commit(DataManagerClientImpl.java:105) ~[cuba-client-7.1.2.jar:7.1.2]
	at com.haulmont.cuba.client.sys.DataManagerClientImpl.commit(DataManagerClientImpl.java:128) ~[cuba-client-7.1.2.jar:7.1.2]
	at com.company.enerstroymain.web.screens.importinfo.ImportInfoBrowse$1.run(ImportInfoBrowse.java:83) ~[ROOT-web-0.5.0.0-SNAPSHOT.jar:na]
	at com.company.enerstroymain.web.screens.importinfo.ImportInfoBrowse$1.run(ImportInfoBrowse.java:75) ~[ROOT-web-0.5.0.0-SNAPSHOT.jar:na]
	at com.haulmont.cuba.gui.backgroundwork.LocalizedTaskWrapper.run(LocalizedTaskWrapper.java:57) ~[cuba-gui-7.1.2.jar:7.1.2]
	at com.haulmont.cuba.web.gui.executors.impl.WebBackgroundWorker$WebTaskExecutor.call(WebBackgroundWorker.java:205) ~[cuba-web-7.1.2.jar:7.1.2]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
	at com.haulmont.cuba.web.gui.executors.impl.WebBackgroundWorker$WebTaskExecutor.lambda$startExecution$1(WebBackgroundWorker.java:376) ~[cuba-web-7.1.2.jar:7.1.2]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

С жалобой на строчку

importInfo.setFile(dataManager.commit(fileDescriptor));

Почему такое происходит?

Добрый день,

Давайте посмотрим в исходный код класса FileUploading (который открыт и доступен всем для изучения):

    @Override
    public void putFileIntoStorage(UUID fileId, FileDescriptor fileDescr) throws FileStorageException {
        try {
            uploadFileIntoStorage(fileId, fileDescr, null);
        } catch (InterruptedException e) {
            // should never happen
            throw new FileStorageException(FileStorageException.Type.IO_EXCEPTION, fileDescr.getId().toString());
        }

        deleteFile(fileId); // !!!!!!!!!!!!
    }
    @Override
    public FileDescriptor getFileDescriptor(UUID fileId, String name) {
        File file = getFile(fileId);
        if (file == null) {
            return null; // !!!!!!!!!
        }
        Metadata metadata = AppBeans.get(Metadata.NAME);

        FileDescriptor fDesc = metadata.create(FileDescriptor.class);

        fDesc.setSize(file.length());
        fDesc.setExtension(FilenameUtils.getExtension(name));
        fDesc.setName(name);
        fDesc.setCreateDate(timeSource.currentTimestamp());

        return fDesc;
    }

Думаю, ответ на вопрос “почему возникает NPE при повторном вызове getFileDescriptor() после вызова putFileIntoStorage()” понятен.

1 симпатия

То есть если я хочу 2 раза загрузить один и тот же файл, мне придется 2 раза загружать его в uploadField?

Нет, зачем загружать два раза.
После первого вызова
dataManager.commit(fileDescriptor)
и
fileUploadingAPI.putFileIntoStorage(fileId, fileDescriptor);

Ваш загруженный файл оказывается в хранилище файлов и с привязанным к нему сохраненным FileDescriptor.

После этого вы можете делать с файлом все что угодно, используя экземпляр FileDescriptor и Spring-бины FileStorageService, FileStorageAPI.
Если по каким-то причинам вам нужно будет склонировать этот файл в файловом хранилище, то используйте методы com.haulmont.cuba.core.app.FileStorageAPI#openStream и com.haulmont.cuba.core.app.FileStorageAPI#saveStream

Только лучше это реализовать на среднем слое, чтобы не гонять поток данных туда-сюда из core в web и обратно.

С другой стороны, вы можете использовать один и тот же экземпляр FileDescriptor многократно, привязывая его к нескольким сущностям (у вас ImportInfo). Только связь ImportInfo.file тогда должна быть many-to-one.