ops4j / org.ops4j.pax.web

OSGi R7 Http Service, Whiteboard and Web Applications (OSGi CMPN Release chapters 102, 140 and 128) implementation using Jetty 9, Tomcat 9 or Undertow 2.
https://ops4j1.jira.com/wiki/display/paxweb/Pax+Web
Other
144 stars 184 forks source link

SharedWebContainerContext throws Http context org.ops4j.pax.web.service.internal.DefaultSharedWebContainerContext@... is already associated to bundle ... [PAXWEB-344] #452

Closed ops4j-issues closed 12 years ago

ops4j-issues commented 12 years ago

Laszlo Hordos created PAXWEB-344

I try to use and share the same HttpContext between multiple Bundles. I have multiple HttpServlet and Filer to register with the same HttpContext. I use the org.ops4j.pax.web.service.WebContainer to register the filter. This class has the getDefaultSharedHttpContext method which is "package private" but the implementation is public. This is one of the issues but I find a workaround.

Now I have the org.ops4j.pax.web.service.internal.HttpServiceStarted#getOrCreateContext(HttpContext) where it's:

m_serverModel.associateHttpContext(context, m_bundle,
httpContext instanceof SharedWebContainerContext);

In the
org.ops4j.pax.web.service.spi.model.ServerModel

public void associateHttpContext( final HttpContext httpContext,
final Bundle bundle,
final boolean allowReAsssociation )
{
final Bundle currentBundle = m_httpContexts.putIfAbsent( httpContext, bundle );
if( ( !!allowReAsssociation ) && currentBundle != null && currentBundle != bundle ) { throw new IllegalStateException( "Http context " + httpContext + " is already associated to bundle " + currentBundle ); }

}

I think the "!!allowReAsssociation" is a typo error and it should be !allowReAsssociation

How can I runtime register/remove Filer and Servlet but keep the same HttpServiceContext?

org.eclipse.jetty.server.handler.HandlerCollection#_handlers has my org.ops4j.pax.web.service.jetty.internal.HttpServiceContext but the ServletHandler _servletHandler filed has always new instance. If I register the Servlet the new HttpServiceServletHandler instance has no _filerMappings. If I register the filter afterward a new HttpServiceServletHandler instance has no servletNameMap.

How should I do this properly? I try to debug but I'd appreciate any help.


Affects: 1.1.2 Fixed in: 3.0.0.M1, 3.0.0 Votes: 1, Watches: 1

ops4j-issues commented 12 years ago

Achim Nierbeck commented

so you are trying to register multiple servlets and filters to a common httpContext? why don't you use the whiteboard extender then?
I think it's much easier to use.

ops4j-issues commented 12 years ago

Laszlo Hordos commented

Thanks for the advice with the WhiteBoard. I used that before in some simple case but now I try to apply it to my case. The shared HttpContext I think is a problem so I can not use that but fortunately I have resources only in one bundle now but I have to get them from multiple soon.

I debug now the WhiteBoard and It does not play well in a multi bundle game.

In org.ops4j.pax.web.extender.whiteboard.internal.ExtenderContext the code

public WebApplication getWebApplication( final Bundle bundle,
final String httpContextId )
{
final ContextKey contextKey = new ContextKey( bundle, httpContextId );
WebApplication webApplication = m_webApplications.get( contextKey );

The context is not shared between the bundles because the ContextKey and the org.ops4j.pax.web.extender.whiteboard.internal.tracker.AbstractTracker use

final WebApplication webApplication = m_extenderContext.getWebApplication(
serviceReference.getBundle(),
webElement.getHttpContextId()
);

The workaround is I have to pick one single bundle and us the BundleContext of that to register the service. I can not use declarative service (SCR) to register my services and us the Pax WhiteBoard patter to register WebElement.

FrameworkUtil.getBundle(ContextRegistrator.class).getBundleContext().registerService(ServletMapping.class.getName(), servletMapping, null);

I can test the 1.1.3-SNAPSHOT and give you feedback on how it work if try to fix this shared HttpContext between multi bundle with WhiteBoard scenario.

I have some additional problems when I shut down the system I get NPE exceptions.
org.ops4j.pax.web.service.jetty.internal.JettyServerImpl

public void removeServlet(final ServletModel model) {
LOG.debug("Removing servlet [" + model + "]");
// jetty does not provide a method fro removing a servlet so we have to
// do it by our own
// the facts bellow are found by analyzing ServletHolder implementation
boolean removed = false;
final ServletContextHandler context = m_server.getContext(model.getContextModel()
.getHttpContext());

Here the "context" is null and the next line throws NPE.

I also get NPE from org.ops4j.pax.web.service.jetty.internal.JettyServerImpl.removeFilter(JettyServerImpl.java:321)

BTW I use Pax Web 1.1.1 because when I change to 1.1.2 I missing requirement 63.0 osgi.wiring.package; (&(osgi.wiring.package=javax.servlet)(version>=2.3.0)(!(version>=3.0.0))) In 1.1.1 the OPS4J Pax Web - Jetty Bundle (1.1.1) exported the package so I didn't looked into the problem so I changed back to 1.1.1

This is another issue I solve later.

ops4j-issues commented 12 years ago

Achim Nierbeck commented

have you tried sharing the http-context between the bundles as a service?
If you don't provide the httpContext a default one is created. I think the main issue is about sharing the httpContext.
The spec says nothing about it, so maybe registering a "common" httpcontext as a service and referencing this one might work :smile:

I suggest also taking a look at the whiteboard sample project:

ops4j-issues commented 12 years ago

Laszlo Hordos commented

Yes I did register the HttpContext as a service in my first example and then I used the HttpService to register my servlet from different bundles. As you may not know (It was new to me) the different bundles gets different instance of org.ops4j.pax.web.service.internal.HttpServiceStarted and the private final Bundle m_bundle; value is different in each instance (There is a service factory so when you use SCR or getService() it different per bundle)

When I use bundel#1 to register the the servlet I get the HttpService@instance-1 and I register the servlet and I use the shared HttpContext. Then I can access to the servlet but there is no filter.

When I use bundle#2 to register the filter I get the HttpService@instance-2 and I register the filter and I use the shared HttpContext. Then I try to access to the same servlet but then the servlet is gone and the new HttpServiceContext handles the request and this context has the filter but not the servlet.

If I activate the bundel#1 again then I'm back to have the servlet in the HttpServiceContext but not the filter.

All your examples using the same single bundle and then it works well but try a sample where it's separated and you will see.

My working example is available:

Firs I create the HttpContext
https://svn.forgerock.org/openidm/trunk/openidm-httpcontext/src/main/java/org/forgerock/openidm/http/ContextRegistrator.java

Your example was the previous:
https://svn.forgerock.org/openidm/trunk/openidm-httpcontext/src/main/java/org/forgerock/openidm/http/ContextRegistrator.java?p=898

When I register the filter:
https://svn.forgerock.org/openidm/trunk/openidm-filter/src/main/java/org/forgerock/openidm/filter/AuthFilter.java

Your example was the previous:
https://svn.forgerock.org/openidm/trunk/openidm-filter/src/main/java/org/forgerock/openidm/filter/AuthFilter.java?p=898

When I register the servlet:
https://svn.forgerock.org/openidm/trunk/openidm-restlet/src/main/java/org/forgerock/openidm/restlet/Servlet.java

Your example was the previous:
https://svn.forgerock.org/openidm/trunk/openidm-restlet/src/main/java/org/forgerock/openidm/restlet/Servlet.java?p=898

To do as you do in your example from multiple bundles I need to register the HttpContext as a service AND I need to register the org.ops4j.pax.web.service.WebContainer service instance from the httpcontext bundle and use that instance to register later the filters and the servlets.

ops4j-issues commented 12 years ago

Achim Nierbeck commented

you might also take a look at this thread on the mailinglist:

I suggest to ask the mailinglist first before entering a bug :smile:

ops4j-issues commented 12 years ago

Laszlo Hordos commented

The original bug I issued was not discussed and it's not fixed. The DefaultSharedHttpContext implementation.

1 getDefaultSharedHttpContext method is "package private"
#2 if( ( !!allowReAsssociation ) && currentBundle != null && currentBundle != bundle ) should be if( ( !allowReAsssociation ) && currentBundle != null && currentBundle != bundle )

ops4j-issues commented 12 years ago

Achim Nierbeck commented

this will introduce new behavior for the whiteboard extender.
see also here http://wiki.osgi.org/wiki/WebExperience for inspiration

ops4j-issues commented 12 years ago

Achim Nierbeck commented

@Laszlo
do you have a simple test that can be used for integration test to verify that the expected behavior can be achieved?

ops4j-issues commented 12 years ago

Laszlo Hordos commented

I try to write an integration test. I read the http://wiki.osgi.org/wiki/WebExperience and I'm interested in the context.shared=true implementation. In my use-case I have a bundle that registers the shared HttpContext and register an authentication filter. I'd like to use this shared HttpContext from my second Bundle and register my REST servlet so it's protected by the authentication filter. I use Vaadin UI and I have a bundle with my UI application and there is the vaadin bundle with /VAADIN resource folder.

Bundle #1
Register shared HttpContext (path="/MyApp",contextId="MyApp-context",context.shared="true",parameters= {..})
Register authentication Filter (urlPatterns=

["/MyApp//secured/"],contextId="MyApp-context",servletNames=[],ranking=1)

Bundle #2
Register REST Servlet (servletName="MyREST",alias=null,urlPatterns=

["/MyApp/REST/*"],contextId="MyApp-context",initParams={..}

)

Bundle #3
Register UI Servlet (servletName="MyUI",alias=null,urlPatterns=["/MyApp/UI/*"],contextId="MyApp-context",initParams= {..})
TODO - HttpContext#getResource(String) should have access to the resources in Bundle #3!
TODO - How to access resources from Vaadin.jar#/VAADIN as /MyApp/VAADIN/ (Use ResourceMapping (alias="/MyApp/VAADIN",path="bundle://54/VAADIN"))


Bundle #3 and #4 (Vaadin JAR)
Register UI Servlet (servletName="MyUI",alias=null,urlPatterns=

["/MyApp/UI/","/MyApp/VAADIN/"],contextId="MyApp-context",initParams={..}

)
The Shared HttpContext should have access to the resources in both bundle and it may open the backdoor mentioned in the wiki page.

Alternative solution could be if I have to programatically call the DefaultSharedWebContainerContext#registerBundle(Bundle) method on Bundle #3 and #4 before I register the UI servlet.

What do think about the case?

ops4j-issues commented 12 years ago

Achim Nierbeck commented

@Laszlo, about your Vaadin problem, take a look at my optimizations of Kai Toetders OSGI-Vaadin Demo at https://github.com/ANierbeck/osgi-vaadin-demo this might be helpful for you.

Regarding the shared-context. Since I'm out of time right now I have to push this to either a 2.0.x or a 2.1.x

ops4j-issues commented 12 years ago

Carl Hall commented

I'm also hitting this problem. I tried using the whiteboard pattern but couldn't put filters into the shared context. Without a workaround, this issue blocks me from using Pax Web and that makes me sad.

ops4j-issues commented 12 years ago

Achim Nierbeck commented

@Carl I know this is an issue, unfortunately I haven't had the time for this (still do this in my spare time) but I think I've seen some comments on the general mailing list on how to get around this. First have a Bundle register a Context as a service and use this service in all your whiteboard bundles.

ops4j-issues commented 12 years ago

Achim Nierbeck commented

hmm, maybe in a 2.1.x

ops4j-issues commented 12 years ago

Carl Hall commented

I don't fully grasp where the problem lives here but if I can help (with some direction), I'm glad to offer my time.

ops4j-issues commented 12 years ago

Achim Nierbeck commented

@Carl, any help is welcomed and appreciated. As I might have already mentioned right now I'm lacking some time cause I do this completely in my spare time.
The main issue around this is that right now as the OSGi spec states the Context is not sharable across bundles. But there have been some ideas collected at http://wiki.osgi.org/wiki/WebExperience on how to get around this and how to maybe improve the spec at this point. So if you have the time and some ideas on how to fix this you are welcomed to give it a try.

ops4j-issues commented 12 years ago

Achim Nierbeck commented

needs to be bumped to 3.0

ops4j-issues commented 12 years ago

Carl Hall commented

I spent a lot of time learning more about the pax web codebase. The suggestion made above by @Laszlo plays out well (change !!allowReAsssociation to !allowReAsssociation) for getting rid of filter registration exceptions when using the shared web contxt. I'm still having trouble getting a filter to get registered and used across servlets in multiple bundles. I will put together a more succinct test suite for this and report back later. I still have a lot to figure out though.

ops4j-issues commented 12 years ago

Achim Nierbeck commented

@Carl thanks for the feedback, yeah there are some tricky parts to it ...

ops4j-issues commented 12 years ago

Carl Hall commented

I have put together a test project to work through this. The README.md there describes a patch as noted above that is needed to get this to work. Given the testing in this project, I have committed this patch to repository.

There is another scenario noted in this JIRA of allowing filters and servlets to register to the shared context via the whiteboard. I think this should be tracked in another JIRA. I will investigate how the Felix Http Service project handles this as it doesn't create any HttpContext services.

ops4j-issues commented 12 years ago

Carl Hall commented

I have added another branch to the test project to show how the Felix HTTP whiteboard approach differs. In short, there is no http context created and all filters & servlets get registered together unless a specific contextId is given. This is a fundamental difference between Felix HTTP and Pax Web. I will take this to the mail list to find the best solution.

If there are no arguments against it, I think this JIRA should be marked as resolved since the initial pain of registering multiple things to the default shared context has been fixed. I would like to open another JIRA to track further work. I'll wait a few days before resolving this to let folks weigh in.

ops4j-issues commented 12 years ago

Carl Hall commented

Resolving as the fix noted above clears the issue denoted by the title of this ticket.