2
votes

The problem:
Deploying the XCC library with a WAR file (in WEB-INF/classes) prohibits re-deployments/updates of that webapp without restarting the whole Tomcat container. It just stops working, because there are still resources allocated and therefore some JAR files could not be removed by Tomcat.
When deploying the library in Tomcats lib directory (as a JDBC driver would be), Tomcat writes memory leak warnings in Tomcat log as followed:

17-Mar-2016 10:58:45.683 WARNING [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [api] appears to have started a thread named [Thread-4] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:  java.lang.Thread.sleep(Native Method)  com.marklogic.xcc.ContentSourceFactory$ConnectionCollector.run(ContentSourceFactory.java:449)

What I found out:
I did some investigation and found out, that (at least in XCC 8.0.3) there is a thread started in the ContentSourceFactory that is never shut down. That thread is affecting updates/re-deployments of servlets and creates memory leaks. My quick-win solution was to create a custom ContentSourceFactory, having a shutdown method which gets called when the servlet stops.

The question:
Unfortunately I didn't find any clues in the documentation. So whats the official way to use the XCC library (8.x) in a servlet container?

2

2 Answers

2
votes

This thread does appear to run for the duration of the JVM. I will open a defect for this.

A workaround based on experience with similar issues with different products in tomcat. DB drives (JDBC in particular) have historically been problematic in this way.

Put the xcc.jar into the tomcat's 'shared library' class path instead of in the WAR. This is not recommend in general -- but OTOH its exactly why there exists a shared library class path -- This allows your application to be redeployed. The XCC code is thread safe and context independent as long as your not sharing active connection objects yourself it should work fine.

Also make sure you diligently shutdown or close all response objects, the finalizer may take a long time to kick in.

-David

0
votes

My temporary solution:
As DALDEI mentioned, it looks like an open issue in the XCC library. To do a temporary workaround I wrote a DisposableContentSourceFactory based on the original sources (8.0.4). The code can be found here: https://gist.github.com/Mario-Eis/a16437c35d2aa097f668

There is also an advantage in that approach, as it can be smoothly integrated in an IoC context e.g. Spring IoC.

@Configuration
public class XccContext {

    @Bean(destroyMethod = "shutdown")
    public DisposableContentSourceFactory contentSourceFactory() {
        return DisposableContentSourceFactory.create();
    }

    @Bean
    public ContentSource contentSource(XccConfiguration xccConfiguration, DisposableContentSourceFactory contentSourceFactory) throws XccConfigException {
        return contentSourceFactory.newContentSource(xccConfiguration.connectionString());
    }

    @Bean(destroyMethod = "close")
    @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
    public Session marklogicSession(ContentSource contentSource) {
        return contentSource.newSession();
    }
}

To assure a bit of "compatibility" to the official static class approach, I also included a static factory method create() .

If there is a public GIT repository, I could file a pull request.