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;