Some Send Errors Can Cause Undeployment to Wait Forever
-------------------------------------------------------
Key: SM-1850
URL: https://issues.apache.org/activemq/browse/SM-1850
Project: ServiceMix
Issue Type: Bug
Components: servicemix-camel, servicemix-common
Affects Versions: 3.3
Reporter: Robert H. Pollack
If a component causes an exception to be thrown in
{{org.apache.servicemix.common.EndpointDeliveryChannel.send()}}, the exchange
is nevertheless entered into a hashmap called {{knownExchanges}}. Later, when
the component shuts down, the shutdown code in
{{org.apache.servicemix.common.AsyncBaseLifeCycle.prepareShutdown()}} will wait
for this exchange to be processed--but this will never happen. The result is
that the JBI container completely stops.
Reproducing this problem is easy in Camel (which is where I discovered it); it
requires only a one-line Camel route such as this one:
{{from ("timer:dmftimer1?period=60000").to
("jbi:service:alpha:beta:gamma");}}
To reproduce this problem:
# Create a service assembly containing this route.
# Copy the service assembly to the {{hotdeploy}} directory.
# After the exception is thrown, attempt to shut down the assembly (the
simplest way is to control-C the ServiceMix window). The service assembly will
not undeploy and the JBI container will not shut down.
I will attach two zip files to assist in reproducing this error:
# A pre-built service assembly that you can just copy to {{hotdeploy}}.
# Source code and a build script to assist experimentation.
(The build script is an Ant file that requires you to point the property
{{camel-dir}} at a directory that contains the Camel core jar.
The relevant code in {{EndpointDeliveryChannel}} is this:
{noformat}
public void send(MessageExchange exchange) throws MessagingException {
prepareExchange(exchange);
handleExchange(exchange, exchange.getStatus() == ExchangeStatus.ACTIVE);
channel.send(exchange);
}
{noformat}
The call to {{handleExchange()}} enters the exchange into the hashmap. But if
{{channel.send()}} throws an exception, the exchange is never removed from the
hashmap.
The relevant code in {{AsyncBaseLifeCycle}} is this:
{noformat}
public void prepareShutdown(Endpoint endpoint) throws InterruptedException {
Set<String> exchanges = getKnownExchanges(endpoint);
synchronized (exchanges) {
if (!exchanges.isEmpty()) {
exchanges.wait();
}
}
}
{noformat}
I do not know if all send problems cause this behavior. I discovered it by
running a Camel application that attempts to send a message to a non-existent
JBI endpoint. When the Camel program does this, I get a stack trace that (in
part) looks like this:
{noformat}
...
Caused by: javax.jbi.messaging.MessagingException: Could not find route for
exchange: InOnly[
id: ID:10.6.10.123-120d97c610d-3:0
status: Active
role: provider
service: {alpha:beta}gamma
in: null
] for service: {alpha:beta}gamma and interface: null
at
org.apache.servicemix.jbi.nmr.DefaultBroker.sendExchangePacket(DefaultBroker.java:297)
at
org.apache.servicemix.jbi.security.SecuredBroker.sendExchangePacket(SecuredBroker.java:88)
at
org.apache.servicemix.jbi.container.JBIContainer.sendExchange(JBIContainer.java:894)
at
org.apache.servicemix.jbi.messaging.DeliveryChannelImpl.doSend(DeliveryChannelImpl.java:395)
at
org.apache.servicemix.jbi.messaging.DeliveryChannelImpl.send(DeliveryChannelImpl.java:431)
at
org.apache.servicemix.common.EndpointDeliveryChannel.send(EndpointDeliveryChannel.java:88)
at
org.apache.servicemix.common.endpoints.SimpleEndpoint.send(SimpleEndpoint.java:66)
at
org.apache.servicemix.camel.CamelConsumerEndpoint.process(CamelConsumerEndpoint.java:89)
... 13 more
{noformat}
Later, when attempting to shut down, a thread dump includes the following:
{noformat}
"Thread-16" daemon prio=6 tid=0x2bb34400 nid=0x1398 in Object.wait()
[0x2ddff000..0x2ddffc94]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x25aed9f8> (a java.util.HashSet)
at java.lang.Object.wait(Object.java:485)
at
org.apache.servicemix.common.AsyncBaseLifeCycle.prepareShutdown(AsyncBaseLifeCycle.java:657)
- locked <0x25aed9f8> (a java.util.HashSet)
at
org.apache.servicemix.common.DefaultServiceUnit.removeEndpoint(DefaultServiceUnit.java:205)
- locked <0x0826a048> (a
org.apache.servicemix.common.xbean.XBeanServiceUnit)
at
org.apache.servicemix.common.DefaultComponent.removeEndpoint(DefaultComponent.java:301)
- locked <0x08269958> (a
org.apache.servicemix.camel.CamelJbiComponent)
at
org.apache.servicemix.camel.JbiEndpoint$JbiProducer.stop(JbiEndpoint.java:82)
...
{noformat}
My proposed fix is to change the code in {{EndpointDeliveryChannel}} to a
try-catch block, as shown below. But I defer to those who know ServiceMix
better than I to say whether this needs to be more sophisticated. My change,
which I have tested and which works fine for me, is as follows:
{noformat}
public void send(MessageExchange exchange) throws MessagingException {
prepareExchange(exchange);
handleExchange(exchange, exchange.getStatus() == ExchangeStatus.ACTIVE);
try {
channel.send(exchange);
} catch (MessagingException me) {
// If something goes wrong with the send, remove this exchange
// from the list of known exchanges or else the caller will
// never be able to undeploy
// (see AsyncBaseLifeCycle.prepareShutdown()).
handleExchange (exchange, false);
throw me;
} catch (Throwable t) {
handleExchange (exchange, false);
throw new MessagingException (t.getMessage(), t);
}
}
{noformat}
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.