Search code examples
javaservletsosgishiroservletcontextlistener

Programmatic configuration of HTTPService with ServletListener and Filter


I try to implement a HttpService in an OSGI environment in combination with VAADIN and shiro. So i declared the following BundleActivator:

import java.util.logging.Level;

import org.eclipse.equinox.http.servlet.ExtendedHttpService;

import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

/**
 *
 * @author nspecht
 */
public class BundleActivator extends some.package.BundleActivator implements ServiceTrackerCustomizer
{

/**
 * Service tracker.
 */
private ServiceTracker tracker;

/**
 * HttpService.
 */
private ExtendedHttpService httpService;

@Override
protected void afterStart(BundleContext bc, DependencyManager dm) {
    this.tracker = new ServiceTracker(this.context, ExtendedHttpService.class, this);
    this.tracker.open();
}

@Override
protected void beforeStop(BundleContext bc, DependencyManager dm) {
    if (this.tracker != null) {
        this.tracker.close();
        this.tracker = null;
    }
}

@Override
protected void afterBundleChanged(BundleEvent be) {

}

@Override
public Object addingService(ServiceReference sr) {
    this.httpService = (ExtendedHttpService)this.context.getService(sr);
    HttpContext httpContext = this.httpService.createDefaultHttpContext();
    try {

        MainServlet mainServlet = new MainServlet();
        this.httpService.registerServlet(MainServlet.PATH, mainServlet, null, httpContext);

        // TODO: add ServletContextListener and Filter

        LoginServlet loginServlet = new LoginServlet();
        this.httpService.registerServlet(LoginServlet.PATH, loginServlet, null, httpContext);

        // TODO: add ServletContextListener and Filter

    } catch (Exception ex) {
        Logger.getLogger(BundleActivator.class.getName()).log(Level.SEVERE, null, ex);
    }
    return this.httpService;
}

@Override
public void modifiedService(ServiceReference sr, Object t) {
}

@Override
public void removedService(ServiceReference sr, Object t) {
    this.httpService.unregister(MainServlet.PATH);
    this.httpService.unregister(LoginServlet.PATH);
    // TODO: remove ServletContextListener and Filter
}
}

MyServlets are VaadinServlets. Now i want to add Shiro to my application. To make shiro work correctly i have to add the EnvironmentLoaderListemer and ShiroFilter to my services.

If I try the following:

mainServlet.mainServlet.getServletContext().addListener(...)

the application crashes (because context has already been initialized).

mainServlet.getServletContext().createListener(...)

does not throw an exception but the ServletContextListener does not work either. The httpService does not have a method to add Listeners that way. Filters have the same problem and the httpService.registerFilter(...) method is useless with a ShiroFilter when there is no EnvironmentLoader registered by the Listener.

Are there workarounds to add Shiro the programmatic way?

Is there a way to add Listeners and Filters for my Servlets?


Solution

  • Finally i found a solution. I created my own ShiroFilter and added it as usual to my httpService:

    ServletFilter filter = new ServletFilter();
    this.httpService.registerFilter("/", filter, null, httpContext);
    

    The ServletFilter is also simple:

    public class ServletFilter extends IniShiroFilter
    {
        @Override
        protected Ini loadIniFromConfig() {
            return new MyIni();
        }
    
        @Override
        protected Ini loadIniFromPath() {
            return this.loadIniFromConfig();
        }
    }
    

    And finally the MyIni class:

    public class MyIni extends Ini
    {
    /**
     * Constructor.
     */
    public MyIni() {
        super();
        // Set a class loader, which is able to resolve your classes!
        Thread.currentThread().setContextClassLoader(new BundleWideClassLoader());
        this.config();
    
    }
    
    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append("# ").append(this.getClass().getCanonicalName()).append("\n");
    
        for (String section : this.getSectionNames()) {
            buf.append("\n[").append(section).append("]\n");
            Section s = this.getSection(section);
            for (String key : s.keySet()) {
                buf.append(key).append(" = ").append(s.get(key)).append("\n");
            }
        }
    
        return buf.toString();
    }
    
    @Override
    public void load(InputStream iniConfig) {
        // do nothing
    }
    
    @Override
    public void load(Scanner scanner) {
        //do nothing
    }
    
    /**
     * Configure this ini.
     */
    private void config() {
        Section main = this.addSection("main");
        main.put("authc.loginUrl", "/login");
        main.put("authc.successUrl", "/");
        main.put("authc.usernameParam", "user");
        main.put("authc.passwordParam", "pass");
        main.put("authc.rememberMeParam", "remember");
    
        main.put("ds", com.mchange.v2.c3p0.ComboPooledDataSource.class.getCanonicalName());
        ...
    
        main.put("matcher", org.apache.shiro.authc.credential.HashedCredentialsMatcher.class.getCanonicalName());
        main.put("matcher.hashAlgorithmName", "SHA-256");
    
        main.put("jdbcRealm", org.apache.shiro.realm.jdbc.JdbcRealm.class.getCanonicalName());
        ...
    
        main.put("jdbcRealm.dataSource", "$ds");
        main.put("jdbcRealm.credentialsMatcher", "$matcher");
        main.put("securityManager.realms", "$jdbcRealm");
    
        Section urls = this.addSection("urls");
        urls.put("/VAADIN/**", "anon");
        urls.put("/login/**", "anon");
        urls.put("/login", "authc");
        urls.put("/**", "authc");
    }
    }