vaadin/vaadin-upload

Upload constructor thows an exception nested by NullPointerException

ray-mints opened this issue · 7 comments

I use Vaadin 10.
So I have a MainUI class that extends SplitLayout. Inside I have object of WorkLayout (WorkLayout extends VerticalLayout). WorkLayout has few buttons in a constructor and an Upload.
If I write @SpringComponent at the top of my MainUI - my spring boot project won't start, unless I delete Upload or put it in try catch. (If I put it in a try catch - it won't create Upload component on the screen at all and will just do whatever is in catch).
After trying to change things in my code - I figured that the Upload upload = new Upload(); is causing it.
(If i change to Upload upload = new Upload(buffer); - the problem is the same. In fact, even a single line with new Upload(); cause the error.)
Here is the error:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-11 14:20:22.219 ERROR 7985 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainUI' defined in file [/home/username/work/myproject/target/classes/com/example/myproject/frontend/main/MainUI.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.myproject.frontend.main.MainUI]: Constructor threw exception; nested exception is java.lang.NullPointerException
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1236)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1135)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:541)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243)
	at com.example.myproject.MyProjectApplication.main(MyProjectApplication.java:9)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.myproject.frontend.main.MainUI]: Constructor threw exception; nested exception is java.lang.NullPointerException
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:182)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1228)
	... 22 common frames omitted
Caused by: java.lang.NullPointerException: null
	at com.vaadin.flow.server.communication.StreamRequestHandler.generateURI(StreamRequestHandler.java:135)
	at com.vaadin.flow.server.StreamResourceRegistry.getURI(StreamResourceRegistry.java:131)
	at com.vaadin.flow.server.StreamResourceRegistry.getURI(StreamResourceRegistry.java:126)
	at com.vaadin.flow.internal.nodefeature.ElementAttributeMap.setResource(ElementAttributeMap.java:124)
	at com.vaadin.flow.dom.impl.BasicElementStateProvider.setAttribute(BasicElementStateProvider.java:316)
	at com.vaadin.flow.dom.Element.setAttribute(Element.java:342)
	at com.vaadin.flow.component.upload.Upload.<init>(Upload.java:75)
	at com.example.myproject.frontend.main.sublayouts.WorkLayout.<init>(WorkLayout.java:51)
	at com.example.myproject.frontend.main.MainUI.<init>(MainUI.java:18)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:170)
	... 24 common frames omitted

I've tried to create in another project - the result is the same.
Here is the code:

@Route("test")
@SpringComponent
public class Test extends VerticalLayout {
  public Test(){
    add(new Label("label"));
    Upload upload = new Upload();
  }
}

Here is the error:

rg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'test' defined in file [/home/username/work/flow-spring-tutorial-master/target/classes/org/vaadin/spring/tutorial/Test.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.vaadin.spring.tutorial.Test]: Constructor threw exception; nested exception is java.lang.NullPointerException
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1236) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1135) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:541) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869) ~[spring-context-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243) [spring-boot-2.0.2.RELEASE.jar:2.0.2.RELEASE]
	at org.vaadin.spring.tutorial.TutorialApplication.main(TutorialApplication.java:17) [classes/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.vaadin.spring.tutorial.Test]: Constructor threw exception; nested exception is java.lang.NullPointerException
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:182) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1228) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	... 17 common frames omitted
Caused by: java.lang.NullPointerException: null
	at com.vaadin.flow.server.communication.StreamRequestHandler.generateURI(StreamRequestHandler.java:135) ~[flow-server-1.0.0.rc1.jar:na]
	at com.vaadin.flow.server.StreamResourceRegistry.getURI(StreamResourceRegistry.java:131) ~[flow-server-1.0.0.rc1.jar:na]
	at com.vaadin.flow.server.StreamResourceRegistry.getURI(StreamResourceRegistry.java:126) ~[flow-server-1.0.0.rc1.jar:na]
	at com.vaadin.flow.internal.nodefeature.ElementAttributeMap.setResource(ElementAttributeMap.java:125) ~[flow-server-1.0.0.rc1.jar:na]
	at com.vaadin.flow.dom.impl.BasicElementStateProvider.setAttribute(BasicElementStateProvider.java:316) ~[flow-server-1.0.0.rc1.jar:na]
	at com.vaadin.flow.dom.Element.setAttribute(Element.java:343) ~[flow-server-1.0.0.rc1.jar:na]
	at com.vaadin.flow.component.upload.Upload.<init>(Upload.java:75) ~[vaadin-upload-flow-1.0.0.rc1.jar:na]
	at org.vaadin.spring.tutorial.Test.<init>(Test.java:14) ~[classes/:na]
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_171]
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_171]
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_171]
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_171]
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:170) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	... 19 common frames omitted


Process finished with exit code 1

Everything works fine without Upload. Is there a way to bypass it and use Upload component with Spring Boot?

Ok. I found one cheap and dirty workaround (for those who really stuck and need things to be done).
I don't like this solution, but here it is:

@Route("test")
@SpringComponent
public class Test extends VerticalLayout {
  public Test(){
    Button crapButton = new Button("press to get an upload button");
    add(crapButton);
    crapButton.addClickListener(buttonClickEvent -> {
      addUpload();
      crapButton.setVisible(false);
    });
  }

  void addUpload(){
    Upload upload = new Upload();
    add(upload);
  }
}

Seems like the problem is that Element.setAttribute(String, AbstractStreamResource) assumes UI.getCurrent() is available. The logic triggered through that method should instead probably defer any logic until the element is attached and explicitly find the UI its attached to instead of relying on getCurrent().

Closing. Seems to work on V14.4

I just had the same issue. When you annotate a component just with @SpringComponent (without a scope), it's a singleton and will be preinitialized by Spring. You need to ensure that Upload is only used by beans with at least @UIScope to prevent the NPE caused by UI.getCurrent().

According to the documentation, @Route should imply a prototype scope.

All Flow routing components (@Route, RouterLayout or HasErrorParameter) are initialized by Spring, even without explicitly adding the @SpringComponent annotation. Without any annotations, the components act like a bean in prototype scope.

But in my experience, this isn't true (don't know if this is a bug in the documentation or the implementation): You need an explicit @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) if you really want a prototype.

EDIT: Maybe I've missinterpreted "Without any annotations ..." and it means "without @SpringComponent ...".

@chkpnt Could you please submit a new issue?

Mh, I don't think there is a bug with vaadin-upload. I just wanted to add the hint for other developers stumbling over this issue, that a @SpringComponent might need a @UIScope or something else like in this case.