Bug: Unloading child or parent plugin fails (+workaround)

If a plugin that’s in a parent/child relationship is being unloaded (or reloaded) from Openfire, nasty stuff happens. The most visible problem is a big HTTP 500 Error. The logs will include a stacktrace similar to this one:

10:06:41,048 btpool0-83 WARN org.jivesoftware.util.Log - Error unloading plugin webutils. Will attempt again momentarily.

10:06:49,277 btpool0-83 WARN org.jivesoftware.util.Log - Error unloading plugin webutils. Will attempt again momentarily.

10:06:57,534 btpool0-83 WARN org.jivesoftware.util.Log - /plugin-admin.jsp java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at org.jivesoftware.openfire.container.PluginManager.unloadPlugin(PluginManager.ja va:533) at org.jivesoftware.openfire.container.PluginManager.unloadPlugin(PluginManager.ja va:594) at org.jivesoftware.openfire.container.PluginManager.unloadPlugin(PluginManager.ja va:535) at org.jivesoftware.openfire.admin.plugin_002dadmin_jsp._jspService(plugin_002dadm in_jsp.java:132) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:97) at javax.servlet.http.HttpServlet.service(HttpServlet.java:802) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.ja va:1093) at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:11 8) at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:52) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.ja va:1084) at org.jivesoftware.util.LocaleFilter.doFilter(LocaleFilter.java:65) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.ja va:1084) at org.jivesoftware.util.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingF ilter.java:41) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.ja va:1084) at org.jivesoftware.admin.PluginFilter.doFilter(PluginFilter.java:69) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.ja va:1084) at org.jivesoftware.admin.AuthCheckFilter.doFilter(AuthCheckFilter.java:98) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.ja va:1084) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405) at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollect ion.java:211) at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139) at org.mortbay.jetty.Server.handle(Server.java:313) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:506) at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.j ava:830) at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:514) at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:381) at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:396) at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442)

The problem is caused by PluginManager. It tries to delete the same plugin directory twice (once because each parent has at least one child, and once because each child has on parent).

The deleteDir(File dir) method will return ‘false’ if PluginManager asks it to delete a nonexisting directory. Because the delete fails, the plugin-unload action appears to fails as well.

An easy fix/workaround is implemented by changing line 1134 as shown below.

This is the existing code:

boolean deleted = dir.delete();
if (deleted) {
    // Remove the JAR/WAR file that created the plugin folder
    pluginFiles.remove(dir.getName());
}
return deleted;

This is the workaround:

boolean deleted = !dir.exists() || dir.delete();
if (deleted) {
    // Remove the JAR/WAR file that created the plugin folder
    pluginFiles.remove(dir.getName());
}
return deleted;

Hi Guus,

you should ask Gato or Matt for JIRA access, so you could create the issues there.

I did create JM-1178.

The code “boolean deleted = !dir.exists() || dir.delete();” looks ‘evil’, I hope that Gato makes it more easy to read when he fixes this.

LG

Try to read it as: “If the dir does not exist, or if it does exist but can be deleted…”