Can I ignore BeanCreationException and inject null instead? - java

We have a situation where our Spring wires up some beans that include ActiveMQ classes built with Java 6. Our application runs on customer's servers, so we can't guarantee that they have Java 6 or later installed. If they happen to have Java 5, the application can't start because of BeanCreationExceptions with the classes that depend on ActiveMQ (root cause being a UnsupportedClassVersionError).
So my question is, is there any way to ignore a BeanCreationException and still start the application? I want to be able to display an error message saying that they need to install Java 6 or later, but since the application can't even start, I never have a chance to do this.
My hunch is that there is no way to do this, because Spring needs to be able to guarantee that my application is built properly after initialization, but I thought I would ask anyway. Any other suggestions for how to accomplish my end goal would also be helpful and appreciated.
We are using Spring 3.0.6
Thanks!

If you can upgrade to Spring 3.1 (stable), take advantage of Java configuration:
#Bean
public SomeBean public someBean() {
if(isEverythingOkWithJavaVersion()) {
return new CorrectBean();
} else {
return null;
}
}
or:
#Bean
public SomeBean public someBean() {
try {
return new CorrectBean();
} catch(UnsupportedClassVersionError e) {
log.warn("", e);
return null;
}
}
In older versions of Spring FactoryBean might be used to implement the same logic. Instead of returning null you might also return some fake implementation that you can discover later and warn the user when the application tries to use it.

First off, any throwables that are a subclass of java.lang.Error are generally considered to be non-recoverable. So while it's possible to catch them, it's strongly discouraged:
An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.
However, if all you're going to do is display an error message, then you should be able to get away with it.
SO to get back to your question, I suggest creating an implementation of Spring's FactoryBean interface, which would try to load the ActiveMQ classes. If it works, then it can return the appropriate object from FactoryBean.getObject. If it fails (via a caught UnsupportedClassVersionError), then it can return either a null, or some other object representing that condition.

You could create a factory bean and let it create the actual ActiveMQ bean. If it can't be initialized the factory could return a dummy/mock implementation so things don't break. Later you could ask the factory if everything went alright.
http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-extension-factorybean

Related

Registering Hystrix Concurrency Strategy fails after migrating to spring-boot-2.0

Trying to register hystrix concurrency strategy fails after migrating to Spring-boot-2.0 with spring actuator enabled with java.lang.IllegalStateException stating that "Another strategy was already registered" . This is although I have not used registerConcurrencyStrategy anywhere else in my code.
I'd like to register concurrency strategy to carry-forward Log MDC context so that I'm able to log both within and outside the Hystrix wrapped method equally well, which includes thread-locals. And this used to work perfectly in spring-boot-1.5
After having migrated to spring-boot 2.0 (from 1.5), the HystrixPlugins.getInstance().registerConcurrencyStrategy(this); fails with IllegalStateException
As per https://github.com/Netflix/Hystrix/issues/1057, this issue can come if either (a) Any other code flow would have registered its own or default ConcurrencyStrategy before this is invoked (b) any call would have come via Hystrix before this is invoked
Since the above invocation is within the constructor of a class which is annotated with #Component, this should get invoked ideally before any method call happens (except initialization of other beans, including their constructors).
We even tried moving this code inside the SpringBoot Application Class's main method before invoking the SpringApplication.run(MyApplication.class, args); but that also didn't work
#Component
public class ContextCopyHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final String EVENT = "HystrixConcurrencyStrategy";
private static final String ACTION = "ContextCopy";
public ContextCopyHystrixConcurrencyStrategy(Logger logger, LoggerUtil defaultLoggerUtil) {
try {
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
} catch (IllegalStateException e) {
defaultLoggerUtil.logEvents(logger, Level.WARN, e.getMessage(), EVENT, ACTION, "", "Race condition! Could not register strategy. HystrixConcurrencyStrategy is already initialized.");
}
Expected: My registering should have happened before any other code and registering should have been successful
Actual: My registering fails with IllegalStateException
How do I make sure that my registering happens well before any other registering (which is not present in my code, but may be inside some of the libraries that I may be transitively using)
By default, Spring boot 2 accuator registers Hystrix Metric Binder beans which reset already set HystrixConcurrencyStrategy and sets HystrixConcurrencyStrategyDefault.
So, disabling that bean by
management.metrics.binders.hystrix.enabled=false
would help not resetting your custom HystrixConcurrencyStrategy
We took a close look at my maven .m2 directory classes and looked for registerConcurrencyStrategy inside all the classes in all the jars. And we found that
io.micrometer.core.instrument.binder.hystrix
was internally registering the HystrixConcurrencyStrategy with the default one.
And upon further research we found that setting the following property in application.properties:
management.metrics.binders.hystrix.enabled=false disabled the Hystrix Metrics Binder (I'm actually not sure what it does though!) and then things worked
I was using spring-boot-starter-parent v2.5.3 with Spring Cloud version 2020.0.3.
I had to manually include version for spring-cloud-starter-netflix-hystrix. I was getting "Another strategy was already registered" exception when starting my microservice. I included the
management.metrics.binders.hystrix.enabled=false
in the application.properties file and this issue got resolved.

`ProvisionException` is cached and constructor code is never retried

I have a Java web service built using PlayFramework 2.6.5 and Guice DI (libraryDependencies += guice), just in time injection mode. All dependencies are injected via constructor, using #Inject and #ImplementedBy, and the Guice Module is empty.
Due to transient errors, some dependencies can throw an exception in the constructor. When this happens the service fails with a ProvisionException (which is ok, clients are expected to retry).
I found that these exceptions are cached, and even when the root cause of the exception is solved, either Play or Guice never retry instantiating these classes, and keep trowing the same exception until the web service is restarted.
Consider for example the following class Clock with a constructor that fails if it's midnight (00:xx). As soon as the system clock reaches midnight, the service fails to instantiate the class. When clock reaches 1am, the same exception keeps being thrown. Also, the exception message is always the same (in the example the exception message is the time of the first time an exception occurred)
#ImplementedBy(OddClock.class)
public interface IClock {
//...
}
public class OddClock implements IClock {
#Inject
public OddClock() throws Exception {
if (DateTime.now().hourOfDay().get() == 0) {
throw new Exception(DateTime.now().toString());
}
}
}
public class TimeController {
#Inject
public TimeController(IClock clock) {
this.clock = clock;
}
}
btw, the same codebase is used also in a console application, which doesn't suffer from this issue, so I'm thinking there's something special in Play+Guice integration. Any suggestion to turn off the exception caching?
Throwing exceptions and caching the exceptions seems like a built-in behaviour in Guice. It's a fair behaviour too, since Guice expects objects that it creates to avoid IO and other non-deterministic actions.
https://github.com/google/guice/wiki/BeCarefulAboutIoInProviders
Provider doesn't define a retry-strategy. When a value is unavailable, calling get() multiple times may cause multiple failed provisions.
You may be able to avoid the caching by changing the scope that you use so that the instance is recreated each time. E.g. using transient scoping instead of singletons.
A better solution, in my opinion, would be to get your unreliable object and wrap it in another object that hides failures and handles retries. That way Guice would always succeed when it tries to create the reliable object and you could add your own failure handling code inside the reliable wrapper.
E.g. a simple example that retries construction:
public class ReliableClock {
private Factory<Clock> clockFactory;
private Clock internalClock;
public ReliableClock(Factory<Clock> clockFactory) {
this.clockFactory = clockFactory;
}
private synchronized Clock currentClock() throws Exception {
if (clock == null) {
clock = clockFactory.create() // May throw exception
}
return clock;
}
// ... methods ...
}
I found a solution to make PF's behavior more explicit and predictable. The Routes generated by PF by default caches controllers even when their instances are broken, assuming singleton controllers are what a user wants.
As described here, the default behavior can be changed adding a # in front of each action in the routes configuration.
e.g., before:
GET /test webservice.TestController.test
after:
GET /test #webservice.TestController.test
With this syntax, controllers are not singleton-by-default, and one can still use #Singleton where needed. Also, singleton controllers are not cached in case of exceptions, allowing transient errors to recover without restarting the service.
A copy of the code is available here.

How do you enable AspectJ to execute advice on a joinpoint called by a method of the same class?

I'm implementing an AOP-based caching layer similar to Spring Cache, and I'm having trouble getting my advice to execute when the joinpoint is called by another method in its own class. I was initially using Spring AOP's AspectJAutoProxy, and I understand why that doesn't allow for this use case, so I tried switching to AspectJ's load-time weaver, but it doesn't appear to have done anything. Here are all the details:
Spring version: 4.5.2
AspectJ version: 1.8.9
Java agent(s) added to run command. Note that I've tried including each of the two agents separately and together, without any difference in behavior:
-javaagent:/var/app/cops/jars/aspectjweaver-1.8.9.jar -javaagent:/var/app/cops/jars/spring-instrument-4.3.0.RELEASE.jar
Application entrypoint:
#Configuration
#EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
public class Application implements ApplicationContextAware {
#Bean
public InstrumentationLoadTimeWeaver loadTimeWeaver() throws Throwable {
InstrumentationLoadTimeWeaver loadTimeWeaver = new InstrumentationLoadTimeWeaver();
return loadTimeWeaver;
}
// Other unrelated beans
}
Advice signature:
#Around("#annotation(cacheable)")
public Object processCacheable(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable
Method signature:
#Cacheable(key = "'test-key'")
public Map<String, Object> getDataFromSource()
"processCacheable" gets executed before "getDataFromSource" is called by another class, but still not if called from within the same class. Is there some configuration I'm missing in order to get LTW working correctly?
My usual disclaimer: I am not a Spring user. So I have next to zero experience with Spring configuration. But having looked at the weaving agent you put on your command line, I cannot find any AspectJ-related stuff in there. It is a really small JAR with only instrumenting classes that seem to rely on other transformers/weavers being deployed and somehow configured in your container.
I suggest you put the actual AspectJ weaving agent on your command line and see what happens. ;-)
Update: Or you if that does not help try both agents on the command line at the same time. I cannot tell you exactly what spring-instrument does, but I think it somehow integrates aspectjweaver (which does the actual aspect weaving) deeper into the Spring framework, even though AspectJ would also work without Spring even knowing of its existence.

Spring: Don't fail on bean initialization error

I know one of the purposes of Spring is to fail-fast if any bean initialization fails, but I'm curious if the following behavior is possible.
I have an application that depends on many external servers and DBs, and so there are a dozen beans that are interfaces for the different external services. Config values of the form 'db1.enabled=true' and 'db2.enabled=false' define whether these beans should even be initialized. What I want is a 'best effort' scenario where, if db1.enabled is set to true, but the db in question is not reachable for whatever reason and initialization of the bean that calls db1 fails, the whole system shouldn't crash. The effect should be as if db1.enabled was set to false, with ideally an error message somewhere indicating that this occurred. Doing this for all beans would defeat the purpose of Spring, but it would be useful for certain ones.
If you use Java Config you can run basically any code as bean initialization. You can, for example, put something like this in your config:
#Configuration
public class MyConfig{
#Bean
public DataSource mainDataSource(){
try{
// initialize your basic datasource here
} catch(Exception e) {
// try something else here
}
}
}

JMX MXBean Attributes all UNDEFINED - Spring 3.0.x/Tomcat 6.0

I've been trying to get a sample JMX MXBean working in a Spring-configured webapp, but any basic attributes on the MXBean are coming up as UNDEFINED when I connect with jconsole.
Java interface/classes:
public interface IJmxBean { // marker interface for spring config, see below
}
public interface MgmtMXBean { // lexical convention for MXBeans - mgmt interface
public int getAttribute();
}
public class Mgmt implements IJmxBean, MgmtMXBean { // actual JMX bean
private IServiceBean serviceBean; // service bean injected by Spring
private int attribute = 0;
#Override
public int getAttribute() {
if(serviceBean != null) {
attribute = serviceBean.getRequestedAttribute();
}
return attribute;
}
public void setServiceBean(IServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
}
Spring JMX config:
<beans>
<context:component-scan base-package="...">
<context:include-filter type="assignable" expression="...IJmxBean" />
</context:component-scan>
<context:mbean-export />
</beans>
Here's what I know so far:
The element is correctly instantiating a bean named "mgmt". I've got logging in a zero-argument public constructor that indicates it gets constructed.
is correctly automatically detecting and registering the MgmtMXBean interface with my Tomcat 6.0 container. I can connect to the MBeanServer in Tomcat with jconsole and drill down to the Mgmt MXBean.
When examining the MXBean, "Attribute" is always listed as UNDEFINED, but jconsole can tell the correct type of the attribute. Further, hitting "Refresh" in jconsole does not actually invoke the getter method of "Attribute"- I have logging in the getter method to indicate if it is being invoked (similar to the constructor logging that works) and I see nothing in the logs.
At this point I'm not sure what I'm doing wrong. I've tried a number of things, including constructing an explicit Spring MBeanExporter instance and registering the MXBean by hand, but it either results in the MBean/MXBean not getting registered with Tomcat's MBean server or an Attribute value of UNDEFINED.
For various reasons, I'd prefer not to have to use Spring's #ManagedResource/#ManagedAttribute annotations.
Is there something that I'm missing in the Spring docs or MBean/MXBean specs?
ISSUE RESOLVED: Thanks to prompting by Jon Stevens (above), I went back and re-examined my code and Spring configuration files:
Throwing an exception in the getAttribute() method is a sure way to get "Unavailable" to show up as the attribute's value in JConsole. In my case:
The Spring JMX config file I was using was lacking the default-autowire="" attribute on the root <beans> element;
The code presented above checks to see if serviceBean != null. Apparently I write better code on stackoverflow.com than in my test code, since my test code wasn't checking for that. Nor did I have implements InitializingBean or #PostConstruct to check for serviceBean != null like I normally do on almost all the other beans I use;
The code invoking the service bean was before the logging, so I never saw any log messages about getter methods being entered;
JConsole doesn't report when attribute methods throw exceptions;
The NPE did not show up in the Tomcat logs.
Once I resolved the issue with serviceBean == null, everything worked perfectly. Regardless, +1 to Jon for providing a working demo, since there are literally 50 different ways to configure MBeans/MXBeans within Spring.
I've recently built a sample Spring based webapp that very cleanly enables JMX for latest versions of Spring, Hibernate and Ehcache.
It has examples for both EntityManager based access and DAO access (including transactions!). It also shows how to do annotation based injection in order to negate having to use Spring's xml config for beans. There is even a SpringMVC based example servlet using annotations. Basically, this is a Spring based version of a fairly powerful application server running on top of any servlet engine.
It isn't documented yet, but I'll get to that soon. Take a look at the configuration files and source code and it should be pretty clear.
The motivation behind this is that I got tired of all of the crazy blog posts with 50 different ways to set things up and finally made a single simple source that people can work from. It is up on github so feel free to fork the project and do whatever you want with it.
https://github.com/lookfirst/fallback

Categories

Resources