问题描述
在 Osgi @Component
Portlet
中,我可以使用 Osgi 声明式服务访问 Liferay 本地服务:
@Reference
private MyExampleLocalService myExampleService;
除了将服务注入我的 portlet 之外,@Reference
还确保 Liferay 在 MyExampleLocalService
可用之前不会启动我的 portlet。
在 Liferay 中,我还可以在 WAR 中部署 portlet,而无需 @Component
或访问声明性服务。我知道我可以使用像 MyExampleLocalServiceUtil.getService()
这样的静态实用程序访问那里的服务,但这会使我的应用程序面临 NullPointerException
的风险,因为我的 portlet 可能会在 MyExampleLocalService
可用之前启动。
我已经尝试使用 @Inject
和/或 @Reference
来注入服务,但是这不起作用,因为这不是 @Component
portlet。 @Inject
抛出异常,而 @Reference
没有任何效果。
解决方法
Liferay 的本地服务都是 OSGi 服务,因此如果可能,您应该通过 OSGi @Reference
或 OSGi + CDI @Reference
@Inject
(适用于 Portlet 3.0 portlet)获取您的服务依赖项。使用这些注释,OSGi 将管理您的 portlet,在服务可用时启动 portlet 并注入服务,并在服务不可用时关闭 portlet。1但是,如果您不创建 CDI 或基于 OSGi 的 portlet,您不能使用这些首选方法。例如,部署在 WAR 中的旧版 Portlet 2.0 Portlet 和 Spring Portlet 无法使用声明式服务。
这些 portlet 应该在非 OSGi portlet 中访问 Liferay 本地服务,而您应该使用 OSGi ServiceTracker
s。 ServiceTracker
有 waitForService
和 getService
获取服务的方法。如果您想等待服务(超时),您可以在 portlet 初始化完成后随时调用 waitForService
。否则,您可以随时致电 getService()
获取服务,或者在服务不可用时致电 null
。
不要在 portlet 的 ServiceTracker#waitForService
方法中调用 init
!这会导致间歇性死锁,因为 Liferay 只在一个时间。如果您的 portlet 正在部署并等待 init
中的服务,它可以阻止依赖服务包完成部署。但是 portlet 永远不会完成部署,因为它需要依赖项。最终等待的 portlet 将超时,但我认为这也可能存在错误,因为即使在 portlet 超时等待之后,死锁似乎仍然存在。
以下是获取和使用服务的一些示例代码:
public final class NonOSGiPortletUsingServices implements Portlet {
private ServiceTracker<MyExampleLocalService,MyExampleLocalService> myExampleServiceTracker;
@Override
public void init(final PortletConfig config) throws PortletException {
// Obtain the current bundle’s OSGi BundleContext from the PortletContext
// using the magic constant "osgi-bundlecontext"
final String bundleContextKey = "osgi-bundlecontext";
final Object bundleContext = config.getPortletContext().getAttribute(bundleContextKey);
if (bundleContext == null || !(bundleContext instanceof BundleContext)) {
throw new PortletException(
"Could not initialize myExampleService. "
+ bundleContextKey
+ " not found in PortletContext.");
}
myExampleServiceTracker =
new ServiceTracker<>((BundleContext) bundleContext,MyExampleLocalService.class,null);
myExampleServiceTracker.open();
final MyExampleService myExampleService =
myExampleServiceTracker.getService();
if (myExampleService == null) {
LOGGER.warn("Required service {} not available.",MyExampleService.class.getName());
}
super.init(config);
}
@Override
public void destroy() {
super.destroy();
myExampleServiceTracker.close();
}
@Override
public void render(final RenderRequest renderRequest,final RenderResponse renderResponse)
throws IOException,PortletException {
final MyExampleService myExampleService = myExampleService();
if (myExampleService == null) {
renderResponse.getWriter()
.append("Required service ")
.append(MyExampleService.class.getName())
.append(" not available. ")
.append(this.getClass().getName())
.append(" unavailable.");
return;
}
// Use myExampleService here...
}
}
这种获取服务的方法应该适用于任何非 OSGi Liferay Portlet,例如 Portlet 2.0 Portlet、Spring Portlet 和 JSF Portlet(对于 JSF,您可能更喜欢从 BundleContext
)。这种方法适用于任何 OSGi 服务,而不仅仅是 Liferay 的本地服务。
- OSGi 服务是短暂的,随时可能消失。