И 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 . |

