Foros de discusión

Unable to get Services, ServiceTracker is empty

Oleksandr Druzhynin, modificado hace 7 años.

Unable to get Services, ServiceTracker is empty

New Member Mensajes: 6 Fecha de incorporación: 15/11/13 Mensajes recientes
Hello everybody,

I'm trying to integrate local services in my JSF+CDI portlet. The problem is, that ServiceTracker doesn't resolve y service. I'm suspecting, my service is not being registered. Actually, getting the core services like com.liferay.portal.kernel.service.TeamLocalService works as expected. Here is my code:

AbstractServiceProducer.java:
public abstract class AbstractServiceProducer<t> {

	protected ServiceTracker<t, t> serviceTracker;
	private Class<t> klass;

	@SuppressWarnings("unchecked")
	public AbstractServiceProducer() {
		final Class<!--?--> klass = this.getClass();
		final Type type = klass.getGenericSuperclass();
		final ParameterizedType parameterizedType = (ParameterizedType) type;
		final Type[] actualTypes = parameterizedType.getActualTypeArguments();
		this.klass = (Class<t>) actualTypes[0];
	}

	@PostConstruct
	private void postConstruct() {
		final Bundle bundle = FrameworkUtil.getBundle(this.getClass());
		final BundleContext bundleContext = bundle.getBundleContext();
		serviceTracker = new ServiceTracker&lt;&gt;(bundleContext, klass, null);
		serviceTracker.open();
	}

	@PreDestroy
	private void preDestroy() {
		serviceTracker.close();
	}

}
</t></t></t,></t>

TestLocalServiceProducer:
@ApplicationScoped
public class TestLocalServiceProducer extends AbstractServiceProducer<testlocalservice> {
	
	private static final Log log = LogFactoryUtil.getLog(TestLocalServiceProducer.class);
	
	@Produces
	public TestLocalService create() {
		if (serviceTracker.isEmpty()) {
			// ServiceTracker is always empty :(
			log.info("serviceTracker is empty");
		} else {
			log.info("serviceTracker is NOT empty");
		}
		return serviceTracker.getService();
	}

}
</testlocalservice>

JSF Bean:
@Named
@RequestScoped
public class TestBean {

	@Inject
	private TestLocalService testLocalService;

}

The resulting testLocalService is always null, although portal kernel services working as expected.

I successfully created TestService via Service Builder and deployed on the application server. Api&Service are listed in gogo shell:
g! lb
  500|Active     |   10|test-core-api (1.0.0)
  501|Active     |   10|test-core-service (1.0.0)
  597|Active     |   10|test-jsf-portlet (1.0.0)

API bnd.bnd:
Bundle-Name: test-core-api
Bundle-SymbolicName: test.core.api
Bundle-Version: 0.0.1
Export-Package: \
	de.test.core.exception,\
	de.test.core.model,\
	de.test.core.service,\
	de.test.core.service.persistence
-includeresource: META-INF/service.xml=../test-core-service/service.xml

Service bnd.bnd:
Bundle-Name: test-core-service
Bundle-SymbolicName: test.core.service
Bundle-Version: 0.0.1
Liferay-Require-SchemaVersion: 1.0.0
Liferay-Service: true
-plugin.service: com.liferay.ant.bnd.service.ServiceAnalyzerPlugin
-plugin.spring: com.liferay.ant.bnd.spring.SpringDependencyAnalyzerPlugin


I have no idea what am I doing wrong emoticon
Any help would be highly appreciated!

Thanks and best regards,
Oleksandr
thumbnail
Neil Griffin, modificado hace 7 años.

RE: Unable to get Services, ServiceTracker is empty

Liferay Legend Mensajes: 2655 Fecha de incorporación: 27/07/05 Mensajes recientes
Hi Oleksandr,

Thanks for posting this question. I noticed that you are using CDI to @Inject your service using a custom CDI producer. Since portlet developers are typically Java EE developers, one of our main goals is to provide portlet developers with the ability to @Inject OSGi services with CDI. I gave a talk at Liferay's DevCon titled Developing WABs With a View to the Future that discusses this very issue. If you watch the presentation at around 21:30 I talked about the ServiceTracker approach without CDI. At 23:25 I talked about OSGi RFC 0193. Liferay's implementation is coming along and we intend to make it available for developers targeting Liferay 7.0. However we're not there yet with our implementation. Because of that, I recommend you watch at 25:15 where I discussed using a CDI producer to inject a proxy.

So that this time I recommend you either take the ServiceTracker approach without CDI, or the proxy approach with a CDI producer. Either approach will give you the dynamism of OSGi. This is important because the design you currently have loses the dynamism of OSGi. In other words, in your current design you end up @Inject-ing the reference to the OSGi service and not really getting the benefit of the ServiceTracker which tracks when services come-and-go. Also, your producer is creating @ApplicationScoped instances which might cause additional problems in this regard.

Kind Regards,

Neil
Oleksandr Druzhynin, modificado hace 7 años.

RE: Unable to get Services, ServiceTracker is empty

New Member Mensajes: 6 Fecha de incorporación: 15/11/13 Mensajes recientes
Thank you Neil, that DevCon Video was very informative for me. And I really excited about plans for the RFC 0193 implementation for Liferay, it is very promising. I hope this will be released soon.
However I didn't resolve my problem with custom services. Whatever Proxy solution or approach without CDI works perfect with any Liferay kernel services (for example TeamService or UserService), but not with my custom service. I'm experiencing serviceTracker.isEmpty() == true and thus NullPointerException with any of approaches. Even FooLocalServiceUtil.getFoosCount() fails with NPE somewhere in cause-chain. My JSF Portlet doesn't "see" my custom service, although both -api & -service modules have Active status.
I suspect I'm doing something wrong with service configuration or any kind of import/export or anything else emoticon

I did search for solution here on the forum and other Internet resources too with no success and have no idea how to fix the problem. I hope for a community help...

Thank you and best Regards,
Oleksandr
thumbnail
Neil Griffin, modificado hace 7 años.

RE: Unable to get Services, ServiceTracker is empty

Liferay Legend Mensajes: 2655 Fecha de incorporación: 27/07/05 Mensajes recientes
Hi Oleksandr,

Could you provide a directory listing of what is contained in your portlet war's WEB-INF/lib?

Thanks,

Neil
Oleksandr Druzhynin, modificado hace 7 años.

RE: Unable to get Services, ServiceTracker is empty

New Member Mensajes: 6 Fecha de incorporación: 15/11/13 Mensajes recientes
Hello Neil,
after several attemps to fix the problem, I created Portlet and Service from the scratch and purged all own old modules from the Wildfly server:
osgi/modules, osgi/state/org.eclipse.osgi, osgi/wabs and osgi/war. This resolved the issue. I suppose my service somehow wasn't being updated.
Now all work just as expected.
I ended up with following code for JSF/CDI-based Portlets:
Abstract producer:
public abstract class AbstractServiceProducer<t> {

	private static final Log log = LogFactoryUtil.getLog(AbstractServiceProducer.class);

	private Class<t> serviceClass;

	public AbstractServiceProducer() {
		final Class<!--?--> klass = this.getClass();
		final Type type = klass.getGenericSuperclass();
		final ParameterizedType parameterizedType = (ParameterizedType) type;
		final Type[] actualTypes = parameterizedType.getActualTypeArguments();
		@SuppressWarnings("unchecked")
		final Class<t> serviceClass = (Class<t>) actualTypes[0];
		this.serviceClass = serviceClass;
	}

	protected T create() {
		try {
			final Bundle bundle = FrameworkUtil.getBundle(this.getClass());
			final BundleContext bundleContext = bundle.getBundleContext();
			final ServiceTracker<t, t> serviceTracker = new ServiceTracker&lt;&gt;(bundleContext, serviceClass, null);
			serviceTracker.open();
			@SuppressWarnings("unchecked")
			final T service = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),
															new Class[] { serviceClass },
															new OSGiServiceProxy&lt;&gt;(serviceTracker));
			return service;
		} catch (final Throwable e) {
			log.error(e);
			throw e;
		}
	}

	protected void dispose(final T service) {
		try {
			final InvocationHandler invocationHandler = Proxy.getInvocationHandler(service);
			if (invocationHandler instanceof OSGiServiceProxy) {
				@SuppressWarnings("unchecked")
				final OSGiServiceProxy<t> osgiServiceProxy = (OSGiServiceProxy<t>) invocationHandler;
				final ServiceTracker<t, t> serviceTracker = osgiServiceProxy.getServiceTracker();
				serviceTracker.close();
			}
		} catch (final Throwable e) {
			log.error(e);
			throw e;
		}
	}
}
</t,></t></t></t,></t></t></t></t>

and specific producer:
@RequestScoped
public class FooLocalServiceProducer extends AbstractServiceProducer<foolocalservice> {

	@Produces
	@Override
	protected FooLocalService create() {
		return super.create();
	}

	@Override
	protected void dispose(@Disposes final FooLocalService service) {
		super.dispose(service);
	}

}
</foolocalservice>

then service can be @Injected in JSF bean:
@Named
@ViewScoped
public class FooBean {

	@Inject
	private FooLocalService teamService;

}

Thank you for your help!

Best Regards,
Oleksandr
thumbnail
Neil Griffin, modificado hace 7 años.

RE: Unable to get Services, ServiceTracker is empty

Liferay Legend Mensajes: 2655 Fecha de incorporación: 27/07/05 Mensajes recientes
Hi Oleksandr,

Thanks for reporting back -- I'm glad you figured it out and that it is working for you now. And thanks for using Liferay Faces emoticon

Neil