vaadin / flow

Vaadin Flow is a Java framework binding Vaadin web components to Java. This is part of Vaadin 10+.
Apache License 2.0
622 stars 167 forks source link

Make sure that static web resources are available in OSGi #4230

Closed denis-anisimov closed 3 months ago

denis-anisimov commented 6 years ago

This is similar to #4229 but the static web resources here are not handle by the VaadinServlet. They are handled by the web server directly. Those resources are frontend resources like CSS files, HTML files (template files) , JS files. Such files are plain web resources in the application and should be somehow exposed in OSGi.

There can be different ways to do this but it should be simple enough for the developer. Similar things are done here https://github.com/mmerruko/framework/commit/687a9009cf3bd2852181848c0c466aac5fe8f5d3 bt it might be this is not applicable at all here.

What needs to be done: create an application, deploy it to OSGi and make sure the static web resources are available there. If the custom way to do this is too complicated or may be improved then provide a way which can be used to simplify this in OSGi environment (see mentioned commit in FW).

denis-anisimov commented 6 years ago

This is kind of blocked by the #4227 but it doesn't have to wait when the task will be completely done (and merged). The #4227 should give a way to check whether an application can be used in OSGi. At least there should be a simple way to deploy an application and check the result. Otherwise this needs to be done every time when any OSGi related task is done and this is waste of time to reinvent the testing path every time.

denis-anisimov commented 6 years ago

I have some issues with static resources right now.

For the automation tests task I modified root-context tests so that it's OSGi bundle (including UI classes only) : it's a jar file (ui qualifier).

There is webapp folder which (I suppose) won't be properly served inside jar bundle.

So I configured the builder-helper plugin to use resources inside this folder as a part of META-INF/resources. So far so good. The resources are available inside META-INF/resources but they are not served as static resources. That's the very first issue. Should they be served automatically? Servlet specification defines META-INF/resources folder for the resources inside jar files. But the bundle jar file is not a part of WAR file. It's standalone OSGi bundle.

So as a result servlet context which is used to get the resource in the end doesn't know about static resources inside META-INF/resources.

OK, may be it's how it's supposed to work. To be able to expose those resource I register then via HttpService (which is created inside the bundle because Jetty OSGi bundle doesn't give any HttpService by the way). Now resources are available via HTTP.

But we are using servlet context to get resource content by path inside template parser (DefaultTemplateParser uses service.getResourceAsStream which delegates to VaadinServletService::getResourceInServletContextOrWebJarAsStream and its implementation uses the ServletContext::getResourceAsStream method to get the resource content).

And since the servlet context knows nothing about static resources it returns null and the parser is unable to parse the template content.

So it's not enough to expose static resources via HTTP but they also should be available via servlet context (or we need to change our code somehow).

To be able to solve this issue I added also resources from the webapp folder into the root folder.

In the resulting configuration Jetty allows to get resources from the bundle jar and now resources becomes available by their correct path (previously path frontend/something had to be resolved via META-INF/resources/frontend/something, now it's available directly as frontend/something which makes servlet context find it and also via META-INF/resources/frontend/something which makes it's exposed via HTTP).

But this may be a result of main servlet mapping which is not mapped to the / but to the view. I'm not sure that it will be possible with servlet mapped to the root. And anyway this config is too cumbersome and inconvenient.

I will keep it for now for tests but once this ticket is fixed we should adjust tests accordingly.

Sandared commented 6 years ago

I'm not sure, but I think in my old Vaadin8 integration I had a similar problem. Back then, I implemented a BundleTracker (again ;) ) that tracked the bundles with resources and added them via HttpService and a corresponding HttpContext, that delegated loading of resources to the bundle that contained the resources.

The BundleTracker code that adds the resource: https://github.com/Sandared/vaadin8integration/blob/master/de.modularco.vaadin.integration.impl/src/de/modularco/vaadin/integration/impl/OSGiResourceExtender.xtend#L69-L72

The HttpContext that delegates resource loading: https://github.com/Sandared/vaadin8integration/blob/master/de.modularco.vaadin.integration.impl/src/de/modularco/vaadin/integration/impl/OSGiResourceExtender.xtend#L127-L146

The code is unfortunately in written in Xtend but not so different from Java, so I hope it's readable.

I hope this helps.

denis-anisimov commented 6 years ago

Thank you for the examples.

But in fact this is already done almost in the same way in our tests.

The problem is different.

As I mentioned in the comment : we have DefaultTemplateParser on the server side and it parses the template file. It doesn't use HTTP to access resources. It uses ServletContext::getResourceAsStream which has no relation to HTTP and it access resources "locally" ( like ClassLoader ) : get InputStream by resource path. And this is an issue. Normally web server is specifically configured to treat some path for static web resources. If it's configured (e.g. in Jetty resourceBase can be used for that) then ServletContext is able to resolve resources by path inside the configured folders and it returns their content via API method. This is exactly what doesn't happen here.

Sandared commented 6 years ago

Ok I got it. I mistook HttpContext for ServletContext. Nevertheless, the OSGi Compendium Specification states that all servlets registered with the same HttpContext also have the same ServletContext as described here

If the Servlet whose ServletContext is asked by the template processor for the resource is registered with the same HttpContext as the resources are, then it should be able to find and return the registered resource as stream.

If I'm still getting you wrong, then just ignore this comment. I'm not that deep into Servlets and web stuff as you probably are ;)

QNENet commented 6 years ago

A good example in using Vaadin in an OSGi environment can be found at https://github.com/opensecuritycontroller/osc-core/tree/master/osc-ui/src/main/java/org/osc/core/ui It uses the servlet context.

denis-anisimov commented 6 years ago

Thank you for the reference. I will look into this.

denis-anisimov commented 6 years ago

Regarding to my issue with our flow-testroot-context module. At the moment the -ui classifier jar is the WAB actually (even though it's a jar file). OSGi compendium says that files in META-INF are protected and are not exposed via web.

In reality the web context path is "/" for this WAB and static web resources are available from the root folder. So our frontend folder in the root may be server via HTTP and is available via servlet context out of the box. So no need to have an activator which register resources via HTTP service. I made a PR to remove the activator : https://github.com/vaadin/flow/pull/4631.

So there is no issue with the static resource in a WAB file.

But that works only for the resources inside the WAB itself. We still need register resources if they are outside of a WAB (inside a separate bundle). But it's not clear : which resources may be required to register ? (not that webjars don't work and should be repackaged) and even if ew register them via HttpService then what to do with servlet context: they won't be available via Servlet context. Do we have usecases for resources which we want to register to make them available via HTTP and not via servlet context ?

pleku commented 6 years ago

Do we have usecases for resources which we want to register to make them available via HTTP and not via servlet context ?

As discussed, the component related static resources should be made available this way.

But we still need some resources like templates, web component files related to theming; available on the servlet context.

We will first take #4671, fix #4376, create more tests for OSGi (no issue yet) and then we will validate that this works out-of-the-box as it should.

mcollovati commented 3 months ago

Compatibility mode support, that is required by OSGi add-on, is not supported anymore.