Now, the Logback manual Ch. 3, about Joran configuration, suggests basically to use:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
lc.shutdownAndReset();
configurator.doConfigure($source$);
} catch (JoranException je) {
StatusPrinter.print(lc);
}
where I use $source$ for the possible options in Logback, which are basically InputSource, a File, a String specifying a filename, an URL, and last but not least (as we will see) a List<SaxEvent>.
Investigating a little bit, all these methods, with exception of the last (i.e. List<SaxEvent>), use SaxEventRecorder to obtain List<SaxEvent>:
public ListrecordEvents(InputSource inputSource)
throws JoranException {
SAXParser saxParser = null;
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setValidating(false);
spf.setNamespaceAware(true);
saxParser = spf.newSAXParser();
} catch (Exception pce) {
String errMsg = "Parser configuration error occured";
addError(errMsg, pce);
throw new JoranException(errMsg, pce);
}
try {
saxParser.parse(inputSource, this);
return saxEventList;
} catch (IOException ie) {
String errMsg = "I/O error occurred while parsing xml file";
addError(errMsg, ie);
throw new JoranException(errMsg, ie);
} catch (Exception ex) {
String errMsg = "Problem parsing XML document. See previously reported errors. Abandoning all further processing.";
addError(errMsg, ex);
throw new JoranException(errMsg, ex);
}
}//recordEvents
The problem for an OSGi based use is simple; it's not recommendable to simply use JAXP, namely this:
SAXParserFactory spf = SAXParserFactory.newInstance();
The problem is related to the fact that the JAXP discovery mechanism assume visibility of the parser through the context ClassLoader of the actual thread, which may cause some troubles.
Thus, it is suggested to acquire the SAXParserFactory from a specific bundle, using the ServiceRegistry.
Now, to use the JoranConfigurator in an OSGi environment (e.g. a Log bundle), it needs a little trick to ensure that the SAXParserFactory reference is obtained the "OSGi way".
The mechanism I am proposing here is to simply extend and override the SaxEventRecorder:
private static class MySaxEventRecorder extends SaxEventRecorder {
public ListrecordEvents(InputSource inputSource)
throws JoranException {
SAXParser saxParser = null;
try {
saxParser = c_Services.getSAXParserFactory(ServiceMediator.WAIT_UNLIMITED).newSAXParser();
} catch (Exception pce) {
String errMsg = "Parser configuration error occured";
addError(errMsg, pce);
throw new JoranException(errMsg, pce);
}
try {
saxParser.parse(inputSource, this);
return saxEventList;
} catch (IOException ie) {
String errMsg = "I/O error occurred while parsing xml file";
addError(errMsg, ie);
throw new JoranException(errMsg, ie);
} catch (Exception ex) {
String errMsg = "Problem parsing XML document. See previously reported errors. Abandoning all further processing.";
addError(errMsg, ex);
throw new JoranException(errMsg, ex);
}
}
}//MySaxEventRecorder
This line here:
saxParser = c_Services.getSAXParserFactory(ServiceMediator.WAIT_UNLIMITED).newSAXParser();
will obtain a SAXParserFactory the way I described in this blog entry about mediating OSGi services.
You may also use an alternative, like:
private SAXParserFactory getParserFactory()
throws Exception {
ServiceReference refs[] = m_BundleContext.getServiceReferences(
SAXParserFactory.class.getName(),
"(&(parser.namespaceAware=true)"
+ "(parser.validating=true))");
if (refs == null) {
throw new Exception("Could not obtain SAX Parser Factory.");
}
return (SAXParserFactory) m_BundleContext.getService(refs[0]);
}//getParserFactory
The last piece that is missing is to change the configuration to work this way:
try {
SaxEventRecorder recorder = new MySaxEventRecorder();
recorder.setContext(c_LoggerContext);
recorder.recordEvents(new InputSource(new FileInputStream(f)));
} catch (JoranException je) {
StatusPrinter.print(c_LoggerContext);
}
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(c_LoggerContext);
Activator.getLoggerContext().shutdownAndReset();
configurator.doConfigure(f);
...and voila! Logback is configured with a SAXParser from a SAXParserFactory obtained the "OSGi way".


2 comments:
Thank you for this post. Could you describe in a paragraph your OSGi environment?
I have been working mainly with Equinox (R4 reference implementation) at the time of the post.
However, I think the idea behind the post is to avoid OSGi classloading problems and side effects, which should apply for any compliant container.
Post a Comment