Sling ResourceResolverFactory внутри @Activate создает исключение RunTimeException - PullRequest
0 голосов
/ 09 мая 2018

Я новичок в 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;
    }

У меня есть сервлет, который вызывает этот класс, и в сервлете, который я использую

@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 голосов
/ 13 мая 2018

Причина возникновения исключения NullPointerException

Хотя это и не указано явно в вопросе, предоставленная трассировка стека показывает, что служба «запускается» в рамках модульного теста.

Кроме того, трассировка стека показывает, что используется OsgiContext, что не обеспечивает реализацию ResourceResolverFactory.

Поскольку ResourceResolverFactory не зарегистрировано в фиктивном контексте OSGi, @Reference не может быть введено при регистрации и активации службы. Когда ResourceResolverFactory вызывается в методе активации, ссылка null и, следовательно, NullPointerException выбрасывается.

Предлагаемое решение

Поэтому я бы посоветовал использовать превосходный AemContext, который предоставляется wcm.io aem-mock framework или, по крайней мере, SlingContext, предоставленный sling-mocks .

Модульный тест будет выглядеть так:

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. Для их использования необходимо создать службу сопоставления пользователей. Вы можете узнать больше об этом в документации .

0 голосов
/ 10 мая 2018

Обратите внимание, что создание распознавателя внутри метода Acivate является анти-паттерном. Ваш сервис может вызываться несколькими потоками параллельно, и эти вызовы могут обрабатываться параллельно. Когда мы действительно пишем или читаем ресурс, тогда сеанс JCR применяет внутреннюю блокировку , что предотвращает параллельную работу нескольких сеансов в одном и том же сеансе.

Лучший способ избежать этой проблемы - создать распознаватель внутри метода, который вы переопределяете.

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