Pax Web

SharedWebContainerContext throws Http context org.ops4j.pax.web.service.internal.DefaultSharedWebContainerContext@... is already associated to bundle ...

Details

  • Type: New Feature New Feature
  • Status: Reopened Reopened
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: 1.1.2
  • Fix Version/s: 2.0.1
  • Component/s: Whiteboard Extender
  • Labels:
  • Environment:
    All

Description

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.

Activity

Hide
Achim Nierbeck added a comment -

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.

Show
Achim Nierbeck added a comment - 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.
Hide
Laszlo Hordos added a comment -

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.

Show
Laszlo Hordos added a comment - 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.
Hide
Achim Nierbeck added a comment - - edited

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

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

Show
Achim Nierbeck added a comment - - edited 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 I suggest also taking a look at the whiteboard sample project:
Hide
Laszlo Hordos added a comment -

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.

Show
Laszlo Hordos added a comment - 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.
Hide
Achim Nierbeck added a comment -

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

I suggest to ask the mailinglist first before entering a bug

Show
Achim Nierbeck added a comment - you might also take a look at this thread on the mailinglist: I suggest to ask the mailinglist first before entering a bug
Hide
Laszlo Hordos added a comment -

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 )

Show
Laszlo Hordos added a comment - 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 )
Hide
Achim Nierbeck added a comment -

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

Show
Achim Nierbeck added a comment - this will introduce new behavior for the whiteboard extender. see also here http://wiki.osgi.org/wiki/WebExperience for inspiration
Hide
Achim Nierbeck added a comment -

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

Show
Achim Nierbeck added a comment - @Laszlo do you have a simple test that can be used for integration test to verify that the expected behavior can be achieved?
Hide
Laszlo Hordos added a comment -

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?

Show
Laszlo Hordos added a comment - 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?
Hide
Achim Nierbeck added a comment - - edited

@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

Show
Achim Nierbeck added a comment - - edited @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

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated: