We had a medium complicated spring boot 1.5.14 app with rest api + mybatis for backend, angular 4 with material/prime-ng for frontend. It works fine from developers' box up to UAT environments, but in production, it works fine for the first couple of days, then throws NoSuchBeanDefinition. The production environment is openshift + openjdk version "1.8.0_171".
To trim down the app and leave related info, here are code snippets:
public interface ITaxCalculator {
BigDecimal calc(BigDecimal amount);
}
public class FedProvTaxCalculator implements ITaxCalculator {
... ...
}
#Configuration
public class TaxCalculatorConfiguration {
...
#Bean("onTaxCalculator")
public ITaxCalculator ontairioTaxCalculator() {
FedProvTaxCalculator ret = ..
...
return ret;
}
#Bean("bcTaxCalculator")
public ITaxCalculator britishColumbiaTaxCalculator() {
FedProvTaxCalculator ret = ..
...
return ret;
}
}
public class CAOrderProcessor implements IOrderProcessor {
#Autowire #Qualifier("onTaxCalculator")
private FedProvTaxCalculator onTaxCalculator;
#Autowire #Qualifier("bcTaxCalculator")
private FedProvTaxCalculator bcTaxCalculator;
....
}
// --------------- below code are at framework level -----
public interface IOrderProcessor {
void process(Order order);
}
public interface IOrderProcessorFactory {
IOrderProcessor createOrderProcessor(String countryCode, MembershipType membership);
}
#Service
public class OrderProcessorFactoryPropImpl implements IOrderProcessorFactory {
#Autowired
private AutowireCapableBeanFactory beanFactory;
#Override
#Cacheable("orderProcessor")
public IOrderProcessor createOrderProcessor(String countryCode, MembershipType membership) {
String clzName = resolveOrderProcessClzName(countryCode, membership); // resolve to CAOrderProcess clz-name
try {
Object ret = Class.forName(clzName).newInstance();
beanFactory.autowireBean(ret);
// the above line throws error after a while
return (IOrderProcessor)ret;
} catch (Exception ex) {
...
throw new RuntimeException(...);
}
}
private String resolveOrderProcessClzName(String countryCode, MembershipType membership) {
String clzName = lookupFromPropFile(countryCode + "." + membership.name());
if (StringUtils.isBlank( clzName )) {
clzName = lookupFromPropFile(countryCode);
}
return clzName;
}
}
After restarting spring boot app, it works fine for the first couple of days, even with CA=CAOrderProcessor. But then one day, with countryCode=CA, it throws NoSuchBeanDefinitionException: No qualifying bean of type ‘FedProvTaxCalculator’ available: expected at least 1 bean which qualifies as autowire candidate. After restarting Java app, it works again for CA=CAOrderProcessor.
Why does spring framework behave this way? Thanks in advance!
The issue can be solved by
#Configuration public class TaxCalculatorConfiguration {
#Bean("onTaxCalculator")
public ITaxCalculator ontairioTaxCalculator() { ... }
}
public class CAOrderProcessor implements IOrderProcessor {
#Autowire #Qualifier("onTaxCalculator")
private ITaxCalculator onTaxCalculator;
}
Using AutowireCapableBeanFactory is fine. Why does it work initially, and then fails, and only fails on one ENV - openshift with min 2 pods? the other ENVs work fine always. Looks like spring relaxes autowire bean-type check initially, and later on under certain conditions, it checks the bean-type. Logical guess is that bean-definition returns interface type, which may be proxied, bean-wiring refers a concrete type, the proxied interface doesn't equal concrete type, raising this error. But in that case, it should always give error. If not, if I don't use the cache, or evict cache, I should be able to easily re-produce it in any ENVs, but it works fine on my local macos + oracle jdk 1.8. I even create a docker container based on production openshift docker image to run the app without cache, evict cache, force YGC and FGC, it works fine too.
I don't why behaves like that, probably because you use AutowireCapableBeanFactory directly and even worse then that in combination with #Cacheable.
You should reconsider your framework level code. I believe that you should never use AutowireCapableBeanFactory directly and especially in your case. It's simple and you can achieve the same result with less effort and using simple Map of country_code + membershi_type -> processor, for example:
#Configuration
public class ProcessorConfiguration {
. . .
#Bean("cAOrderProcessor ")
public IOrderProcessor cAOrderProcessor() [
return new CAOrderProcessor();
}
. . .
#Bean
public IOrderProcessorFactory processorFactory() {
// create country_code + membershi_type -> processor map
Map<ProcessorKey, IOrderProcessor> processorMap = new HashMap<>();
// not sure about values in MembershipType, so I put SOME just for example
// this map also can be a bean if you're gonna need that in other parts of app
processorMap.put(new ProcessorKey("CA", MembershipType.SOME), cAOrderProcessor());
// set it to factory
return new OrderProcessorFactoryPropImpl(processorMap );
}
. . .
}
public class OrderProcessorFactoryPropImpl implements IOrderProcessorFactory {
private final Map<ProcessorKey, IOrderProcessor> processorMap;
public OrderProcessorFactoryPropImpl(Map<ProcessorKey, IOrderProcessor> processorMap) {
this.processorMap = processorMap;
}
#Override
// #Cacheable("orderProcessor") you dont need that because get it from map costs nothing
// changed the name to "get" instead of "create"
public IOrderProcessor getOrderProcessor(String countryCode, MembershipType membership) {
// just get processor by key
return processorMap.get(constructKey(countryCode, membership));
}
private ProcessorKey constructKey(String countryCode, MembershipType membership) {
return new ProcessorKey(countryCode, membership);
}
}
Also I noticed that you mix java and annotation-based bean config which is considered as a bad practice. Hope this's going to help.
Update 1 - Answering comment
Well, to figure out what's wrong person's gonna need to full copy of your app to debug/logs and reproduce it's usual use cases. It's probably not possible to say what's wrong just looking at examples you've provided (at least for me).
And I just pointed out that the way you are using AutowireCapableBeanFactory is not up to best practices and that's why you have problems in your runtime.
So you probably have 2 solutions:
Get rid of it, and use somewhat different approach (maybe similar to the one I've suggested previously). I believe that's only one good option. But this is up to you to decide.
Enable spring logs and hope that you will catch problem there. Probably need to enable debug logs like that in your log4j.xml (I suppose it's log4j but might be something else):
<category name="org.springframework.beans">
<priority value="debug" />
</category>
Related
I have a spring boot application that uses a few #TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT). I noticed that spring boot doesn't do any exception logging for them when they end up with an exception being thrown.
Because of this I wanted to add some generic logging facility for such exceptions. I found that TransactionalApplicationListener.SynchronizationCallback is the interface I need to implement. However it seems complicated to register these callbacks. I didn't find any call of TransactionalApplicationListener#addCallback in the spring dependencies that would achieve this.
Trying to get a list of TransactionalApplicationListener and the SynchronizationCallback injected and then call addCallback in a #PostConstruct didn't get me further because there were always no listeners injected even though the application did make successful use of them.
So how do I add SynchronizationCallbacks to TransactionalApplicationListeners during spring boot application startup?
The first thing to note is that TransactionalApplicationListeners like all ApplicationListener are not beans in the spring context. They live somewhat outside of it (see org.springframework.context.ConfigurableApplicationContext#addApplicationListener). So injecting them is not possible for the application context.
While debugging and looking through spring sources one finds that these listeners are being created by org.springframework.transaction.event.TransactionalEventListenerFactory. And that is where my solution steps into. We decorate that factory with another one that is aware of SynchronizationCallbacks:
public class SynchronizationCallbackAwareFactory implements EventListenerFactory, Ordered {
private final TransactionalEventListenerFactory delegate;
private final Provider<List<SynchronizationCallback>> synchronizationCallbacks;
private final int order;
public SynchronizationCallbackAwareFactory(TransactionalEventListenerFactory transactionalEventListenerFactory,
Provider<List<SynchronizationCallback>> synchronizationCallbacks,
int order) {
this.delegate = transactionalEventListenerFactory;
this.synchronizationCallbacks = synchronizationCallbacks;
this.order = order;
}
#Override
public boolean supportsMethod(Method method) {
return delegate.supportsMethod(method);
}
#Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
ApplicationListener<?> applicationListener = delegate.createApplicationListener(beanName, type, method);
if (applicationListener instanceof TransactionalApplicationListener) {
TransactionalApplicationListener<?> listener = (TransactionalApplicationListener<?>) applicationListener;
Collection<SynchronizationCallback> callbacks = this.synchronizationCallbacks.get();
callbacks.forEach(listener::addCallback);
}
return applicationListener;
}
#Override
public int getOrder() {
return order;
}
}
Note that I use a javax.inject.Provider in my case to make the retrieval of the callbacks at the latest possible time.
The decorator has to be Ordered because spring will use the first factory supporting the method it gets across. And therefore the order of an instance of this class has to have higher precedence as the order value 50 of TransactionEventListenerFactory.
I had simmilar problem with code as below
#Transactional(propagation = Propagation.REQUIRES_NEW)
public class SomeListenerFacade {
#TransactionalEventListener
public void onSomething(SomeEvent event) {
throw new RuntimeException("some cause");
}
}
I followed your solution. It worked. On the way I've found an alternative way for at least seeing that exception in the logfile
# application.properties
logging.level.org.springframework.transaction.support.TransactionSynchronizationUtils = DEBUG
i am kind of stuck on a problem with creating beans, or probably i got the wrong intention.. Maybe you can help me solve it:
I got a application which takes in requests for batch processing. For every batch i need to create an own context depending on the parameters issued by the request.
I will try to simplyfy it with the following example:
I receive a request to process in a batch FunctionA which is a implementation for my Function_I interface and has sub-implementation FunctionA_DE and FunctionA_AT
Something like this:
public interface Function_I {
String doFunctionStuff()
}
public abstract class FunctionA implements Function_I {
FunctionConfig funcConfig;
public FunctionA(FunctionConfig funcConfig) {
this.funcConfig = funcConfig;
}
public String doFunctionStuff() {
// some code
String result = callSpecificFunctionStuff();
// more code
return result;
}
protected abstract String callSpecificFunctionStuff();
}
public class FunctionA_DE extends FunctionA {
public FunctionA_DE(FunctionConfig funcConf) {
super(funcConf)
}
protected String callSpecifiFunctionStuff() {
//do some specificStuff
return result;
}
}
public class FunctionA_AT extends FunctionA {
public FunctionA_AT(FunctionConfig funcConf) {
super(funcConf)
}
protected String callSpecifiFunctionStuff() {
//do some specificStuff
return result;
}
}
what would be the Spring-Boot-Way of creating a instance for FunctionA_DE to get it as Function_I for the calling part of the application, and what should it look like when i add FunctionB with FunctionB_DE / FunctionB_AT to my classes..
I thought it could be something like:
PSEUDO CODE
#Configuration
public class FunctionFactory {
#Bean(SCOPE=SCOPE_PROTOTYPE) // i need a new instance everytime i call it
public Function_I createFunctionA(FunctionConfiguration funcConfig) {
// create Function depending on the funcConfig so either FunctionA_DE or FunctionA_AT
}
}
and i would call it by Autowiring the FunctionFactory into my calling class and use it with
someSpringFactory.createFunction(functionConfiguration);
but i cant figure it out to create a Prototype-Bean for the function with passing a parameter.. And i cant really find a solution to my question by browsing through SO, but maybe i just got the wrong search terms.. Or my approach to solve this issue i totally wrong (maybe stupid), nobody would solve it the spring-boot-way but stick to Factories.
Appreciate your help!
You could use Springs's application context. Create a bean for each of the interfaces but annotate it with a specific profile e.g. "Function-A-AT". Now when you have to invoke it, you can simply set the application context of spring accordingly and the right bean should be used by Spring.
Hello everyone and thanks for reading my question.
after a discussion with a friend who is well versed in the spring framework i came to the conclusion that my approach or my favoured solution was not what i was searching for and is not how spring should be used. Because the Function_I-Instance depends on the for the specific batch loaded configuration it is not recommended to manage all these instances as #Beans.
In the end i decided to not manage the instances for my Function_I with spring. but instead i build a Controller / Factory which is a #Controller-Class and let this class build the instance i need with the passed parameters for decision making on runtime.
This is how it looks (Pseudo-Code)
#Controller
public class FunctionController {
SomeSpringManagedClass ssmc;
public FunctionController(#Autowired SomeSpringManagedClass ssmc) {
this.ssmc = ssmc;
}
public Function_I createFunction(FunctionConfiguration funcConf) {
boolean funcA, cntryDE;
// code to decide the function
if(funcA && cntryDE) {
return new FunctionA_DE(funcConf);
} else if(funB && cntryDE) {
return new FunctionB_DE(funcConf);
} // maybe more else if...
}
}
I am working on a REST API where I have an interface that defines a list of methods which are implemented by 4 different classes, with the possibility of adding many more in the future.
When I receive an HTTP request from the client there is some information included in the URL which will determine which implementation needs to be used.
Within my controller, I would like to have the end-point method contain a switch statement that checks the URL path variable and then uses the appropriate implementation.
I know that I can define and inject the concrete implementations into the controller and then insert which one I would like to use in each particular case in the switch statement, but this doesn't seem very elegant or scalable for 2 reasons:
I now have to instantiate all of the services, even though I only need to use one.
The code seems like it could be much leaner since I am literally calling the same method that is defined in the interface with the same parameters and while in the example it is not really an issue, but in the case that the list of implementations grows ... so does the number of cases and redundant code.
Is there a better solution to solve this type of situation? I am using SpringBoot 2 and JDK 10, ideally, I'd like to implement the most modern solution.
My Current Approach
#RequestMapping(Requests.MY_BASE_API_URL)
public class MyController {
//== FIELDS ==
private final ConcreteServiceImpl1 concreteService1;
private final ConcreteServiceImpl2 concreteService2;
private final ConcreteServiceImpl3 concreteService3;
//== CONSTRUCTORS ==
#Autowired
public MyController(ConcreteServiceImpl1 concreteService1, ConcreteServiceImpl2 concreteService2,
ConcreteServiceImpl3 concreteService3){
this.concreteService1 = concreteService1;
this.concreteService2 = concreteService2;
this.concreteService3 = concreteService3;
}
//== REQUEST MAPPINGS ==
#GetMapping(Requests.SPECIFIC_REQUEST)
public ResponseEntity<?> handleSpecificRequest(#PathVariable String source,
#RequestParam String start,
#RequestParam String end){
source = source.toLowerCase();
if(MyConstants.SOURCES.contains(source)){
switch(source){
case("value1"):
concreteService1.doSomething(start, end);
break;
case("value2"):
concreteService2.doSomething(start, end);
break;
case("value3"):
concreteService3.doSomething(start, end);
break;
}
}else{
//An invalid source path variable was recieved
}
//Return something after additional processing
return null;
}
}
In Spring you can get all implementations of an interface (say T) by injecting a List<T> or a Map<String, T> field. In the second case the names of the beans will become the keys of the map. You could consider this if there are a lot of possible implementations or if they change often. Thanks to it you could add or remove an implementation without changing the controller.
Both injecting a List or a Map have some benefits and drawbacks in this case. If you inject a List you would probably need to add some method to map the name and the implementation. Something like :
interface MyInterface() {
(...)
String name()
}
This way you could transform it to a Map<String, MyInterface>, for example using Streams API. While this would be more explicit, it would polute your interface a bit (why should it be aware that there are multiple implementations?).
When using the Map you should probably name the beans explicitly or even introduce an annotation to follow the principle of least astonishment. If you are naming the beans by using the class name or the method name of the configuration class you could break the app by renaming those (and in effect changing the url), which is usually a safe operation to do.
A simplistic implementation in Spring Boot could look like this:
#SpringBootApplication
public class DynamicDependencyInjectionForMultipleImplementationsApplication {
public static void main(String[] args) {
SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);
}
interface MyInterface {
Object getStuff();
}
class Implementation1 implements MyInterface {
#Override public Object getStuff() {
return "foo";
}
}
class Implementation2 implements MyInterface {
#Override public Object getStuff() {
return "bar";
}
}
#Configuration
class Config {
#Bean("getFoo")
Implementation1 implementation1() {
return new Implementation1();
}
#Bean("getBar")
Implementation2 implementation2() {
return new Implementation2();
}
}
#RestController
class Controller {
private final Map<String, MyInterface> implementations;
Controller(Map<String, MyInterface> implementations) {
this.implementations = implementations;
}
#GetMapping("/run/{beanName}")
Object runSelectedImplementation(#PathVariable String beanName) {
return Optional.ofNullable(implementations.get(beanName))
.orElseThrow(UnknownImplementation::new)
.getStuff();
}
#ResponseStatus(BAD_REQUEST)
class UnknownImplementation extends RuntimeException {
}
}
}
It passes the following tests:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldCallImplementation1() throws Exception {
mockMvc.perform(get("/run/getFoo"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("foo")));
}
#Test
public void shouldCallImplementation2() throws Exception {
mockMvc.perform(get("/run/getBar"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("bar")));
}
#Test
public void shouldRejectUnknownImplementations() throws Exception {
mockMvc.perform(get("/run/getSomethingElse"))
.andExpect(status().isBadRequest());
}
}
Regarding two of your doubts :
1. Instantiating the service object should not be an issue as this is one time job and controller gonna need them to serve all type of request.
2. You can use the exact Path mapping to get rid of switch case. For e.g. :
#GetMapping("/specificRequest/value1")
#GetMapping("/specificRequest/value2")
#GetMapping("/specificRequest/value3")
All of the above mapping will be on separate method which would deal with specific source value and invoke respective service method.
Hope this will help to make code more cleaner and elegant.
There is one more option of separating this on service layer and having only one endpoint to serve all types of source but as you said there is different implementation for each source value then it says that source is nothing but a resource for your application and having separate URI/separate method makes the perfect sense here. Few advantages that I see here with this are :
Makes it easy to write the test cases.
Scaling the same without impacting any other source/service.
Your code dealing the each source as separate entity from other sources.
The above approach should be fine when you have limited source values. If you have no control over source value then we need further redesign here by making source value differentiate by one more value like sourceType etc. and then having separate controller for each group type of source.
i am going to continue to express my seemingly endless missunderstanding in EJBs:
I am using JBoss 8 (Wildfly 8.1) as my Applicationserver.
I am currently building a workaround for the Problem, that Inpustreams cannot be passed to remote EJB via RMI (which makes absolute sense once i thought about it).
I i must not replace the existing Resources Interface (writing an Inpustream to a File) I build following 3 Projects as a solution:
Interfaces (library included in both EARs):
com.package.ejb
public interface StorageAdapter extends Serializable{
String store(Inpustream is); (//returns an id
Inputstream load(String id);
}
public interface StorageAdapterProvider{
StorageAdapter provide();
}
Persistence-EAR
com.package.impl
public class FileSystemStorageAdapter implements com.package.ejb.Storageadapter {
//implementation, writing to locally mounted path in filesystem...
}
com.package.impl
#Singleton
#Remote(com.package.ejb.StorageAdapterProvider.class)
public class StorageAdapterProviderBean implements com.package.ejb.StorageadapterProvider {
public StorageAdapter provide() {
return new FileSystemStorageAdapter();
}
}
Business-EAR
com.package.business
public class StorageProvider {
#EJB(looklup = "java:global/Persistence-EAR/StorageAdapterProviderBean!com.package.ejb.StorageAdapterProvider"
private StorageAdapterProvider provider;
#Produces
public StorageAdapter getStorageAdapter() {
return provider.provide();
}
}
I then use #Inject StorageAdapter storageAdapter; to get an instance of the Storageadapter-implementation.
Business-EAR/Storageprovider then throws a ClassCastException, telling me that com.package.impl.FileSystemStorageAdapter cannot be cast to com.package.ejb.StorageAdapter
i added some logging to the EJB
com.package.impl
#Singleton
#Remote(com.package.ejb.StorageAdapterProvider.class)
public class StorageAdapterProviderBean implements com.package.ejb.StorageadapterProvider {
public StorageAdapter provide() {
StroageAdapter ret = new FileSystemStorageAdapter();
logger.info("EJB: RETURNING Stortage adapter");
logger.info(" is of type:"+ret.getClass().getName());
logger.info(" is Storageadapter: "+ (ret instanceof StorageAdapter));
logger.info(" is FileSystemStorageAdapter: "+ (ret instanceof FileSystemStorageAdapter));
return ret;
}
}
and it correctly prints:
EJB: RETURNING Stortage adapter
is of type:com.package.impl.FileSystemStorageAdapter
is Storageadapter: true
is FileSystemStorageAdapter: true
Further investigation shows, that the Exception is thrown while the container is Wrapping the "ret" Object, as i also get the Exeption if i chnage the #Provides implementation to:
Object o = provider.provide();
Is it impossible to return an Interface type from an EJB?
What am i missing?
Does Business-EJB need to know the implementing Classes? - Wouldn't a ClassNotFound Exception make more sense in this case?
Thanks in advance!
EDIT:
My Packaging looks as follows:
Persistence.ear
-lib/Interfaces.jar
-StorageAdapter
-Storageprovider
-persistence_ejb.jar
-FileSystemStorageAdapter
-StorageAdapterProviderBean
Business.ear
-lib/Interfaces.jar
-StorageAdapter
-Storageprovider
-business_ejb.jar
-StorageProvider
-web.war
-Jaxrsres
The Jaxrsres is the one having a Storageadapter Injected via #Inject
I am deploying these 2 wars to a JBoss Wildlfly 8.1 Server.
I did not make any relevant changes to the configuration - so all the other Point you requested are defaults as far as I understand.
EDIT2:
It somewhat defeats the purpose, but it works if i add the Persistence.ear/persistence_ejb.jar to the libraries of Business.ear.
Business.ear
-lib
-Interfaces.jar
-StorageAdapter
-Storageprovider
-persistence_ejb.jar
-FileSystemStorageAdapter
-StorageAdapterProviderBean
-business_ejb.jar
-StorageProvider
-web.war
-Jaxrsres
This is obviously not what I want though :/
The main Point is, i want to be able to redeploy Persistence.ear with, say, "DatabaseStorageAdapter", without touching business.ear.
The "Dream" is to Provide the Storageadapter as an EJB Directly - but as the Interface is fixed and needs an InputStream, this cannot be done.
Why does 'instance' never iterate over any implementations? What am I missing?
JBoss EAP 6.3.0.GA (AS 7.4.0.Final-redhat-19)
public interface Simple { }
public class SimpleA implements Simple { public SimpleA() { } }
public class SimpleB implements Simple { public SimpleB() { } }
public class SimpleUser {
#Inject #Any Instance<Simple> instance;
#PostConstruct public void init() {
for (final Simple simple : instance) {
System.out.println(simple);
}
}
}
In case it helps someone else, I also am using Deltaspike 1.5.2 and am running into the same issue (if I remove Deltaspike, I no longer have the problem).
In my case adding producer methods did not solve it. After looking around I BeanProvider which gets around the problem but is far from elegant.
https://deltaspike.apache.org/documentation/core.html#BeanProvider
I had to call
List<MyServiceInterface> myServiceList = BeanProvider.getContextualReferences(MyServiceInterface.class, false, false);
There are probably better ways. I notice Deltaspike turns on a bunch of extensions by default, but couldn't find docs on how to disable the ones I am not using as I suspect one may be causing this issue.