I want to catch bean instantiation exceptions in my code. What options do I have?
One way to do this is to use Java-based container configuration:
#Configuration
public class AppConfig {
#Bean
public SomeBean someBean() {
try {
return new SomeBean(); // throws SomeException
} catch(SomeException se) {
return new SomeBeanStub();
}
}
}
Is that possible to define exception handlers for bean instantiation using Spring using XML-based or annotation-based configuration?
Method someBean should catch SomeException and then throw BeanCreationException with SomeException as the cause:
#Configuration
public class AppConfig {
#Bean
public SomeBean someBean() {
try {
return new SomeBean(); // throws SomeException
} catch (SomeException se) {
throw new BeanCreationException("someBean", "Failed to create a SomeBean", se);
}
}
}
You are not suppose to do that. That is the whole point of having Spring create a bean for you. If you are to create your own beans using new (like above), why use Spring to create beans for you?
You can indeed allocate object for your self and work along instead of dependency injection and all.
Though I understand the essence behind the question. I think it is best if it fails during the server load time. Reason: The application wont be in an inconsistent state. Say suppose you catch the exception and do some cleanliness, but the other classes would be expecting for that bean to exist which it doesn't.
Hence best it fails at initialization so that the application is consistent. Though I do not know of any other legitimate way of doing.
Just for completeness.
You can also lazy init the bean and catch the exception the first time you use the bean.
spring config:
<bean id="a" class="A" lazy-init="true" />
In java:
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
public class B {
#Autowired
#Lazy
A a;
public void foo(){
try{
a.foo();
} catch (BeanCreationException e){
// ignore if you want
}
}
}
In case if you expect failures during bean creation and the bean is not mandatory for the rest of the application (pretty legitimate situation) you can do as you have suggested by catching all failure exceptions in the #Bean method body and returning null to indicate that the bean creation has failed.
The bean will not be added to the Spring application context in this case and the context construction will succeed in case if there are no mandatory dependencies on the given bean.
Related
Here is the situation. I have a starter which performs checks on every request. I have performed it by creating such an aspect:
#Around("execution(* (#org.springframework.web.bind.annotation.RestController *).*(..))")
public Object check(final ProceedingJoinPoint joinPoint) {
CheckResponse checkResponse = client.check();
if (checkResponse.getDate().before(new Date())) {
throw new CheckException("message");
}
try {
return joinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
Everything worked fine until I needed to use this CheckResponse in my rest controller.
How can I get it inside there? I thought about creating request scoped beans inside my aspect and injecting it into controller with spring but I can't figure out how to do it programmatically. I guess another way can be using reflection but I am inclined to more elegant ways. Maybe you have any ideas?
P.S. The controller method has its own parameters such as #RequestParam, and using joinPoint.proceed() can override those parameters. I am trying to make this thing as much universal as possible.
I want to call different methods that interact with my database in one method.
something like this :
#Autowired
EnteteService es; // service for jpaRepository
#Autowired
SystemOracleServiceJpa so; // service using jdbcTemplate
#Autowired
DetailService ds; // service for jpaRepository
#Transactional
public void configure(EnteteAR entete) throws ConfigurationException {
try{
this.es.save(entete); // first methode
List<DetailAR> details = this.so.getStructure(entete.getTableName());
if(details.size()>0){
this.ds.saveAllDetails(details); // second
this.so.CreateTable(details, entete.getTableName(), "DEM");//third
this.so.createPkIdxDem(entete.getTableName()); // fourth
this.so.CreateTable(details, entete.getTableName(), "BACK"); // fifth
}
else{
throw new ConfigurationException("Configuration error");
}
}catch(Exception e){
throw new ConfigurationException(e.getMessage());
}
}
I want to commit only if no errors appears in all this methods inside my main method "configure".
I was thinking that #transactionnal annotation work for this, but that commit after each method inside.
Exemple :
if this.es.save work and this.ds.saveAllDetails dont, I find data of es.save on database :(
Someone can help my please ?
thank with advance for your reading and your potential help.
#Transactional will automatically invoke a rollback if an unchecked exception is thrown from the executed method.
ConfigurationException in your case is a checked exception and hence it does not work.
You can make it work by modifying your annotation to
#Transactional(rollbackOn = ConfigurationException.class)
public void configure(EnteteAR entete) throws ConfigurationException {
try{ ....
Is there a way to catch DestinationResolutionException and MessageDispatchingException when using DSL? These exceptions usually indicate misconfiguration but I am not sure how I could configure my flow to catch these exceptions and apply some custom logic?
#SpringBootApplication
public class IntegrationMisconfigurationExampleApplication {
public static void main(final String[] args) {
SpringApplication.run(IntegrationMisconfigurationExampleApplication.class, args);
}
#Bean
public IntegrationFlow loggingFlow() {
return IntegrationFlows.from("input")
.<String, String>transform(String::toUpperCase)
// .nullChannel();
.get();
}
#Bean
public CommandLineRunner demo() {
return args -> {
final MessagingTemplate template = messagingTemplate();
template.convertAndSend("input", "abc");
};
}
#Bean
public MessagingTemplate messagingTemplate() {
return new MessagingTemplate();
}
}
The example above throws a DestinationResolutionException because loggingFlow.transformer#0 is not properly initialized. Is there a way to catch this exception?
Those exceptions are runtime errors. We really can't determine a misconfiguration at startup.
The way to catch runtime exception like that and do some analyzing work is with the ExpressionEvaluatingRequestHandlerAdvice, which you can add to your transform(String::toUpperCase) configuration in the second argument for endpoint configuration:
.<String, String>transform(String::toUpperCase, e-> e.advice(myExpressionEvaluatingRequestHandlerAdvice()))
See more info about this advice in the Reference Manual: https://docs.spring.io/spring-integration/docs/current/reference/html/#message-handler-advice-chain
Also you need to keep in mind that transformer is really a request-reply component with required non-null return value. Therefore you really can't configure a transform() just for one-way flow. It is going to throw an exception when there is no next channel in the flow or no replyChannel header in the message.
Below is what I did, I need to implement rollback, using #transactional annotation, but not working as expected, what else need to be done for proper rollback to happen ?? I want that when the code is executed result in db should be "testingOne" , currently it is set to "notRollBacked". Can you please point my mistake.
public Response deleteUser(Request argVO)throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setUserName("testingOne");
UsersLocalServiceUtil.updateUsers(users);
try
{
testRollbackFunction();
}
catch (Exception ex)
{
}
return new Response();
}
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
private void testRollbackFunction() throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setUserName("notRollbacked");
UsersLocalServiceUtil.updateUsers(users);
throw new PortalException();
}
****************Edit 1*************
I did what was mentioned in answers:
I did taken bean from context
and written a class/bean as
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
public class RollBack
{
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
public void thisWillRollBack() throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setBarringReason("notRollbacked");
UsersLocalServiceUtil.updateUsers(users);
throw new PortalException();
}
}
spring xml file bean refrence set as :
<bean id="rollBackBean" class="com.alepo.RollBack">
</bean>
public Response myMethod(Request argVO)throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setBarringReason("testingOne");
UsersLocalServiceUtil.updateUsers(users);
try
{
Test test = new Test();
Object obj = test.getBean();
RollBack rollBack = (RollBack)obj;
rollBack.thisWillRollBack();
}
catch (Exception ex)
{
ex.printStackTrace();
}
return new Response();
}
#################EDIT 4
now calling rollback function as :
RollBack rollBack = (RollBack)PortalBeanLocatorUtil.getBeanLocator().locate("rollBackBean");
rollBack.thisWillRollBack();
No Test class in picture now ...no new anywhere ...
still NOT WORKING .......
If you have a #Transactional annotation on method, Spring wraps the call to this method with aspect handling the transaction.
So:
1) Only public methodes can be wrapped in aspect
2) You call wrapped code only if you call the method on a bean taken from / injected by Spring container.
In your case:
1) The code isn't wrapped in transactional aspect because it is not public method
2) Event if it was, it is called directly from within the class, so you wouldn't call wrapped version anyway.
So the solution is to make separate bean with #Transactional method, inject it into and call it from Response class.
Of course you need <tx:annotation-driven/> in your spring-xml or instruct Spring otherwise to process #Transactional annotations (see the reference).
The issue is you are outside the application context. You are creating a new instance of a class, NEW is bad in Spring, very bad. Get an instance of Test from the application context, not by creating a new instance unless you start your application context in Test. Try to Autowire test in your class you mention above or inject it from Spring and then let me know, but the code you are showing above will never work with transaction management.
Sometimes my beans are not able to initialized properly due to external factors. Such as the MongoDB instance not being online. Is there a graceful way of handling the failed bean initializations? The following is the bean in question:
#Bean
public MorphiaDataSource morphiaDataSource() {
try {
MorphiaDataSource bean = new MorphiaDataSource();
Mongo mongo = new Mongo(mongoHost, mongoPort);
bean.setMongo(mongo);
bean.setMorphia(new Morphia());
bean.setDatabase(mongoDatabase);
bean.setUsername(mongoUsername);
bean.setPassword(mongoPassword);
return bean;
} catch(Exception e) {
logger.error("Error creating MorphiaDataSource: " + e.getMessage());
// Tell the context it's screwed?
}
return null;
}
If you rethrow the exception the context will stop loading and your application will be effectively dead. Or if you really want the JVM to completely stop call System.exit(1)