I just upgraded IDEA to 2017 and jdk to 1.8_131. Then I made a sample project to test services composition and injection in different components, and I got a Security Exception (below), not sure why.
Project files attached.
13:33:30.577 INFO c.h.c.security.app.LoginWorkerBean - Logged in: 68756a19-a044-84d1-0353-783769de8605 [anonymous]
13:33:30.578 INFO com.company.test.core.AppLifecycle - *****************************************com.company.test.service.SampleServiceBean@11ac07ce
13:33:30.585 ERROR c.h.cuba.core.sys.AppContextLoader - Error initializing application
java.lang.SecurityException: No security context bound to the current thread
at com.haulmont.cuba.core.sys.AppContext.getSecurityContextNN(AppContext.java:148) ~[cuba-global-6.4.2.jar:6.4.2]
at com.haulmont.cuba.core.sys.ServiceInterceptor.aroundInvoke(ServiceInterceptor.java:85) ~[cuba-core-6.4.2.jar:6.4.2]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at com.sun.proxy.$Proxy210.getGlobalService(Unknown Source) ~[na:na]
at com.company.test.core.AppLifecycle.applicationStarted(AppLifecycle.java:34) ~[app-core-0.1-SNAPSHOT.jar:na]
at com.haulmont.cuba.core.sys.AppContext.startContext(AppContext.java:234) ~[cuba-global-6.4.2.jar:6.4.2]
at com.haulmont.cuba.core.sys.AppContext$Internals.startContext(AppContext.java:289) ~[cuba-global-6.4.2.jar:6.4.2]
at com.haulmont.cuba.core.sys.AbstractWebAppContextLoader.contextInitialized(AbstractWebAppContextLoader.java:74) ~[cuba-global-6.4.2.jar:6.4.2]
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4727) [catalina.jar:8.5.9]
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5189) [catalina.jar:8.5.9]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) [catalina.jar:8.5.9]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:752) [catalina.jar:8.5.9]
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:728) [catalina.jar:8.5.9]
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734) [catalina.jar:8.5.9]
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1107) [catalina.jar:8.5.9]
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1841) [catalina.jar:8.5.9]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_131]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_131]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_131]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
avr. 27, 2017 1:33:30 PM org.apache.catalina.core.StandardContext listenerStart
GRAVE: Exception lors de l'envoi de l'?®v?¿nement contexte initialis?® (context initialized) ?á l'instance de classe d'?®coute (listener) com.haulmont.cuba.core.sys.AppContextLoader
java.lang.RuntimeException: java.lang.SecurityException: No security context bound to the current thread
at com.haulmont.cuba.core.sys.AbstractWebAppContextLoader.contextInitialized(AbstractWebAppContextLoader.java:78)
You use *Service instance from AppLifecycle class. At this moment you have to obtain user session and set SecurityContext to be able to do it, since no one can call services from a client (in your case it is web client) without authentication. I think this behaviour is not related with your update.
It can be achieved using LoginService and system session:
@Inject
protected LoginService loginService;
@Inject
protected WebAuthConfig config; // contains trusted client password
...
try {
// obtain system session
UserSession systemSession = loginService.getSystemSession(config.getTrustedClientPassword());
// set security context and perform secured action: Runnable or SecuredOperation<T>
AppContext.withSecurityContext(new SecurityContext(systemSession), () -> {
// your service call here
});
} catch (LoginException e) {
throw new IllegalStateException("Unable to login with trusted client password", e);
}
This approach is required only when your code does not have user context: it is not UI class or executed without user interaction (for instance as scheduler), and not initiated by user.
Sorry, you was too fast. I finally tried to have the code in the correct class (needing a coffee!), but in the core module (where AppLifecycle is) the code does not have access to WebAuthConfig class. Any alternative ?
Thanks Yuriy, we’re making progress here. I tried @Authenticated on AppLifeCycle applicationStarted() but it did not work (despite not being a Service as you said, maybe name should finish by Bean ?). However it works with Authentication injection.
Now I later run into an issue in ExtAppMainWindow when calling the getGlobalService() method of SampleService : java.lang.ClassCastException: com.company.test.service.GlobalServiceBean cannot be cast to com.company.test.service.GlobalService
What is strange is that for this sample project, I created both GlobalService and SampleService with Studio so following the exact template.
I also tried to remove completely the AppLifecycle class. Same issue.
Code below.
@Component("test_AppLifecycle")
public class AppLifecycle implements AppContext.Listener {
@Inject
private Logger log;
@Inject
private LoginService loginService;
@Inject
Authentication authentication;
@Inject
SampleService sampleService;
//---
public AppLifecycle() {
AppContext.addListener(this);
}
@Override
public void applicationStarted() {
authentication.begin();
try {
log.info("*****************************************" + this.sampleService);
log.info("*****************************************" + this.sampleService.getGlobalService());
} finally {
authentication.end();
}
}
@Override
public void applicationStopped() {
}
}
public class ExtAppMainWindow extends AppMainWindow {
@Inject
private Logger log;
@Inject
private SampleService sampleService;
public void ready() {
log.info("*****************************************" + this.sampleService);
log.info("*****************************************" + this.sampleService.getGlobalService());
}
}
Mike
PS : I read the documentation, the subtility you mention about authentication in Service & Bean could benefit more attention into it. I’m not sure I understood it myself to be honest.
PS2 : what is the markdown to have names in red as you do ?
Why do you want to cast GlobalService to GlobalServiceBean? We actively use AOP and often instances of *Service cannot be cast to *ServiceBean since they are proxies with additional logging/security functionality. Actually, in web application there are only proxies that use remote invocations for core services. Thus, do not cast your service instances to implementations.