И Log4j 1.x, и Log4j 2.x поддерживают использование файлов XML для определения конфигурации ведения журнала . В этом посте рассматриваются некоторые нюансы и тонкости, связанные с использованием JAXB для работы с этими файлами конфигурации XML с помощью классов Java. Примеры в этом посте основаны на Apache Log4j 1.2.17 , Apache Log4j 2.6.2 и Java 1.8.0_73 с JAXB xjc 2.2.8-b130911.1802.
Log4j 1.x: log4j.dtd
XML грамматика Log4j 1.x определяется DTD вместо XML-схемы W3C . К счастью, реализация JAXB, поставляемая с JDK, предоставляет «экспериментальную, неподдерживаемую» опцию для использования DTD в качестве входных данных, из которых генерируются классы Java. Следующая команда может использоваться для запуска инструмента командной строки log4j.dtd
.
xjc -p dustin.examples.l4j1 -d src -dtd log4j.dtd
Следующий снимок экрана демонстрирует это.
Выполнение команды, описанной выше и показанной на снимке экрана, приводит к тому, что классы Java генерируются в пакете Java в каталоге src
именем dustin.examples.l4fj1
который позволяет log4j.dtd
демаршаллинг из XML, log4j.dtd
log4j.dtd, и маршалинг в log4j.dtd
— совместимый XML.
Log4j 2.x: Log4j-config.xsd
Конфигурация XML в Log4j 2.x может быть либо «краткой», либо «строгой», и мне нужно использовать « строгий » в этом посте, поскольку в этой форме используется грамматика, определенная файлом XML-схемы W3C Log4j-config.xsd
и I нужна схема для генерации классов Java с JAXB . Следующая команда может быть запущена для этой схемы XML для генерации классов Java, представляющих строгий XML Log4j2.
xjc -p dustin.examples.l4j2 -d src Log4j-config.xsd -b l4j2.jxb
Выполнение вышеупомянутой команды приводит к тому, что классы Java генерируются в пакете Java в каталоге src
именем dustin.examples.l4j2
который позволяет отменить вывод из XML, совместимого с Log4j-config.xsd
выполнить маршалинг в XML, совместимый с Log4j-config.xsd
.
В предыдущем примере я включил файл привязки JAXB с параметром -b
за которым следует имя файла привязки ( -b l4j2.jxb
). Эта привязка была необходима, чтобы избежать ошибки, которая не позволила xjc
сгенерировать Java4-совместимые с Log4j 2.x классы с сообщением об ошибке «Свойство« Значение »уже определено. Используйте <jaxb: property> для разрешения этого конфликта ». Эта проблема и способы ее решения обсуждаются в статье « Брит » на посту Бермудских островов. Свойство «Стоимость» уже определено. Используйте для разрешения этого конфликта . Источник для файла привязки JAXB, который я использовал здесь, показан ниже.
l4j2.jxb
01
02
03
04
05
06
07
08
09
10
11
|
< jxb:bindings version = "2.0" < jxb:bindings schemaLocation = "Log4j-config.xsd" node = "/xsd:schema" > < jxb:bindings node = "//xsd:complexType[@name='KeyValuePairType']" > < jxb:bindings node = ".//xsd:attribute[@name='value']" > < jxb:property name = "pairValue" /> </ jxb:bindings > </ jxb:bindings > </ jxb:bindings > </ jxb:bindings > |
Только что показанный файл привязки JAXB позволяет xjc
успешно анализировать XSD и генерировать классы Java. Одна небольшая цена, которую нужно заплатить (помимо написания и ссылки на файл привязки), заключается в том, что к атрибуту «value» KeyValuePairType
нужно будет обращаться в классе Java как к полю с именем pairValue
а не к value
.
Unmarshalling Log4j 1.x XML
log4j.dtd
использования для работы с классами, сгенерированными JAXB, для log4j.dtd
1.x и Log-config.xsd
Log4j 2.x Log-config.xsd
это преобразование XML-файлов конфигурации Log4j 1.x в «строгие» XML-файлы конфигурации Log4j 2.x , В этой ситуации потребуется log4j.dtd
Log4j 1.x log4j.dtd
-совместимый XML и маршалловый Log4j 2.x Log4j-config.xsd
-совместимый XML.
В следующем листинге кода показано, как XML Log4j 1.x может быть распакован с использованием ранее сгенерированных классов JAXB.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
/** * Extract the contents of the Log4j 1.x XML configuration file * with the provided path/name. * * @param log4j1XmlFileName Path/name of Log4j 1.x XML config file. * @return Contents of Log4j 1.x configuration file. * @throws RuntimeException Thrown if exception occurs that prevents * extracting contents from XML with provided name. */ public Log4JConfiguration readLog4j1Config( final String log4j1XmlFileName) throws RuntimeException { Log4JConfiguration config; try { final File inputFile = new File(log4j1XmlFileName); if (!inputFile.isFile()) { throw new RuntimeException(log4j1XmlFileName + " is NOT a parseable file." ); } final SAXParserFactory spf = SAXParserFactory.newInstance(); final SAXParser sp = spf.newSAXParser(); final XMLReader xr = sp.getXMLReader(); final JAXBContext jaxbContext = JAXBContext.newInstance( "dustin.examples.l4j1" ); final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); final UnmarshallerHandler unmarshallerHandler = unmarshaller.getUnmarshallerHandler(); xr.setContentHandler(unmarshallerHandler); final FileInputStream xmlStream = new FileInputStream(log4j1XmlFileName); final InputSource xmlSource = new InputSource(xmlStream); xr.parse(xmlSource); final Object unmarshalledObject = unmarshallerHandler.getResult(); config = (Log4JConfiguration) unmarshalledObject; } catch (JAXBException | ParserConfigurationException | SAXException | IOException exception) { throw new RuntimeException( "Unable to read from file " + log4j1XmlFileName + " - " + exception, exception); } return config; } |
Разобрать этот XML-файл Log4j 1.x было немного сложнее, чем разобрать его из-за характера log4j.dtd
пространства имен log4j.dtd
. Этот подход для решения этой проблемы описан в Gax’s Jaxb UnMarshall без пространства имен и в Deepa S ‘s Как инструктировать JAXB игнорировать пространства имен . Использование этого подхода помогло избежать сообщения об ошибке:
UnmarshalException: неожиданный элемент (uri: ”http://jakarta.apache.org/log4j/”, локальный: ”configuration”). Ожидаемые элементы …
Чтобы разобрать Log4j 1.x, который в моем случае ссылается на log4j.dtd
в файловой системе, мне нужно было предоставить специальное системное свойство Java для средства запуска Java при запуске этого кода с Java 8. В частности, мне нужно было указать
-Djavax.xml.accessExternalDTD=all
чтобы избежать сообщения об ошибке: «Не удалось прочитать внешний DTD, поскольку доступ к« файлу »не разрешен из-за ограничений, установленных свойством accessExternalDTD ». Дополнительные сведения об этом можно найти на странице Wiki NetBeans FaqWSDLExternalSchema .
Маршаллинг Log4j 2.x XML
Маршаллинг Log4j 2.x XML с использованием сгенерированных JAXB Java-классов довольно прост, как показано в следующем примере кода:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/** * Write Log4j 2.x "strict" XML configuration to file with * provided name based on provided content. * * @param log4j2Configuration Content to be written to Log4j 2.x * XML configuration file. * @param log4j2XmlFile File to which Log4j 2.x "strict" XML * configuration should be written. */ public void writeStrictLog4j2Config( final ConfigurationType log4j2Configuration, final String log4j2XmlFile) { try ( final OutputStream os = new FileOutputStream(log4j2XmlFile)) { final JAXBContext jc = JAXBContext.newInstance( "dustin.examples.l4j2" ); final Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true ); marshaller.marshal( new ObjectFactory().createConfiguration(log4j2Configuration), os); } catch (JAXBException | IOException exception) { throw new RuntimeException( "Unable to write Log4 2.x XML configuration - " + exception, exception); } } |
В этом случае сортировки есть одна тонкость, которая может быть неочевидна в только что показанном листинге кода. Классы, сгенерированные JAXB xjc
из Log4j-config.xsd
имеют классов с @XmlRootElement . Классы JAXB, которые были сгенерированы из log4j.dtd
1.x log4j.dtd
действительно включали классы с этой аннотацией @XmlRootElement . Поскольку классы Java на основе Log4j 2.x Log4j-config.xsd
не имеют этой аннотации, возникает следующая ошибка при попытке упорядочить экземпляр ConfigurationType
напрямую:
MarshalException — со связанным исключением: [com.sun.istack.internal.SAXException2: невозможно маршалировать тип «dustin.examples.l4j2.ConfigurationType» как элемент, потому что отсутствует аннотация @XmlRootElement]
Чтобы избежать этой ошибки, я вместо этого (строка 18 приведенного выше листинга кода) упорядочил результат вызова new ObjectFactory().createConfiguration(ConfigurationType)
для переданного экземпляра ConfigurationType
и теперь он успешно упорядочен.
Вывод
JAXB можно использовать для генерации классов Java из log4j.dtd
1.x и из Log4j Log4j-config.xsd
Log4j 2.x, но есть некоторые тонкости и нюансы, связанные с этим процессом, которые успешно генерируют эти классы Java и используют сгенерированные Java. классы для маршала и демаршала XML.
Ссылка: | Файлы конфигурации JAXB и Log4j XML от нашего партнера JCG Дастина Маркса из блога Inspired by Actual Events . |