Статьи

XML-файлы конфигурации JAXB и Log4j

И 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

Следующий снимок экрана демонстрирует это.

111

Выполнение команды, описанной выше и показанной на снимке экрана, приводит к тому, что классы 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

112

Выполнение вышеупомянутой команды приводит к тому, что классы 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"
              xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <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 .