我是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
请帮我理解我犯错的地方 此外,如果我将解析器工厂定义移动到同一类中的公共方法,它的工作正常
答案 0 :(得分:3)
虽然在问题中未明确提及,但提供的堆栈跟踪显示该服务是"运行"在单元测试中。
此外,堆栈跟踪显示使用了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。
答案 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;
}
}