【问题标题】:Sling ResourceResolverFactory inside @Activate throws RunTimeException@Activate 内的 Sling ResourceResolverFactory 抛出 RunTimeException
【发布时间】:2018-10-19 02:26:18
【问题描述】:

我是 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

请帮助我了解我在哪里犯了错误 此外,如果我将解析器工厂定义移动到同一个类中的公共方法,它可以完美地工作

【问题讨论】:

  • 请发布整个错误消息,包括堆栈跟踪。
  • 此外,您的问题标题显示“抛出NullPointerException”,但问题文本显示错误为RuntimeException。是哪个?
  • 抱歉,现在修正了描述
  • 看起来这个错误是在构建过程中抛出的?特别是在 Maven 构建的测试阶段?还是在 AEM 上抛出(在 error.log 中)??
  • NPE 被抛出 MyTest.java 的第 75 行。您还没有对源代码进行行编号,所以您能突出显示哪一行是第 75 行吗?

标签: annotations osgi aem activation


【解决方案1】:

NullPointerException 的原因

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

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

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

建议的解决方案

因此,我建议使用wcm.io aem-mock framework 提供的优秀AemContext 或至少sling-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 中找到更多相关信息。

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 你的前两句话没有多大意义。在 activate 方法完成之前,任何消费者都不能调用服务。
    • 是的,但是如果您在激活方法中创建会话,那么这不是最佳做法。
    • 虽然@NeilBartlett 是对的,但创建一个服务范围的ResourceResolver 仍然被认为是不好的做法。 ResourceResolver 应该是短暂的,并且在被“扔掉”之前只用于少数操作。实现这一点的最佳方法是使用try-with-resource 语句,在try 块内运行一些操作。在 try 块运行后,ResourceResolver 将被自动丢弃(因为 AutoClosable 实现)。
    猜你喜欢
    • 2018-03-31
    • 1970-01-01
    • 2015-01-25
    • 2023-03-24
    • 2017-06-02
    • 2018-08-03
    • 2018-07-10
    • 2011-10-02
    • 2012-11-23
    相关资源
    最近更新 更多