I'm using spring-retry to provide retry policy for my business logic. I have Interface and the service that implements it
public interface MyInterface {
#Retryable(maxAttempts = 5, backoff = #Backoff(value = 0L))
void doSth() throws Exception;
#Recover
void recoverIfFailed(Exception e);
}
#Service
public class MyService implements MyInterface {
public void doSth() throws Exception {
throw new Exception();
}
public void recoverIfFailed(Exception e) {
System.out.println("Recovered!");
}
}
and in this configuration everything working fine. However I cannot understand why I mustn't move #Recovery annotation to the interface implementation like this:
#Service
public class MyService implements MyInterface {
#Retryable(maxAttempts = 5, backoff = #Backoff(value = 0L)) // -- this is working
public void doSth() throws Exception {
throw new Exception();
}
#Recover // -- this is not working!
public void recoverIfFailed(Exception e) {
System.out.println("Recovered!");
}
}
I really would like to not expose my recovery method in the interface (as it seems to be my internal logic) but I cannot due to this issue. Can anyone advise what can be an issue?
I submitted an open pull request to fix this.
As a work-around, use #EnableRetry(proxyTargetClass = true).
Related
I want to assert an exception that should be thrown within an #Async void method.
The following fails, even though I already add a SyncTaskExecutor explicit.
org.opentest4j.AssertionFailedError: Expected RuntimeException to be thrown, but nothing was thrown.
#TestConfiguration
public class SyncTaskExecutorTestConfiguration {
#Bean
#Primary
public TaskExecutor asyncExecutor() {
return new SyncTaskExecutor();
}
}
#SpringBootTest
#Import(SyncTaskExecutorTestConfiguration.class)
public class MyTest {
#Test
public void test() {
assertThrows(RuntimeException.class, () -> service.run());
}
}
#Service
#Async //also #EnableAsync existing on #Configuration class
public class AsyncService {
public void run() {
//of course real world is more complex with multiple sub calls here
throw new RuntimeException("junit test");
}
}
I'm facing the same problem.
bilak's post gave the idea of having my custom AsyncUncaughtExceptionHandler declared with a #Component annotation.
Then, in my custom implmentation of AsyncConfigurer I was injecting my custom AsyncUncaughtExceptionHandler.
In my tests, I used the #MockBean annotation on my custom AsyncUncaughtExceptionHandler, so I was able to verify that the handleUncaughtException was called with the appropriate exception.
Code sample:
AsyncExceptionHandler
#Slf4j
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("Exception while executing with message: {} ", throwable.getMessage());
log.error("Exception happen in {} method ", method.getName());
}
}
CustomAsyncConfigurer
#Configuration
public class CustomAsyncConfigurer implements AsyncConfigurer {
final private AsyncExceptionHandler asyncExceptionHandler;
#Autowired
public TaskExecutorConfiguration(AsyncExceptionHandler asyncExceptionHandler) {
this.asyncExceptionHandler = asyncExceptionHandler;
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("AsyncThread::");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
My unit test:
class FooServiceTest extends FooApplicationTests {
#MockBean
private AsyncExceptionHandler asyncExceptionHandler;
#Autowired
private FooService fooService;
#Test
void testCreateEnrollmentBioStoreException() throws Exception {
fooService.doBar();
ArgumentCaptor<FooException> argumentCaptor = ArgumentCaptor.forClass(FooException.class);
verify(asyncExceptionHandler, times(1)).handleUncaughtException(argumentCaptor.capture(), any(), any());
FooException exception = argumentCaptor.getValue();
assertEquals("Foo error message", exception.getMessage());
}
}
I'm not sure if this is the right way, but I have a void method that was turned into async, so I didn't want to change the return value just for the tests.
Since the #Async method get executed asynchronously by a thread from asyncExecutor and it is terminated due to RuntimeException which doesn't have any impact on Main thread, the actually Main-Test thread competes successfully with the rest of flow once after it trigger the async call. So i will recommend to use the CompletableFuture to hold the reference of Async process always even it's required or not and truthfully will help in test cases
#Service
#Async
public class AsyncService {
public CompletableFuture<Void> run() {
//of course real world is more complex with multiple sub calls here
throw new RuntimeException("junit test");
}
}
So in the test you can wait for Async thread to complete assert the cause from ExecutionException, Since the get method throws ExecutionException if this future completed exceptionally
CompletableFuture.allOf(wait);
One more note you can refer link for asserting wrapped exceptions
What about using AsyncUncaughtExceptionHandler that will be defined for your AsyncConfigurer?
So basically when you execute your method which throws exception you can verify that exception was handled inside handler? Just an idea, didn't tried this.
I have #transactional method that seems to be working (rolling back) if run the actual service and provide inputs that would cause a run-time error. If I create a Test for that method that throws a run-time error it doesn't seem to rollback anymore. Any idea why this doesn't work while testing?
it's somthing like:
#Service
public class SampleServiceImpl implements SampleService {
private final RepoA repoA;
private final RepoB repoB;
public SampleServiceImpl(RepoA repoA, RepoB repoB) {
this.repoA = repoA,
this.repoB = repoB
}
#Transactional
#Override
public void addItems() {
repoA.save(new ItemA(1,'name1')); //works
repoB.save(new ItemB(2,'name2')); //throws run-time error
}
}
#RunWith(SpringRunner.class)
#DataJpaTest
public class Tests {
#Autowired
private RepoA repoA;
#Mock
private Repob repoBMock;
#Test
public void whenExceptionOccurrs_thenRollsBack() {
var service = new SampleService(repoA, repoBMock);
Mockito.when(repoBMock.save(any(ItemB.class))).thenThrow(new RuntimeException());
boolean exceptionThrown = false;
try {
service.addItems()
} catch (Exception e) {
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
Assert.assertFalse(repoA.existsByName('name1')); // this assertion fails because the the first item still exists in db
}
}
Just add annotation Rollback and set the flag to false.
#Test
#Rollback(false)
In my Backend, I have added #PreAuthorize("hasRole('ROLE_ADMIN') to allow user to access the functions in service layer. And now I would like to use my schedule job (springframework scheduling) to access these service but obviously it can not. My question is how can I add ROLE_ADMIN role or generate a user principal for the schedule job?
#PreAuthorize("hasRole('ROLE_ADMIN')")
JsonNode loadSMS(String additionalPath) {
.....
}
Either have another method not annotated with the #PreAuthorize which your scheduler calls. Move the implementation into this new method, and change the existing loadSMS to use this new method to reduce code duplication. Otherwise you could add a role at runtime, but I don't think that is such a great idea.
You can try below code
#Service
class SchedulerService {
#Autowired
private YourService service;
#Scheduled(fixedRate = 600000L, initialDelay = 60000L)
public void executeTask() throws IOException {
RunAs.runAsAdmin(() -> {
service.loadSMS(String additionalPath) {
});
}
}
public class RunAs {
#FunctionalInterface
public interface RunAsMethod {
default void run() {
try {
runWithException();
} catch (Exception e) {
}
}
void runWithException() throws Exception;
}
public static void runAsAdmin(final RunAsMethod func) {
final AnonymousAuthenticationToken token = new AnonymousAuthenticationToken("adminUser", "adminPassword",
ImmutableList.of(new SimpleGrantedAuthority("ROLE_ADMIN")));
final Authentication originalAuthentication = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder.getContext().setAuthentication(token);
func.run();
SecurityContextHolder.getContext().setAuthentication(originalAuthentication);
}
}
I have the following classes:
public FooDAO extends AbstractDAO<Foo> { // Dropwizard DAO
#Inject FooDAO(SessionFactory sf) { super(sf); }
public void foo() { /* use SessionFactory */ }
}
public class FooService {
private final FooDAO fooDAO; // Constructor-injected dependency
#Inject FooService (FooDAO fooDAO) { this.fooDAO = fooDAO; }
#UnitOfWork
public void foo() {
this.fooDAO.foo();
System.out.println("I went through FooService.foo()");
}
}
Now, FooService is not a resource, so Dropwizard doesn't know about it and doesn't automagically proxy it. However the smart guys at Dropwizard made it so I can get a proxy through UnitOfWorkAwareProxyFactory.
I tried doing feeding these proxies to Guice with an interceptor, but I faced an issue because UnitOfWorkAwareProxyFactory only ever creates new instances and never lets me pass existing objects. The thing with new instances is that I don't know the parameters to give it since they're injected by Guice.
How do I create #UnitOfWork-aware proxies of existing objects?
Here's the interceptor I've made so far:
public class UnitOfWorkModule extends AbstractModule {
#Override protected void configure() {
UnitOfWorkInterceptor interceptor = new UnitOfWorkInterceptor();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(UnitOfWork.class), interceptor);
requestInjection(interceptor);
}
private static class UnitOfWorkInterceptor implements MethodInterceptor {
#Inject UnitOfWorkAwareProxyFactory proxyFactory;
Map<Object, Object> proxies = new IdentityHashMap<>();
#Override public Object invoke(MethodInvocation mi) throws Throwable {
Object target = proxies.computeIfAbsent(mi.getThis(), x -> createProxy(mi));
Method method = mi.getMethod();
Object[] arguments = mi.getArguments();
return method.invoke(target, arguments);
}
Object createProxy(MethodInvocation mi) {
// here, what to do? proxyFactory will only provide objects where I pass constructor arguments, but... I don't have those!
}
}
}
Of course, if Dropwizard (or Guice) offers me a simpler way to do so, which is it?
As from Dropwizard 1.1: (not yet released, as of August 10, 2016)
public class UnitOfWorkModule extends AbstractModule {
#Override
protected void configure() {
UnitOfWorkInterceptor interceptor = new UnitOfWorkInterceptor();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(UnitOfWork.class), interceptor);
requestInjection(interceptor);
}
#Provides
#Singleton
UnitOfWorkAwareProxyFactory provideUnitOfWorkAwareProxyFactory(HibernateBundle<AlexandriaConfiguration> hibernateBundle) {
return new UnitOfWorkAwareProxyFactory(hibernateBundle);
}
private static class UnitOfWorkInterceptor implements MethodInterceptor {
#Inject
UnitOfWorkAwareProxyFactory proxyFactory;
#Override
public Object invoke(MethodInvocation mi) throws Throwable {
UnitOfWorkAspect aspect = proxyFactory.newAspect();
try {
aspect.beforeStart(mi.getMethod().getAnnotation(UnitOfWork.class));
Object result = mi.proceed();
aspect.afterEnd();
return result;
} catch (InvocationTargetException e) {
aspect.onError();
throw e.getCause();
} catch (Exception e) {
aspect.onError();
throw e;
} finally {
aspect.onFinish();
}
}
}
}
Is there way to bind a method interceptor to a provider rather than an instance?
e.g. I use the code below to bind interceptors how would I bind INTERCEPTOR to a provider and then to the annotation?
bindInterceptor(
Matchers.any(), Matchers.annotatedWith(ANNOTATION.class), new INTERCEPTOR());
Guice does not allow AOP on instances not built by Guice: Guice AOP Limitations
"Instances must be created by Guice by an #Inject-annotated or no-argument constructor"
This means that instances created with a provider will not be candidates for AOP.
On the flip side, as long as your Provider is instantiated by Guice under the conditions mentioned, your Provider may be a candidate for AOP.
Here's an example that demonstrates this:
AOP Annotation:
#Retention(RetentionPolicy.RUNTIME) #Target(ElementType.METHOD)
#interface AOPExample {}
Provider:
public class ExampleProvider implements Provider<Example> {
#AOPExample
public Example get() {
System.out.println("Building...");
return new Example();
}
}
Target Example:
public class Example {
#AOPExample
public void tryMe() {
System.out.println("example working...");
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Module:
public class ExampleModule extends AbstractModule {
#Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AOPExample.class), new LoggingAOP());
bind(Example.class).toProvider(ExampleProvider.class);
}
}
Test Code:
public class Test {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new TestModule());
ExampleProvider exampleProvider = injector.getInstance(ExampleProvider.class);
Example example = exampleProvider.get();
example.tryMe();
Example directExample = injector.getInstance(Example.class);
directExample.tryMe();
}
}
Test Output:
start
Building...
end took: 3
example working...
start
Building...
end took: 0
example working...
Notice that the "example working..." is not surrounded by the timer code. The Provider.get ("Building...") is however.
If your question is: can the interceptor (new INTERCEPTOR()) be provided through a Guice Provider, the answer is no. The closest you may get to this functionality is calling the requestInjection() in the module configure method. This will inject your Interceptor with the appropriate code. From your interceptor you may be able to use Providers to avoid any sort of overhead that is causing you slowness during startup.
Here's what I mean:
Module:
public class TestModule extends AbstractModule {
#Override
protected void configure() {
bind(String.class).toInstance("One");
bind(String.class).annotatedWith(Names.named("two")).toInstance("Two");
LoggingAOP loggingAOP = new LoggingAOP();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AOPExample.class), loggingAOP);
requestInjection(loggingAOP);
bind(Example.class).toProvider(ExampleProvider.class);
}
}
Interceptor:
public class LoggingAOP implements MethodInterceptor {
#Inject
private Provider<SomethingThatTakesALongTimeToInit> provider;
public Object invoke(MethodInvocation invocation) throws Throwable {
provider.get()...
System.out.println("start");
long start = System.currentTimeMillis();
Object value = invocation.proceed();
System.out.println("end took: " + (System.currentTimeMillis() - start));
return value;
}
}
Hope this answers your question.
The question, as I read it, is, how does one bind the interceptor type itself to a provider, rather than having to instantiate the interceptor at configuration time.
I don't think there's an easy way to do that, but one could write an interceptor that itself accepts a Provider for an implementation type. An example of this is shown in the Guice AOP documentation:
public class NotOnWeekendsModule extends AbstractModule {
protected void configure() {
bindInterceptor(any(),
annotatedWith(NotOnWeekends.class),
new WeekendBlocker(getProvider(Calendar.class)));
}
}