Spring injection not working for FF4J web console

2.8k Views Asked by At

I am try to get the FF4j (ff4j.org) web console to work. According to the documentation on the website, I use the following configuration:

<servlet>
    <servlet-name>ff4j-console</servlet-name>
    <servlet-class>org.ff4j.web.embedded.ConsoleServlet</servlet-class>
    <init-param>
        <param-name>ff4jProvider</param-name>
        <param-value><path to class>.ConsoleFF4jProvider</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>ff4j-console</servlet-name>
    <url-pattern>/ff4j-console</url-pattern>
</servlet-mapping>

<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

My implementation of FF4JProvider is:

import org.ff4j.FF4j;
import org.ff4j.web.api.FF4JProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * Created by itaykl on 17/06/2015.
 */
@Component
public class ConsoleFF4jProvider implements FF4JProvider{

    @Autowired
    private FF4j ff4j;

    public ConsoleFF4jProvider() {
    }

    @Override
    public FF4j getFF4j() {
        return ff4j;
    }
}

My problem is that no matter what I do I cannot get the autowiring of ff4j to work. Whenever the ConsoleServlet gets to the method call of getFF4J(), the ff4j class member is null.

I have tried using several other servlets together with the FF4J console and tried defining the ff4j bean in several ways.

Currently it is defined as:

<bean id="ff4j" class="org.ff4j.FF4j" ></bean>

But nothing seems to work.

If anyone found a solution to this problem and can share it, I would really appreciate it.

3

There are 3 best solutions below

5
On BEST ANSWER

As I was saying in my comments, the constructor of FF4j wasn't invoked yet when you are trying to access to bean in ConsoleFF4jProvider. It is because Spring loads first ConsoleFF4jProvider and not beans defined in web.xml. To solve this problem you can remove:

@Autowired
private FF4j ff4j;

and modify your getFF4j() function as following:

@Override
public FF4j getFF4j() {
    final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.register(FF4j.class); 
    context.refresh();

    FF4j ff4j= context.getBean(FF4j.class);
    return ff4j;
}

Or you can initialize ff4j in constructor as well.

Edit: Otherwise you can add

SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

to the constructor of ConsoleFF4jProvider

Hope this will solve your issue.

0
On

For me it's working like below in case of referring FF4J in servlet container. Just override configure function and source the FF4jConfig class where your FF4j is defined.

@Configuration 
public class FF4jWebConsoleConfiguration extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(FF4JConfig.class); //scan
    }
    
    // autowired working here
    @Bean
    public FF4jDispatcherServlet ff4jDispatcherServlet(@Autowired FF4j ff4j) {
...
}
0
On

Spring 4 injection works with @Configuration class and @Autowired

The problem is the servlet is initialized by Jetty (your servlet container), that's why ConsoleFF4jProvider is not aware of Spring context.

With this example you can still use Spring injection mechanism with @Autowired.

You need to :

  1. Add a Spring listener in web.xml.
  2. Create a Java class to handle the bean FF4j.
  3. Inject the Spring context in ConsoleFF4jProvider.

web.xml :

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <display-name>HelloWorld ff4j app</display-name>

     <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>

    <!-- Configuration using @Configuration classes -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>foo.bar.ApplicationConfig</param-value>
    </context-param>

    <servlet>
        <servlet-name>ff4j-console</servlet-name>
        <servlet-class>org.ff4j.web.embedded.ConsoleServlet</servlet-class>
        <init-param>
            <param-name>ff4jProvider</param-name>
            <param-value>foo.bar.ConsoleFF4jProvider</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>ff4j-console</servlet-name>
        <url-pattern>/ff4j-console</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp </welcome-file>
    </welcome-file-list>

</web-app>

ApplicationConfig.java

package foo.bar;

import org.ff4j.FF4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfig {

    @Bean
    public FF4j getFF4j() {
        return new FF4j("ff4j.xml");
    }
}

ConsoleFF4jProvider.java updated

package foo.bar;

import org.ff4j.FF4j;
import org.ff4j.web.api.FF4JProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

public class ConsoleFF4jProvider implements FF4JProvider {

    @Autowired
    private FF4j ff4j;

    /**
     * Inject the appropriate Spring bean FF4j declared into
     * {@link ApplicationConfig}. Property {@code ff4j} marked with @Autowired
     * annotation will work.
     */
    public ConsoleFF4jProvider() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    }

    @Override
    public FF4j getFF4j() {
        return ff4j;
    }
}