Search code examples
javajsfejbliferayosgi

Liferay Portal: OSGI Modularity with JSF and EJB- CDI not injecting/retrieveing with @Resource


I am currently working on the Liferay 7.1.2.ga3 CE with tomcat and was going over the OSGI section of the tutorial in the official documentation. This one: Docs

I decided to give it a shot with the simplest project possible. I have created a Liferay Workspace in my IntelliJ idea and added some component as discribed in the turorial.

The problem is that it either does not inject or does inject but does not retrieve the dependecy because in this case in the consumer the _applicationServices variable is always at null.

EDIT: I have asked the same question in the official Liferay forum here : Question in Liferay.org

Here is the code:

The api:

@ProviderType
public interface ApplicationServices {
    String getSearchFiltersForRegister(String registerCode);
}

The provider:

@Component(
    immediate = true,
    property = {
    },
    service = ApplicationServices.class
)
public class ApplicationServicesImpl implements ApplicationServices {
    @Override
    public String getSearchFiltersForRegister(String registerCode) {
        return "OSGI modularity made service";
    }
}

The consumer:

@ManagedBean(name = "registerSearchBean")
@ViewScoped
@Component(
        immediate = true,
        property = {
                "osgi.command.scope=getSearchFiltersForRegister",
                "osgi.command.function=getSearchFiltersForRegister"
        },
        service = Object.class
)
public class RegisterSearchBean {

        public String message;

        @Reference
        private ApplicationServices _applicationServices;

        @PostConstruct
        public void initBean(){
                getSearchFiltersForRegister();
        }

        public void getSearchFiltersForRegister(){
                ApplicationServices applicationServices=_applicationServices;
                message=applicationServices.getSearchFiltersForRegister("asd");
        }

        public String getMessage() {
                return message;
        }

        public void setMessage(String message) {
                this.message = message;
        }
}

NOTE: I have also tried to separate the consumre module in a different class and use it in this bean without success.

Additional info:

  • The project is a gradle LiferayWorkaspace.
  • Api and services modules are generated via the Liferay plugin in the modules folder.
  • The JSF module is generated via the maven archetype with gradle support in the "war" folder.
  • Dependecies are correctly defined and it is all correctly deployed in the Liferay Server. At least it appears so.... no errors for missing classes appear on deploy.

What am I doing wrong and why application services is always null?


Solution

  • Answered extensively by Neil Griffin on the liferay forum.:

    Hi Georgi,Thanks so much for reaching out. You asked a very important question that deserves a thorough answer. Since JSF is a technology based on Java EE, it expects the project to be a Web Application ARchive (WAR) rather than a Java ARchive (JAR). When the WAR is deployed to $LIFERAY_HOME/deploy, Liferay will automatically convert the Java EE WAR to an OSGi Web Application Bundle (WAemoticon. Although WABs are first-class OSGi citizens, they do not participate in the build-time discovery of services via classes annotated with @Component. The more natural fit for WABs is CDI, which accomplishes deploy-time discovery of services by scanning the classpath.So the difficulty with the code that you pasted is this: You are combining OSGi Declarative Services build-time discovery (via @Component) with a CDI deploy-time discovery (via @ViewScoped). These need to be used in a mutually exclusive way -- In other words, they cannot be combined.Here's the encouraging news -- Liferay 7.1, 7.2, and 7.3 ship with weld-osgi-bundle.jar in $LIFERAY_HOME/modules and fully supports the CDI requirements of Portlet 3.0 via OSGi CDI Integration. This means it is possible to develop WARs that use JSR 330 @Inject in order to inject services from the OSGi Service Registry into CDI beans. It also makes it possible to publish CDI beans into the OSGi service registry. I recently gave a talk at Liferay DEVCON 2019 titled Portlet 3.0 MVCBean and PortletMVC4Spring that demonstrates the promise of OSGi CDI integration. If you have time, I recommend that you watch the presentation because it should help you get a better idea as to how it all works. But I'm sorry to say the JSF isn't quite able to take advantage of this yet. We're still working on our implementation of JSR 378, which makes it possible to deploy a JSF portlet within Liferay as a Portlet 3.0 "bean portlet" -- that's the part that enables all the OSGi CDI Integration functionality. We have it working in the 5.x branch of the liferay-faces-bridge-impl.git repository but won't be able to cut a production release until JSR 378 is complete.So unless you want to experiment with the 5.x version of the bridge, then I recommend that you follow the advice in the my DEVCON 2016 presentation titled Developing WABs With a View to the Future in which I describe how to use a ServiceTracker in order to acquire services from the OSGi Service Registry within a WAR portlet like those required by JSF.Having said that, it would probably be a good idea for you to develop OSGi services with @Component (Declarative Services) in a JAR module. The JAR module can be deployed separately from your JSF portlet WAR module. That way, you should be able to acquire the service (defined by @Component) from the OSGi Service Registry via the ServiceTracker in your JSR managed bean. Eventually, once we release JSR 378, you will be able to use JSR 330 @Inject instead of the ServiceTracker in order to inject your OSGi service into your JSF managed bean (managed by CDI via @javax.faces.view.ViewScoped). Just to clarify, again, in the future, you would want to use the CDI compatible version of the @ViewScoped annotation and not the one intended for use with the JSF Managed Bean Facility (MBF) which is @javax.faces.bean.ViewScoped.Kind Regards,Neil