How do I catch BeanCreationException in Apache Camel route initialization to allow my Spring application to start anyway? - spring

I am using Apache Camel and I defined a route receiving inputs from 2 queues located on 2 distinct servers. I want ideally to consume from both server but I also want to be able to run the application when one of the 2 destinations is down.
Here's my route:
try {
from("mccdsJmsRequest1:queue:{{mccds.queues.in}}").to("direct:apolloMccdsRoute");
} catch (Exception e){
// debug line
System.out.println("Ms1 not reachable");
e.printStackTrace();
}
try {
from("mccdsJmsRequest2:queue:{{mccds.queues.in}}").to("direct:apolloMccdsRoute");
} catch (Exception e){
// debug line
System.out.println("Ms2 not reachable");
e.printStackTrace();
}
from("direct:apolloMccdsRoute").routeId("apolloMCCDSRoute")
// Main route starts here...
I declare my beans here:
#Bean
public JndiObjectFactoryBean mccdsJmsConnectionFactory1() {
JndiObjectFactoryBean cf = new JndiObjectFactoryBean();
cf.setJndiEnvironment(prodMccdsJndiProperties.getJndi1());
cf.setJndiName(jndiName1);
return cf;
}
#Bean
public JndiObjectFactoryBean mccdsJmsConnectionFactory2(){
JndiObjectFactoryBean cf = new JndiObjectFactoryBean();
cf.setJndiEnvironment(prodMccdsJndiProperties.getJndi2());
cf.setJndiName(jndiName2);
return cf;
}
#Inject
private CamelContext camelContext;
#Bean
public JmsComponent mccdsJmsRequest1() {
JmsComponent ac = new JmsComponent(camelContext);
ac.setConnectionFactory((ConnectionFactory) mccdsJmsConnectionFactory1().getObject());
ac.setConcurrentConsumers(5);
return ac;
}
#Bean
public JmsComponent mccdsJmsRequest2(){
JmsComponent ac = new JmsComponent(camelContext);
ac.setConnectionFactory((ConnectionFactory) mccdsJmsConnectionFactory2().getObject());
ac.setConcurrentConsumers(5);
return ac;
}
If one of the connection factory is not reachable the application doesn't start.
I would like to catch an ignore the exception:
o.s.b.f.s.DefaultListableBeanFactory : Bean creation exception on non-lazy FactoryBean type check: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mccdsJmsConnectionFactory2' defined in class path resource [ca/bell/it/spa/uim/apollo/maximo/config/mccds/ProdMccdsJmsConfiguration.class]: Invocation of init method failed; nested exception is javax.naming.CommunicationException [Root exception is java.net.ConnectException: t3://someTestServerIP: Destination unreachable; nested exception is:
java.net.ConnectException: Connection refused (Connection refused); No available router to destination]

Try to set lookupOnStartup to false. (Add cf.setLookupOnStartup(false) to the mccdsJmsConnectionFactory1 and mccdsJmsConnectionFactory2 bean definitions.)
Also check this thread: http://forum.spring.io/forum/spring-projects/batch/95620-jndi-lookup

That won't work because your try/catch blocks are never executed during the execution of your route. In your code, you'd only get an exception during Camel's initial execution of this code upon application startup when it's building the route, when it doesn't care if your queues even exist or not.
Instead you need to determine when an error occurs in your route when it's executed after being constructed. There are a number of ways to do this, but a good start might be to look at Camel's Exception Clause
And alternative and perhaps even better choice is to make use of Camel's Load Balancer . It allows you to try any number of endpoints and failover to the next should one fail with an exception.

Related

Application is not stopped with missingTopicFatal and manually started listeners

I'm using spring and spring-kafka.
When the application starts, I'm loading compacted topics into memory then when the topics are fully read, the application is started.
In order to do that I created #KafkaListeners with autostartup to false and a SmartLifeCycle bean with AbstractMessageListenerContainer.DEFAULT_PHASE - 1 phase which is doing listener.start() on all those listeners (which read compacted topics) then wait for them to be finished.
This is working great but if I set spring.kafka.listener.missing-topics-fatal = true with a missing topic, there is an error :
org.springframework.context.ApplicationContextException: Failed to start bean 'org.springframework.kafka.config.internalKafkaListenerEndpointRegistry'; nested exception is java.lang.IllegalStateException: Topic(s) [mytopic] is/are not present and missingTopicsFatal is true
It's the expected behavior, but the application is not shutdown, my manually started listeners keeps running and sending errors :
java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext#502e1410 has not been refreshed yet
How can I make the application exit in this case ?
Catch the exception and shut down the JVM:
#SpringBootApplication
public class So60036945Application {
public static void main(String[] args) {
try {
SpringApplication.run(So60036945Application.class, args);
}
catch (ApplicationContextException ace) {
if (ace.getCause() instanceof IllegalStateException) {
ace.printStackTrace();
System.exit(1);
}
}
}
#KafkaListener(id = "so60036945", topics = "so60036945a")
public void listen(String in) {
System.out.println(in);
}
}
But, as I said in Gitter, it's better to auto-start the compacted topic listeners and start the other listeners manually (the other way around to what you are doing now).

Multiple DefaultMessageListenerContainer for external systems

I have my application that should communicate with two external systems A and B. So I am creating actually 2 containers like this:
private DefaultMessageListenerContainer createContainer(Conf conf) {
DefaultMessageListenerContainer jmsCnr= new DefaultMessageListenerContainer();
jmsCnr.setConnectionFactory(connectionFactoryAdapter);
jmsCnr.setDestinationResolver(destinationResolver);
jmsCnr.setDestinationName(destinationName);
jmsCnr.setMessageListener(MyClass);
jmsCnr.initialize();
return jmsCnr;
}
And creation of connection factory goes like this:
private ConnectionFactory createConnectionFactory(Conf conf) {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
JndiTemplate jndiTemplate = getJNDITemplate(conf);
String connectionFactoryJndiName = conf.getConnectionFactoryJndi();
jndiObjectFactoryBean.setJndiTemplate(jndiTemplate);
jndiObjectFactoryBean.setJndiName(connectionFactoryJndiName);
jndiObjectFactoryBean.setProxyInterface(ConnectionFactory.class);
jndiObjectFactoryBean.setLookupOnStartup(false);
try {
jndiObjectFactoryBean.afterPropertiesSet();
} catch (NamingException e) {
e.printStackTrace();
}
return (ConnectionFactory) jndiObjectFactoryBean.getObject();
}
After that containers are created, I am looping over them and call start().
Everything is working fine My listener is receiving message in case if message is sent to external system A.
However if I configuring my startup class to create two containers for external system A and for B, I am not receiving messages from queues of system A, and from system B messages are received.
Again, I am using spring implementations without spring context, everything is programmatic.
My question is, where I am missing something? Seems creation of second DefaultMessageListenerContainer instance for external system B is overriding the previous.

InstanceNotFoundException when trying to get Activemq MBean

I have the following configuration:
#Configuration
public class ConfigureRMI {
#Value("${jmx.rmi.host:localhost}")
private String rmiHost;
#Value("${jmx.rmi.port:1099}")
private Integer rmiPort;
#Bean
public RmiRegistryFactoryBean rmiRegistry() {
final RmiRegistryFactoryBean rmiRegistryFactoryBean = new RmiRegistryFactoryBean();
rmiRegistryFactoryBean.setPort(rmiPort);
rmiRegistryFactoryBean.setAlwaysCreate(true);
return rmiRegistryFactoryBean;
}
#Bean
#DependsOn("rmiRegistry")
public ConnectorServerFactoryBean connectorServerFactoryBean() throws Exception {
final ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
connectorServerFactoryBean.setObjectName("connector:name=rmi");
connectorServerFactoryBean.setServiceUrl(String.format("service:jmx:rmi://%s:%s/jndi/rmi://%s:%s/jmxrmi", rmiHost, rmiPort, rmiHost, rmiPort));
return connectorServerFactoryBean;
}
#Bean
#DependsOn("connectorServerFactoryBean")
public DestinationViewMBean queueMonitor() {
JMXConnectorServer connector = null;
MBeanServerConnection connection;
ObjectName nameConsumers;
try {
connector = connectorServerFactoryBean().getObject();
connection = connector.getMBeanServer();
nameConsumers = new ObjectName("org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=tasks");
} catch (Exception e) {
e.printStackTrace();
return null;
}
DestinationViewMBean mbView = MBeanServerInvocationHandler.newProxyInstance(connection, nameConsumers, DestinationViewMBean.class, true);
return mbView;
}
}
It configures and instantiates DestinationViewMBean that I try to use later in code like this:
Long queueSize = queueMonitor.getQueueSize();
But it throws an exception javax.management.InstanceNotFoundException: org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=tasks
I'm sure the names are as I typed. I can see the brocker name and tasks queue in ActiveMQ web console, elements are queued and dequeued as intended. But I cant't monitor the queue size. The method I used (the one I provided) was made from many answers here on SO and man pages on JMX and ActiveMQ.
I'm wondering if I'm missing something obvious. I turned firewall down, I'm on localhost. Why can't DestinationViewMBean find the queue?
UPD: I used JConsole to check the MBean name. I managed to fix InstanceNotFoundException but now I can't get any attribute from the bean. I've tried a lot of them in debugger (just run throught the attributes I could find in DestinationViewMBean interface). But on every try of attribute getter I get javax.management.AttributeNotFoundException: getAttribute failed: ModelMBeanAttributeInfo not found for QueueSize (or any other attribute).

ActiveMQ fails to start (EOFException: null)

I'm having issues start my spring web app with camel and ActiveMQ.
The particular error I'm getting is not very descriptive:
16:18:53.552 [localhost-startStop-1] ERROR o.a.a.b.BrokerService - Failed to start Apache ActiveMQ ([activemq.myworkingdomain.com, ID:Ricardos-MacBook-Air.local-65257-1453738732697-0:2], {})
java.io.EOFException: null
at java.io.DataInputStream.readBoolean(DataInputStream.java:244) ~[na:1.8.0_60]
at org.apache.activemq.openwire.v11.SubscriptionInfoMarshaller.looseUnmarshal(SubscriptionInfoMarshaller.java:133) ~[activemq-client-5.13.0.jar:5.13.0]
at org.apache.activemq.openwire.OpenWireFormat.doUnmarshal(OpenWireFormat.java:366) ~[activemq-client-5.13.0.jar:5.13.0]
at org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:277) ~[activemq-client-5.13.0.jar:5.13.0]
at org.apache.activemq.store.kahadb.KahaDBStore$KahaDBTopicMessageStore$1.execute(KahaDBStore.java:755) ~[activemq-core-5.7.0.jar:5.7.0]
at org.apache.kahadb.page.Transaction.execute(Transaction.java:769) ~[kahadb-5.7.0.jar:5.7.0]
at org.apache.activemq.store.kahadb.KahaDBStore$KahaDBTopicMessageStore.getAllSubscriptions(KahaDBStore.java:749) ~[activemq-core-5.7.0.jar:5.7.0]
at org.apache.activemq.store.kahadb.KahaDBStore$KahaDBTopicMessageStore.<init>(KahaDBStore.java:663) ~[activemq-core-5.7.0.jar:5.7.0]
at org.apache.activemq.store.kahadb.KahaDBStore.createTopicMessageStore(KahaDBStore.java:920) ~[activemq-core-5.7.0.jar:5.7.0]
at org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter.createTopicMessageStore(KahaDBPersistenceAdapter.java:100) ~[activemq-core-5.7.0.jar:5.7.0]
I'm sticking with java and simple spring stuff no xml:
#Bean
public CamelContext camelContext() {
final CamelContext camelContext = new DefaultCamelContext();
camelContext.addComponent("activemq", activeMQComponent());
try {
CamelConfigurator.addRoutesToCamel(camelContext);
camelContext.start();
} catch (final Exception e) {
LOGGER.error("Failed to start the camel context", e);
}
LOGGER.info("Started the Camel context and components");
return camelContext;
}
#Bean
public ActiveMQComponent activeMQComponent() {
final ActiveMQComponent activeMQComponent = new ActiveMQComponent();
activeMQComponent.setConfiguration(jmsConfiguration());
activeMQComponent.setTransacted(true);
activeMQComponent.setCacheLevelName("CACHE_CONSUMER");
return activeMQComponent;
}
#Bean
public JmsConfiguration jmsConfiguration() {
final JmsConfiguration jmsConfiguration = new JmsConfiguration(pooledConnectionFactory());
jmsConfiguration.setConcurrentConsumers(CONCURRENT_CONSUMERS);
return jmsConfiguration;
}
#Bean
public PooledConnectionFactory pooledConnectionFactory() {
final PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(
activeMQConnectionFactory());
pooledConnectionFactory.setMaxConnections(MAX_CONNECTIONS_TO_POOL_FACTORY);
// pooledConnectionFactory.start();
return pooledConnectionFactory;
}
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
return new ActiveMQConnectionFactory(username, password, activeMQBrokerURL);
}
I've been trying to change the order in which things load also the routes and what's included in them, deleting the kahadb local folder but nothing seems to work or even point me in the right location.
Your stacktrace shows that your application in using
activemq-client-5.13.0
activemq-core-5.7.0
I am pretty sure that the version mismatch is responsible for this error.
Can you just import activemq-all 5.13.0 and try again?
According to your stacktrace, seems like you are experiencing issues with KahaDB (the persistence engine by default for Apache ActiveMQ).
I believe KahaDB has a bug within it and you can find information about it on the Apache issues Web Page.
I had this issue once, but it strange since it seems like it happens at random, sometimes it works, sometime it fails.
I managed to fix this issue by choosing a different persistence engine for ActiveMQ. Maybe you can give it a try and tell me if it works for you. Hope this information can help you.

Error in initialising portlets on portal server

I have a WebSphere Portal server that houses portlets. The portlets make remote calls to ejbs running on WAS. Both the portal server and the WAS are running on the local machine. The WAS starts up properly, but on trying to start up the portal server, it throws the following error:
nested exception is: java.lang.NullPointerException at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
On looking up the source in spring, I find the method definition:
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.injectMethods(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Autowiring of methods failed", ex);
}
return pvs;
}
Since the error is in spring source code and not my code, how should I start debugging it?

Resources