I have recently started to work a little bit with
Logback, to see whether I could overhaul and enhance Coalevo logging completely; primarily because it really never was a good idea to use Commons-Logging in the first place.
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 List recordEvents(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 List recordEvents(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".