I have a Spring Boot application with multiple RestControllers, each of them connecting to different databases based on the services they are calling.
If any of them can't connect to the database for whatever reason, I'm throwing a message stating so like this:
#RestController
#RestControllerAdvice
public class GlobalExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
// The Network Adapter could not establish the connection
#ExceptionHandler(value = SQLRecoverableException.class)
public ResponseEntity<String> handleBaseException(Exception e) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("Service Unavailable.");
}
}
Now, is it possible to identify which RestController triggered this exception?
I don't know how to connect a Global Exception to a Rest Controller when I'm using #RestControllerAdvice
Spring provide two level of Exception handling,
1. Controller level.
2. Global level.
Use controller level exception handling, If you need to add something based on controller basis.
Global level handling meant to be handle exception irrespective of knowing which controller exception belongs to.
Hope this answers your question. Better to go for controller level exception handing in your case.
Comment if you need more information.
Related
I'm tasked with removing functionality given by #ControllerAdvice(basePackages = {"com.bla.controller"}) located in an old applications. This functionality became outdated, creating some unexpected error handling issues, after another "global" functionality utilizing #ControllerAdvice got installed by a separate team.
While this "global" functionality covers most needs, the old application still has a need for custom error handling, but without #ControllerAdvice.
My knowledge of Spring Annotations is very limited, and I'm hoping that someone may have a good idea what can be used instead so that the old application would have awareness where to send errors for error handling.
Here is an implementation sample from the file utilizing #ControllerAdvice(basePackages = {"com.bla.controller"}) notation showing both custom and basic usage in the old application:
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(ResourcePageException.class)
public String notFound(Model model, ResourcePageException exception) {
setAttributes(model);
populateModel(model, exception);
return Mapping.NO_RESULTS_VIEW;
}
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(NoCustomSearchResultsRestException.class)
public ResponseEntity<Object> locationLookupRestError(NoCustomSearchResultsRestException exception) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getModel());
}
So far I wasn't able to find a way to move all of the #ExceptionHandler methods into a single place without the use of #ControllerAdvice, but I did find this page summarizing Spring's error handling that I found useful in terms of understanding error handling by Spring: Using Spring Boot's ErrorController and Spring's ResponseEntityExceptionHandler correctly
In my application I am currently creating a JpaRepository via the usual mechanism, i.e. by defining an interface like so:
public interface HistoryInfoRepository extends JpaRepository<HistoryInfo, Long>
This works all fine BUT if the DB is not available or not accessible when starting up the application then the creation of the repo bean fails, the injection into the respective service (which receives that repo as an autowired constructor argument) fails and as a consequence my applications main class:
SpringApplication.run(Application.class, args);
fails as well. I.e. the entire application just fails with a gigantic stacktrace.
I would want to make situation a bit more user friendly in that the application at least comes up so far that it is able to display some (hopefully) helpful error message with an explanation and some hints what should be checked for.
That would require to NOT autowire the JpaRepository but create it programmatically, e.g. in said service's constructor but wrapped within some try - catch construct.
I don't want to give up that convenience that the JpaRepository mechanism provides but how can one create such a JpaRepository programmatically when there is only an interface defined and everything else is done automagically by Spring?
Is there some API-call to create such a JpaRepository for a given interface?
I would like to see something like this:
public HistoryInfoService( /* HistoryInfoRepository historyRepo */) {
...
try {
historyRepo = createJpaRepositoryFromInterface(HistoryInfoRepository.class);
} catch (Exception ex) {
// inform user about missing or non-reachable DB server and suggest remedies...
}
...
}
Here I commented away the constructor argument which is normally provided via auto-wiring, but instead I want to create the JpaRepository using some method-call. That would allow me to properly react to the failed Repo-creation as indicated by the comment in the catch clause.
I searched for something like that but I must have been using the wrong search terms and found nothing so far. Any hints?
I have below maven dependency & configuration set up
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
#Configuration
#EnableMongoAuditing
public class MongoConfig {
#Bean
MongoTransactionManager transactionManager(MongoDbFactory mongoDbFactory) {
return new MongoTransactionManager(mongoDbFactory);
}
}
Updated: I've taken the suggested solution to create a bean with #Transactional, and have it injected into my test class. Below is the service bean I created:
#Service
#Transactional
#RequiredArgsConstructor
public class MongoTransactionService {
private final UserRepo userRepo;
public void boundToFail() throws RuntimeException {
userRepo.save(User.builder().id("1").build());
throw new RuntimeException();
}
}
and test class where I inject a bean of MongoTransactionService:
#DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class,
includeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MongoTransactionService.class))
#ExtendWith(SpringExtension.class)
class MongoTransactionServiceTest {
#Autowired
UserRepo userRepo;
#Autowired
MongoTransactionService mongoTransactionService;
#Test
void testTransactional() {
try {
mongoTransactionService.boundToFail();
} catch (Exception e) {
// do something
}
val user = userRepo.findById("1").orElse(null);
assertThat(user).isNull();
}
}
I am expecting a call to boundToFail(), which throws a RuntimeException, would roll back the saved user, but the user still gets persisted in the database after the call.
It turns out that #DataMongoTest doesn't activate the auto-configuration for MongoDB transactions. I've filed a ticket with Spring Boot to fix that. In the mean time, you can get this to work by adding
#ImportAutoConfiguration(TransactionAutoConfiguration.class)
to your test class.
Note that using MongoDB transactions requires a replica set database setup. If that's not given the creation of a transaction will fail and your test case will capture that exception and the test will still succeed. The data will not be inserted but that's not due to the RuntimeException being thrown but the transaction not being started in the first place.
The question previously presented a slightly different code arrangement that suffered from other problems. For reference, here's the previous answer:
#Transactional needs to live on public methods of a separate Spring bean as the transactional logic is implemented by wrapping the target object with a proxy that contains an interceptor interacting with the transaction infrastructure.
You example suffers from two problems:
The test itself is not a Spring bean. I.e. there's no transactional behavior added to boundToFail(…). #Transactional can be used on JUnit test methods but that's controlling the transactional behavior of the test. Most prominently, to roll back the transaction to make sure changes to the data store made in the test do not affect other tests. See this section of the reference documentation.
Even if there was transactional logic applied to boundToFail(…), a local method call to the method would never trigger it as it doesn't pass the proxy that's applying it. See more on that in the reference documentation.
The solution to your problem is to create a separate Spring bean that carries the #Transactional annotation, get that injected into your test case and call the method from the test.
When implementing MVC project, I usually add Service Layer to perform the actual work. But actually sometimes 1 Web Request should be done with several AppService methods. Then the location of Unit-of-Work (UoW) may affect the coding handling.
No matter in C# EF/Java Spring, there's Transaction annotation in Service Layer methods, so the transaction is Per-Service based (i.e. UoW on Service layer). Let's take Java version as example here:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
Public class UserAppService{
public UserDTO createUser() {
// Do sth to create a new user
userRepository.save(userBean);
// Convert userBean to userDTO
return userDTO;
}
public xxx DoSth() {
// Break the operation here
throw new Exception("Whatever");
// (never execute actually)
sthRepository.save(someBean);
}
}
Then in Controller:
Public class SomeController : Controller {
Public xxx DoSth(){
UserAppService Service = new UserAppService();
Service.CreateUser(); // DB committed
Service.DoSth(); //Exception thrown
}
}
With this structure, If there's any exception thrown on 2nd service method call, the 1st service method still commit the user to the DB. If I want "all-or-nothing" handling, this structure doesn't work unless I wrap those service method calls into another wrapper service call with single transaction. But it's sort of extra work.
Another version is using transaction on Controller action level instead (i.e. UoW on Controller Action). Let's take C# code as example:
Remarks: AppService in code version 2 here use the DbContext (sth like transaction) defined in controller, and doesn't do any commit inside.
Public class SomeController : Controller {
Public ActionResult DoSth(){
using (var DB = new DbContext()){
Var UserAppService = new UserAppService(DB);
var userEntity = userAppService.GetUser(userId);
UserAppService.DoSth(userEntity);
Var AnotherAppService = new AnotherAppService(DB);
AnotherAppService.DoSthElse(userEntity);
// Throw exception here
throw new Exception("Whatever");
DB.Save(); // commit
}
}
}
In this example, there will be no partial commit to the DB.
Is applying UoW on service-layer really better?
Is applying UoW on service-layer really better?
IMO No. And you've just figured out why. If the service methods are discreet and re-usable, they are also not suitable for being atomic transactions.
In .NET the controller should control the transaction lifecycle, and enlist service methods in the transaction.
Note that this also implies that the service methods should be local method calls, not remote or web service calls.
It is better because your following the main principle of Object Oriented Programming seperation of concerns.What if you made another controller and wanted to do more database processing using the same object? You dont want to instantiate the controller in which your doing something completely different.By the way check out the facade service pattern http://soapatterns.org/design_patterns/service_facade it may help you understand why its so sexy. .Hi the image above shows the pattern, basically you wrap your database access objects with transactional at the service layer so a customerService object can wrap 1,2...inf transactions and either all fail or succeed.
I am having a tough time to understand why this code is failing
I have a test method
IUnitOfWork unitofwork = EFUnitOfWork.CreateInstance();
IRepository<InformationRequest> informationRequestRepository = unitofwork.CreateRepository<InformationRequest>();
IEnumerable<InformationRequest> requests = informationRequestRepository.ToList();
unitofwork.Dispose();
EFUnityOfWork.CreateInstance calls the EFUnitOfwork Constructor
public EFUnitOfWork()
{
_currentContext = new MyDataContext();
}
Here is the code for CreateRepository
public IRepository<T> CreateRepository<T>()
{
return new Repository<T>(_currentContext);
}
The test above doesnt work on a load test. When i try to run it it says
System.Data.EntityException: The underlying provider failed on Open. ---> System.InvalidOperationException: The connection was not closed. The connection's current state is connecting.
I am disposing the context and creating a new one everytime. I dont understand where i am going wrong
Your code EFUnitOfWork.CreateInstance() is a static method.
When 2 threads call this at the same time they could get back the same context. Then you could get the error that you see.
You could fix it by locking such that it is only called by one thread at a time. But time would introduce a performance bottleneck.