Details
-
Bug
-
Resolution: Fixed
-
Medium
-
2.4.5
-
Running Linux/i686 with JDK 1.6, Resin 3.0, HSQL embedded database
Description
Short intro follows. I've written a Quartz job which exports all spaces of a wiki to HTML and PDF each night. In order to get this working, I had to hack Confluence source a bit because the renderer depends on an instance of javax.servlet.ServletContext. the getResourceAsStream() method of ServletContext is used to fetch external resources for rendering. However, when running as a Quartz job, the thread doesn't have ServletContext available and bad things will happen. My suggestion is to either a) make resource loading independent of ServletContext and use this new code when loading resources or b) ensure there's a ServletContext available regardless if the running thread is a servlet or not and continue using current implementation. a) is probably the right thing to do but probably much harder to implement. I quickly solved the problem by going the b) route. Next I'm going to describe this ugly hack in more detail.
Here is the bit from my export job class which calls the Confluence export code:
private File[] exportSpace(Space space, User user) throws ImportExportException, IOException {
log.info("Exporting space " + space.getKey() + " as user " + user.getName());
ContentTree contentTree = importExportManager.getContentTree(user, space);
DefaultExportContext context = new DefaultExportContext();
context.addWorkingEntity(space);
context.setScope(ImportExportManager.EXPORT_XML_SCOPE_SPACE);
context.setExportComments(true);
context.setExportAttachments(true);
context.setDateFormatter(userAccessor.getConfluenceUserPreferences(user).getDateFormatter(formatSettingsManager));
context.setContentTree(contentTree);
context.setType(ImportExportManager.TYPE_HTML);
String exportPathHtml = importExportManager.exportAs(context);
And when the job is executed with unpatched Confluence 2.4.5, NullPointerException shown in export-null-pointer-exception.log will be thrown.
At line 12 of java/com/atlassian/core/filters/ServletContextThreadLocal.java getRequest() returns null and getSession() on that null results in NullPointerException. I think line 12 should be replaced by something like this line:
return getRequest() != null ? getRequest().getSession().getServletContext() : null;
However, I wanted to continue using atlassian-core jar from Atlassian Maven2 repository so I didn't want directly patch ServletContextThreadLocal.java. Instead, I created attached HackedServletContextThreadLocal and HackedServletContextThreadLocalFilter classes and replaced all occurrences of ServletContextThreadLocal and ServletContextThreadLocalFilter in Confluence code and descriptors with these classes.
Then I created a new servlet (SaveServletContextServlet), which is load on startup in web.xml. The servlet just saves its servlet context as a property of Confluence bootstrap manager.
Then I patched java/com/atlassian/confluence/importexport/impl/AbstractRendererExporterImpl.java with patch found in safer-AbstractRendererExporterImpl.java.diff. Now servlet context is retrieved using the servlet contex thread local hack class. If it returns null (NullPointerException is thrown anymore), it tries to retrieve a servlet context from the bootstrap manager property saved by the servlet described above. Now we should have a working servlet context whether the running thread is a servlet or Quartz job. It's ugly but it does the trick for me.
Attachments
Issue Links
- is cloned from
-
CONFSERVER-7129 A way to access resources of the Confluence web application from code not run under a servlet?
- Closed
- is related to
-
CONFSERVER-9896 Rework the Space Export functionality - broken by 2.6 changes.
- Closed
- relates to
-
CONFSERVER-9693 Exporting a space that is associated to a SpaceGroup causes an error (lazy init exception)
- Closed
-
CONFSERVER-9659 PDF space export failing in Resin 3.x due to incorrect handling of HttpServletRequest
- Closed