Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot instatiate view with type parameter #8371

Closed
javier-godoy opened this issue May 18, 2020 · 5 comments · Fixed by #8431
Closed

Cannot instatiate view with type parameter #8371

javier-godoy opened this issue May 18, 2020 · 5 comments · Fixed by #8431

Comments

@javier-godoy
Copy link
Contributor

Vaadin 14.1.28. Starting an application that contains the following view fails with the exception listed below. If the view doesn't implement HasUrlParameter the application starts correctly.

@Route("TypeParameterExample")
public class TypeParameterExample<T> extends VerticalLayout
 implements HasUrlParameter<String> {

	@Override
	public void setParameter(BeforeEvent event, String parameter) { }
}
java.lang.NullPointerException: null
	at com.googlecode.gentyref.GenericTypeReflector.erase(GenericTypeReflector.java:44) ~[gentyref-1.2.0.vaadin1.jar:na]
	at com.vaadin.flow.router.ParameterDeserializer.isAnnotatedParameter(ParameterDeserializer.java:200) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.server.startup.RouteTarget.addTargetByType(RouteTarget.java:110) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.server.startup.RouteTarget.<init>(RouteTarget.java:83) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.router.internal.ConfigureRoutes.lambda$setRoute$0(ConfigureRoutes.java:136) ~[flow-server-2.1.9.jar:2.1.9]
	at java.util.HashMap.computeIfAbsent(HashMap.java:1126) ~[na:1.8.0_92]
	at com.vaadin.flow.router.internal.ConfigureRoutes.setRoute(ConfigureRoutes.java:135) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.router.internal.AbstractRouteRegistry.addRouteToConfiguration(AbstractRouteRegistry.java:366) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.router.internal.AbstractRouteRegistry.lambda$setRoute$83b15968$1(AbstractRouteRegistry.java:308) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.router.internal.AbstractRouteRegistry.configure(AbstractRouteRegistry.java:95) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.router.internal.AbstractRouteRegistry.setRoute(AbstractRouteRegistry.java:307) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.server.startup.ApplicationRouteRegistry.setRoute(ApplicationRouteRegistry.java:235) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.router.RouteConfiguration.setAnnotatedRoute(RouteConfiguration.java:225) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.spring.VaadinServletContextInitializer$RouteServletContextListener.setAnnotatedRoutes(VaadinServletContextInitializer.java:204) ~[vaadin-spring-12.1.4.jar:na]
	at com.vaadin.flow.spring.VaadinServletContextInitializer$RouteServletContextListener.lambda$contextInitialized$785b83af$1(VaadinServletContextInitializer.java:179) ~[vaadin-spring-12.1.4.jar:na]
	at com.vaadin.flow.router.internal.AbstractRouteRegistry.update(AbstractRouteRegistry.java:106) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.router.RouteConfiguration.update(RouteConfiguration.java:203) ~[flow-server-2.1.9.jar:2.1.9]
	at com.vaadin.flow.spring.VaadinServletContextInitializer$RouteServletContextListener.contextInitialized(VaadinServletContextInitializer.java:179) ~[vaadin-spring-12.1.4.jar:na]
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4680) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5143) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_92]
	at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) [na:1.8.0_92]
	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:841) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_92]
	at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) [na:1.8.0_92]
	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.StandardService.startInternal(StandardService.java:421) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:932) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.apache.catalina.startup.Tomcat.start(Tomcat.java:456) [tomcat-embed-core-9.0.22.jar:9.0.22]
	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:105) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:86) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:416) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:180) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:180) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:153) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) [spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:743) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:390) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1214) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1203) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.vaadin.ext.javier_v14.spring.Application.main(Application.java:15) [classes/:na]
@Legioth
Copy link
Member

Legioth commented May 20, 2020

While I agree that this case shouldn't cause an exception, I'm still a bit curious to understand what the purpose is for having an unbound type parameter in a concrete view?

@javier-godoy
Copy link
Contributor Author

@Legioth, I understand they implemented the view using reflection. Different kinds of objects are retrieved depending on a parameter. This approach provides some weak type-safety, for instance, when implementing a helper method that returns one of those objects or receives it as a parameter (even though the actual type after erasure is Object).

@Legioth
Copy link
Member

Legioth commented May 20, 2020

That would be the case if you'd bind the type parameter, i.e. something like this:

@Route("BoundParameterExample")
public class BoundParameterExample extends TypeParameterExample<MyType> {
}

In this example, the type parameter is unbound which basically means that it serves no purpose for the view itself. There might be a benefit from outside code that interacts with it, e.g. something like this:

TypeParameterExample<MyType> currentView = (TypeParameterExample<MyType> ) getCurrentView();
MyType value = currentView.getValue();

@javier-godoy
Copy link
Contributor Author

It can also be used internally (maybe not a strong case, I agree):

@Route("TypeParameterExample")
public class TypeParameterExample<T> extends VerticalLayout implements HasUrlParameter<String> {

	private String entityType; 
        private Grid<T> grid = new Grid<>();	
        TypeParameterExample(){
		add(grid);
		grid.addColumn(this::getName).setHeader("name");
		grid.addColumn(this::getValue).setHeader("value");
		grid.asSingleSelect().addValueChangeListener(ev->{
			if (ev.getValue()!=null) Notification.show("Selected: "+getName(ev.getValue()));
		});
        }

	@Override
	public void setParameter(BeforeEvent event, String parameter) { 
		entityType=parameter;		
		grid.setItems(loadItems());
        }

	private Stream<T> loadItems() {
		//some custom logic for retrieving the items depending on the entityType
	}
	
	private String getName(T item) {
		//some custom logic for reflecting "name" depending on the actual type
	}
	
	private String getValue(T item) {
		//some custom logic for reflecting "value" depending on the actual type
	}

}

@denis-anisimov
Copy link
Contributor

Just to clarify : any example here is invalid if it's a standalone.

TypeParameterExample<T> can be used only if you instantiate the object by yourself.
Route is instantiated via reflection which has no any knowledge which parameter to use.

The only correct usage here is subclassing of TypeParameterExample<T> with a concrete parameter type. Route is inherited so it makes sense to have it.

But TypeParameterExample can't be used as a route target itself and should be filtered out from routes at the stage of checking route targets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

4 participants