Skip to content
Permalink
Browse files
Added a new module containing code to debug resource leaks caused by …
…missing calls to TransportSender#cleanup.
  • Loading branch information
veithen committed Sep 25, 2010
1 parent a5c6ff6 commit 67aa25dee49add12cc3a1fbd85ecd2d424f79415
Showing 7 changed files with 291 additions and 0 deletions.
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you under the Apache License, Version 2.0 (the
~ "License"); you may not use this file except in compliance
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transports</artifactId>
<version>1.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-debug</artifactId>
<name>Apache Axis2 - Transport - Debugging tools</name>
<description>Contains a set of debugging tools for Axis2 transports</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>${axis2.version}</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,96 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axis2.transport.debug;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.description.TransportOutDescription;
import org.apache.axis2.handlers.AbstractHandler;
import org.apache.axis2.transport.TransportSender;
import org.apache.axis2.util.Loader;

public class DebugTransportSender extends AbstractHandler implements TransportSender {
private static final String INVOCATION_KEY = DebugTransportSender.class.getName() + ".INVOCATION";

private final Set<Invocation> invocations = Collections.synchronizedSet(new HashSet<Invocation>());
private ReportScheduler scheduler;
private TransportSender target;

public void init(ConfigurationContext confContext, TransportOutDescription transportOut) throws AxisFault {
Parameter targetClassParameter = transportOut.getParameter("targetClass");
if (targetClassParameter == null) {
throw new AxisFault("targetClass parameter is mandatory");
}
try {
target = (TransportSender)Loader.loadClass((String)targetClassParameter.getValue()).newInstance();
} catch (Exception ex) {
throw new AxisFault("Unable to create target TransportSender", ex);
}
scheduler = new ReportScheduler(this);
new Thread(scheduler).start();
target.init(confContext, transportOut);
}

public void cleanup(MessageContext msgContext) throws AxisFault {
Invocation invocation = (Invocation)msgContext.getProperty(INVOCATION_KEY);
if (invocation == null) {
System.out.println("TransportSender#cleanup called without corresponding call to TransportSender#invoke!");
} else if (!invocations.remove(invocation)) {
System.out.println("TransportSender#cleanup called twice for the same message context.");
}
scheduler.scheduleReport();
target.cleanup(msgContext);
}

public InvocationResponse invoke(MessageContext msgContext) throws AxisFault {
Invocation invocation = new Invocation(Thread.currentThread().getStackTrace());
invocations.add(invocation);
msgContext.setProperty(INVOCATION_KEY, invocation);
scheduler.scheduleReport();
return target.invoke(msgContext);
}

public void stop() {
target.stop();
scheduler.stop();
generateReport();
}

void generateReport() {
synchronized (invocations) {
int size = invocations.size();
if (size > 0) {
System.out.println("There is/are " + size
+ " pending invocation(s) for which TransportSender#cleanup has not been called yet:");
for (Invocation invocation : invocations) {
System.out.println();
for (StackTraceElement ste : invocation.getStackTrace()) {
System.out.println(ste);
}
}
}
}
}
}
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axis2.transport.debug;

class Invocation {
private final StackTraceElement[] stackTrace;

public Invocation(StackTraceElement[] stackTrace) {
this.stackTrace = stackTrace;
}

public StackTraceElement[] getStackTrace() {
return stackTrace;
}
}
@@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axis2.transport.debug;

class ReportScheduler implements Runnable {
private final DebugTransportSender sender;
private long nextReport = -1;
private boolean running;

public ReportScheduler(DebugTransportSender sender) {
this.sender = sender;
}

public synchronized void run() {
while (running) {
try {
if (nextReport == -1) {
wait();
} else {
long timeout = nextReport - System.currentTimeMillis();
if (timeout > 0) {
wait(timeout);
} else {
sender.generateReport();
}
}
} catch (InterruptedException ex) {
break;
}
}
}

public synchronized void scheduleReport() {
nextReport = System.currentTimeMillis() + 10000;
notifyAll();
}

public synchronized void stop() {
running = false;
notifyAll();
}
}
@@ -160,6 +160,7 @@
<module>modules/jms</module>
<module>modules/sms</module>
<module>modules/testkit</module>
<module>modules/debug</module>
<module>modules/all</module>
</modules>

@@ -0,0 +1,53 @@
~~ Licensed to the Apache Software Foundation (ASF) under one
~~ or more contributor license agreements. See the NOTICE file
~~ distributed with this work for additional information
~~ regarding copyright ownership. The ASF licenses this file
~~ to you under the Apache License, Version 2.0 (the
~~ "License"); you may not use this file except in compliance
~~ with the License. You may obtain a copy of the License at
~~
~~ http://www.apache.org/licenses/LICENSE-2.0
~~
~~ Unless required by applicable law or agreed to in writing,
~~ software distributed under the License is distributed on an
~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~~ KIND, either express or implied. See the License for the
~~ specific language governing permissions and limitations
~~ under the License.

Transport debugging tools

The <<<axis2-transport-debug>>> module contains tools to debug transport related problems.

* Locating missing calls to <<<TransportSender#cleanup(MessageContext)>>>

The Axis2 transport API requires that <<<TransportSender#cleanup(MessageContext)>>> be called for
for every call to <<<TransportSender#invoke(MessageContext)>>>. This is necessary to allow the transport
sender to clean up resources after the response message has been processed. When using the <<<ServiceClient>>>
API, this is done explicitly by a call to <<<ServiceClient#cleanupTransport()>>> or implicitly
when sending another request.

Failure to call <<<TransportSender#cleanup(MessageContext)>>> will cause resource leaks.
However, this type of problem is difficult to debug because the error that is triggered by
the resource leak usually can't be correlated directly with the <<<TransportSender#invoke(MessageContext)>>>
invocation(s) for which the cleanup has not been done.

In these situations, <<<DebugTransportSender>>> comes to the rescue. It acts as a wrapper
around an existing <<<TransportSender>>> implementation and keeps track of the calls to
<<<TransportSender#invoke(MessageContext)>>> and <<<TransportSender#cleanup(MessageContext)>>>.
If it detects that <<<TransportSender#cleanup(MessageContext)>>> has not been called after a
given timeout, it will output a report containing the stack trace of the invocation of
<<<TransportSender#invoke(MessageContext)>>>.

In order to enable this feature for a given transport, simply replace the configured
class by <<<org.apache.axis2.transport.debug.DebugTransportSender>>> and add a
<<<targetClass>>> parameter with the real (original) transport sender implementation.
Here is an example for the HTTP transport:

+------------------------+
<transportSender name="http" class="org.apache.axis2.transport.debug.DebugTransportSender">
<parameter name="targetClass">org.apache.axis2.transport.http.CommonsHTTPTransportSender</parameter>
<parameter name="PROTOCOL">HTTP/1.1</parameter>
<parameter name="Transfer-Encoding">chunked</parameter>
</transportSender>
+------------------------+
@@ -59,6 +59,7 @@
<item name="UDP" href="udp.html"/>
<item name="XMPP" href="xmpp.html"/>
<item name="SMS" href="sms.html"/>
<item name="Debug" href="debug.html"/>
</item>
<item name="Project Information" href="project-info.html">
<item name="Mailing Lists" href="mail-lists.html"/>

0 comments on commit 67aa25d

Please sign in to comment.