I have read carefully the article about Interceptors in the Seam/Weld documentation and implemented a InterceptorBinding:
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface MyLog {}
and a Interceptor class:
#MyLog #Interceptor
public class ErpLogInterceptor implements Serializable
{
#AroundInvoke
public Object logMethodEntry(InvocationContext invocationContext) throws Exception
{..}
#PostConstruct
public Object logPostConstruct(InvocationContext invocationContext) throws Exception
{...}
}
No I tried to activated the interceptor in the #Named #ViewScoped bean:
#javax.inject.Named;
#javax.faces.bean.ViewScoped
public class MyBean implements Serializable
{
#PostConstruct #MyLog
public void init()
{...}
#MyLog public void toggleButton()
{..}
}
If I push a button on my JSF page the method toggleButton is invoked correctly and the Interceptor method logMethodEntry is called. But it seems the method #PostConstruct (I am interested in) is never intercepted by my class.
The question seems to be related to Java EE Interceptors and #ViewScoped bean but actually my interceptor is working in normal methods.
You should set return type of #PostConstruct interceptor to void not Object.
Change:
#PostConstruct
public Object logPostConstruct(InvocationContext invocationContext) throws Exception
{...}
to:
#PostConstruct
public void logPostConstruct(InvocationContext invocationContext) throws Exception
{...}
Related
I have a question about spring bean injection in service tasks of Flowable, why only this kind of injection with a static modifier worked, and what is the logic of it?
I must inject a spring bean in a Flowable java service task, and I tested some different kind of injection Field, constructor, and setter injection, eventually setter injection with static modifier worked for me like this :
public class GetCurrentUserDlg implements JavaDelegate {
private static PersonService personService;
#Autowired
public void setPersonService(PersonService personService) {
this.personService = personService;
}
#Override
public void execute(DelegateExecution execution) {
personService.getCurrentUser();
}
}
While I can not answer your question, the following works fine for me:
public class SomeDelegate implements JavaDelegate {
#Autowired
private SomeBean bean;
#Override
public void execute(DelegateExecution execution) {
System.out.println(this.bean);
}
}
The class is then used in the process via flowable:class="packages.SomeDelegate"
But, be aware, that you may have problems with autowiring dependencies in the SomeBean bean. This dependencies are not injected when using the flowable:class attribute. In order for this to work you have to make the SomeDelegate a actual bean itself (e.g. via #Service) and use it in your process definition via flowable:delegateExpression="${someDelegate}"
Example:
#Service("someDelegate")
public class SomeDelegate implements JavaDelegate {
...
and
<serviceTask id="doSomething" name="Do Something" flowable:delegateExpression="${someDelegate}"/>
It should work like this:
#Component
public class GetCurrentUserDlg implements JavaDelegate {
#Autowired
private PersonService personService;
#Override
public void execute(DelegateExecution execution) {
personService.getCurrentUser();
}
}
#Component
public class PersonService {
// its methods
}
I have CDI interceptor class annotated with javax.interceptor.InterceptorBinding #LogBinding, defined as follows:
#InterceptorBinding
#Retention(RUNTIME)
#Target({METHOD, TYPE})
public #interface LogBinding {}
Then I use annotation on EJB bean:
#Alternative
#Stateless
#LogBinding
public class FooService {
#Asynchronous
public fooMethod() {
}
}
I invoke fooMethod from another bean. The problem is that interceptor isn't called. Everything works when I change #LogBinding annotation to #Interceptors({LogInterceptor.class}) on the FooService.
I don't know if it may have impact but FooService is market as #Alternative, because it's injected in other places as EJB bean, below is the producer field:
#Default
#Produces
#EJB
private FooService fooService;
#Interceptor
#LogBinding
#Priority(Interceptor.Priority.APPLICATION)
public class LogInterceptor {
#AroundInvoke
public Object invoke(final InvocationContext invocationContext) throws Exception {
System.out.println("it works");
}
}
Why it doesn't work?
You also have to annotate your interceptor class with that binding in order for it to know which class to use as the interceptor
#LogBinding
#Interceptor
public class LogInterceptor {...}
I have an interceptor binding annotation :
#InterceptorBinding
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyBinding {
}
To this CDI interceptor :
#Interceptor
#MyBinding
public class MyInterceptor {
#AroundInvoke
public Object applyPolicy(InvocationContext ctx) throws Exception {
return blablabla;
}
}
And a class annotated, that mean every methods of this class will invoke MyInterceptor
#MyBinding
public class GlobalController {
public void methodA() {...}
public void methodB() {...}
}
All works fine, but I wish methodB not invoking my interceptor.
I tried both annotations #ExcludeClassInterceptors and #ExcludeDefaultInterceptors on my method but it doesn't works for me. I think these annotations are especially for exclude a method for EJB Interceptor, and not CDI Interceptor with Interceptor binding.
Not sure about these annotations but as a workaround you can add an annotation to the method you want to exclude. Get Method from InvocationContext in the interceptor and check whether the method has the annotation. In this case just delegate to the original method.
Try #MyBinding at method level:
public class GlobalController {
#MyBinding
public void methodA() {...}
public void methodB() {...}
}
I searched SO and found bunch of other questions that looked similar but not exactly, so I'll ask another one.
I have Spring application and say I created custom aspect (looking for CatchMe annotation) to log exceptions in a specific way. I want to test the aspect by mocking the behavior of one of my Spring #Service class's method so it throws exception when it is called. Then in another method, annotated with my custom annotation #CatchMe, I call the first method. What I expect to happen is the exception to get logged. Unfortunatelly the exception is thrown but the aspect is not triggered. So how can I make the aspect to get triggered in this test using Mockito?
Note: I've checked those (plus a bunch more):
Unit testing Spring #Around AOP methods
Spring Aspect not triggered in unit test
Spring: cannot inject a mock into class annotated with the #Aspect annotation
but most of them are Controller related and not Service related and I want to test only the service.
The Test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {BeanConfig.class})
public class MyServiceTest {
#Autowired
#InjectMocks
private MyService service;
#Mock
private MyServiceDependency serviceDep;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(service, "serviceDep", serviceDep);
}
#Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
Services
#Service
public class MyService {
#Autowired
private MyServiceDependency serviceDep;
#CatchMe
public void execute() {
serviceDep.process();
}
}
#Service
public class MyServiceDependency {
public Object process() {
// may throw exception here
}
}
Configuration and Aspect
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"com.example.services"})
public class BeanConfig { .. }
#Aspect
#Component
public class CatchMeAspect {
#Around("#annotation(CatchMe)")
public Object catchMe(final ProceedingJoinPoint pjp) throws Throwable {
try {
pjp.proceed();
} catch (Throwable t) {
// fency log
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CatchMe {}
EDIT: The functionality works but I want to verify it with the test.
Actually it is working as expected, however you are running in a side effect of proxy based AOP, especially class based proxies in this case.
Currently you are setting the field on the proxy and not on the actual object inside the proxy. Which is what you actually want. To obtain the actual instance use AopTestUtils.getUltimateTargetObject and then use that in the ReflectionTestUtils.setField method.
#Autowired
#InjectMocks
private MyService service;
#Mock
private MyServiceDependency serviceDep;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MyService serviceToInject = AopTestUtils.getUltimateTargetObject(service);
ReflectionTestUtils.setField(serviceToInject, "serviceDep", serviceDep);
}
However I think that approach is wrong, when you start messing around like this there is a better way. Simply use Spring to inject the mock. Create a specific #Configuration class for this test case. Make it a internal public static class and for the dependency add a mocked #Bean.
#Configuration
#Import(BeanConfig.class)
public static class TestBeanConfig {
#Bean
public MyServiceDependency myServiceDependency() {
return Mockito.mock(MyServiceDependency.class);
}
}
Now in your test class you can simply #Autowire both beans and not need to use reflection or whatever to set dependencies.
#RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
#Autowired
private MyService service;
#Autowired
private MyServiceDependency serviceDep;
#Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
Which will take care of the correct dependencies.
I had the same problem as #nyxz and this is intentional, see https://github.com/spring-projects/spring-boot/issues/7243.
Inspired by #M. Deinum following solution worked for me with Spring Boot 2.3.4.RELEASE and JUnit 5.
We will just provide a mocked bean without #MockedBean
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class MyServiceTest {
#Autowired
private MyService service;
#Test
public void test() {
service.execute();
}
static class TestBeanConfig {
#Bean
#Primary
public MyServiceDependency myServiceDependency() {
MyServiceDependency myServiceDependency = Mockito.mock(MyServiceDependency.class)
// Add behavior of mocked bean here
return myServiceDependency;
}
}
}
I have a #RequestScoped bean that calls a method from a #SessionScoped one. Inside the last one, I'm getting a NullPointerException when I try to get a java.util.logging.Logger. This doesn't happens if I call the same method from the #SessionScoped directly, not from the other bean...can somebody tell me why is this happening??
The #RequestScoped bean:
#RequestScoped
public class IndexBean {
#Inject
private Auth auth;
public void redirect() throws IOException {
auth.logout();
}
}
And the #SessionScoped one:
#SessionScoped
public class Auth implements Serializable {
public void logout() {
Logger.getLogger(Auth.class.getName()).log(Level.INFO,"Logout");
}
}
The exception arises when I call the redirect() method.