@Activate中的Sling ResourceResolverFactory抛出RunTimeException

时间:2018-05-09 04:13:22

标签: annotations osgi aem activation

我是AEM OSGI的新手,任何帮助都将不胜感激 我有一个包含@Activate注释激活方法的类,我在其中解析并构建资源

@Component
@Service(MyTest.class)
public class MyTest {
private static final Logger LOG = LoggerFactory.getLogger(MyTest.class);
...
...
@Reference
private ResourceResolverFactory resolverFactory;

@Activate
protected void activate() {
    final ResourceResolver resolver;
    try {
        resolver = resolverFactory.getAdministrativeResourceResolver(null);
    } catch (LoginException e) {
        LOG.error("error resolving resource resolver", e);
        return;
    }

我有一个servlet,它调用这个类,并在我正在使用的servlet上使用

@Reference
MyTest test;


@Override
protected void doPost
....

这是我得到的错误

  java.lang.RuntimeException: Unable to invoke method 'activate' for class com.demo.MyTest
java.lang.RuntimeException: Unable to invoke method 'activate' for class com.demo.MyTest at org.apache.sling.testing.mock.osgi.OsgiServiceUtil.invokeMethod(OsgiServiceUtil.java:263)
at org.apache.sling.testing.mock.osgi.OsgiServiceUtil.activateDeactivate(OsgiServiceUtil.java:101)
at org.apache.sling.testing.mock.osgi.MockOsgi.activate(MockOsgi.java:211)
at org.apache.sling.testing.mock.osgi.MockOsgi.activate(MockOsgi.java:222)
at org.apache.sling.testing.mock.osgi.context.OsgiContextImpl.registerInjectActivateService(OsgiContextImpl.java:155)
at org.apache.sling.testing.mock.osgi.context.OsgiContextImpl.registerInjectActivateService(OsgiContextImpl.java:142)
at com.Demo.MyDemoTest(MyDemoTest.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.NullPointerException
at com.Demo.MyTest.activate(MyTest.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.sling.testing.mock.osgi.OsgiServiceUtil.invokeMethod(OsgiServiceUtil.java:254)
... 33 more

请帮我理解我犯错的地方 此外,如果我将解析器工厂定义移动到同一类中的公共方法,它的工作正常

2 个答案:

答案 0 :(得分:3)

NullPointerException的原因

虽然在问题中未明确提及,但提供的堆栈跟踪显示该服务是"运行"在单元测试中。

此外,堆栈跟踪显示使用了OsgiContext,但未提供ResourceResolverFactory的实现。

由于在模拟OSGi上下文中未注册ResourceResolverFactory,因此在注册和激活服务时无法注入@Reference。当在activate方法中调用ResourceResolverFactory时,引用为null,因此抛出NullPointerException

提议的解决方案

因此,我建议您使用由wcm.io aem-mock framework提供的优秀AemContextsling-mocks提供的至少SlingContext

单元测试看起来像这样:

public class MyUnitTest {

    @Rule
    public AemContext context;

    @Test
    public void someTest() {
        MyTest service = context.registerInjectActivateService(new MyTest());

        [... additional test code ...]
    }
}

由于AemContext已经注册了功能ResourceResolverFactory(模拟),因此单元测试代码不必创建模拟并注册它。调用registerInjectActivateService()方法时,将实例化MyTest类的新实例,并注入引用的ResourceResolverFactory

附加说明

请勿创建服务范围 ResourceResolvers。这是一种不好的做法。 ResourceResolver应该是短暂的。这意味着它们仅用于少数几个操作" (比如阅读资源)然后丢弃。

执行此操作的最佳方法是使用try-with-resource语句,如下所示:

public class MyTest {

    private static final SERVICE_NAME = "MyTestService";
    private static final Map<String, Object> authenticationInfo = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, SERVICE_NAME);

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    public void someMethod() {

        try (ResourceResolver resolver = getResourceResolver()) {
            [... use resolver to do stuff in JCR ...]
        }

    }

    private ResourceResolver getResourceResolver() {
        try {
            return resourceResolverFactory.getServiceResourceResolver(authenticationInfo);
        } catch (LoginException cause) {
            throw new IllegalStateException("Unable to obtain ResourceResolver!", cause)
        }
    }
}

我选择创建一个单独的方法来创建ResourceResolver,以避免因异常处理而混淆someMethod()。但这显然是可以改变的。

由于不推荐使用管理ResourceResolver,我还选择使用服务ResourceResolver。要使用那些您需要创建服务用户映射。您可以在documentation

中找到有关此内容的更多信息

答案 1 :(得分:0)

请注意,在Acivate方法中创建解析器是反模式。您的服务可能由多个线程并行调用,并且这些调用可以并行处理。 当我们写入或读取资源时,JCR会话应用internal lock,这会阻止多个会话在同一个会话中并行工作。

避免此问题的最佳方法是在您覆盖的方法中创建解析器。

@Override
 performSomeOperation(){
          final ResourceResolver resolver;
try {
    resolver = resolverFactory.getAdministrativeResourceResolver(null);
} catch (LoginException e) {
    LOG.error("error resolving resource resolver", e);
    return;
}
}