[an error occurred while processing this directive] IT • archiv :: Print

IT • archiv


[an error occurred while processing this directive]

[an error occurred while processing this directive]

Проверка ввода данных на Java с использованием схем XML. Часть 2

[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::
[an error occurred while processing this directive](none)
[an error occurred while processing this directive]([an error occurred while processing this directive] Бретт МакЛафлин [an error occurred while processing this directive])

[an error occurred while processing this directive](none)

Использование схем XML для наложения ограничений на данные Java.

Data Validation
PDF versionPDF версия
Обзор
Управление конструкциями XML путем использования DTD или схем XML стало общепризнанной практикой. Однако, использование тех же самых механизмов для контроля за параметрами методов Java не столь распространено. В этой статье Брет рассматривает целиком весь путь обработки параметров в методах Java и проверки их правильности по отношению к ограничениями, указанным в документах XML. Исследуются различные подходы, а также примеры реальных кодов утилит для преобразования ограничений XML, которые можно с пользой применить в программах на Java. (3000 слов)

Существующее сегодня многообразие прикладного программного обеспечения на Java, казалось бы, предлагает API для решения любых возникающих задач: удаленный вызов методов (RMI), компоненты многократного использования (EJB), обработка XML (SAX, DOM, JDOM, JAXP) и интерфейс пользователя (Swing), а также разработка систем справочников (JavaHelp). Все же программисты до сих пор в каждом проекте тратят часы и даже дни, разрабатывая подпрограммы проверки правильности. Заметьте, не сложные формулы, а коды, гарантирующие, что значения имеют правильные типы при обработке формы HTML, или проверяющие соответствие данных диапазону размеров ботинок. Такое положение вещей приводит нас к мысли пересмотреть основные задачи, возникающие перед программистом.

Настоящая серия статей подробно рассматривает задачу проверки на Java для того, чтобы разрешить эту проблему, или по крайней мере показать методы решения до тех пор, пока не будет разработан устойчивый API для проверки правильности, этот ряд берет детальный, смотрят на проверку правильности в Java. Здесь не дается объяснение использования JavaScript в HTML или дорогих библиотеках независимых разработчиков, а вместо этого рассматривается создание простой вычислительной базы для проверки правильности, удовлетворяющей существующим стандартам. Цель статьи сконцентрирована на простоте использования и простоте самих средств добавления новых правил проверки и ограничения данных, которые не вносят излишнего беспорядка в деловую и представительную логику.

Повторение пройденного

Для начала читателю следует ознакомиться с Частью 1 настоящей серии статей. В этой статье были рассмотрены существующие подходы к решению задачи проверки правильности, в частности решения на основе чистого Java. Оба рассмотренных варианта (такие как проверка непосредственно в сервлете или компоненте JavaBean), а также вспомогательный класс (класс ParameterParser Джейсона Хантера) часто приводили к коду, в котором не все было в порядке, проверка правильности смешивалась с деловой и прикладной логикой. Кроме того, требовалось писать многочисленные блоки try/catch обработки исключений. В добавок оставались нежелательные проблемы с постоянной перекомпиляцией исходных кодов даже в случае минимальных изменений констант ограничений (например, при изменении допустимого диапазона значений с 0..20 до 1..20).

В качестве другого подхода к решению были рассмотрены файлы свойств Java. Для начала было отмечено: хотя Java позволяет записывать в файлах свойств значения, разделенные точками (key1.key2.key3 = value), язык не позволяет использовать их на понятном уровне. Например:

 ldap.hostname = galadriel.middleearth.com
 ldap.port = 389
 ldap.userDN = cn=Directory Manager
 ldap.password = foobar

Казалось бы, записи в примере файла свойств представляют собой логическую группу, так как все они начинаются с ключа ldap. Однако, в случае стандартного API Java это не так. Функционально этот набор записей соответствует:

 hostname = galadriel.middleearth.com
 port = 389
 userDN = cn=Directory Manager
 password = foobar
 

Другими словами, нет средств, которые, например, выбирали бы все ключи с корнем ldap. Таким образом многоуровневые ключи могут использоваться только для лучшего прочтения человеком и существенно добавляют сложностей при реальной программной обработке, если не использовать вспомогательных библиотек. Поэтому файлы свойств Java тоже не подходят для работы с большими массивами правил проверки.

В заключении был кратко затронут вопрос использования XML для хранения проверочных ограничений. Основное внимание было уделено схемам XML, поскольку в них уже заложен механизм проверки значений в документе XML, обеспечивающий проверку типов и одновременно понятный для пользователя. Он позволяет задавать границы диапазона величин, наборы значений перечисляемых типов, и все это на основе простого синтаксиса. В этой статье мы дальше углубимся в рассмотрение вопросов использования XML для наложения ограничений на данные, поставляемые в приложения Java, чтобы читатель смог самостоятельно создать работающий код с использованием XML. В первую очередь рассмотрим возможности XML.

Многократное использование опций

Теперь мы определились с тем, каким образом хотим использовать схемы XML наложения ограничений на данные. Приступим к исследованию специфики вопроса. Основное положение, на котором будет строиться наша методика состоит в том, что описание ограничений записано на одном языке, XML, а данные обрабатываются на другом, Java. Так что преобразования определенно будут иметь место. Как же следует поступить? Преобразовать все данные в XML, и затем проверять правильность этого XML по заданной схемы? Или преобразовать схему XML в объекты Java, и использовать их для проверки данных? А может быть комбинировать эти методы?

Переход от Java к XML

Довольно просто выглядит первый вариант, преобразование данных Java в XML. Благодаря росту популярность связывания данных XML, появилось несколько оболочек для преобразования или создания представления документов XML в виде объектов Java. Одна из таких оболочек, которую разработал автор статьи, подробно обсуждается в статье "Объекты, везде объекты" (см. Ресурсы). Для преобразований между Java и XML предоставляется завершенная работающая библиотека. Она обладает всеми необходимыми качествами для решения задачи проверки — простым, компактным и интуитивно понятным API.

Однако, если задуматься, связывание данных не так уж безупречно. Во-первых, данные Java часто могут храниться в четырех, пяти и более объектах. Представим форму с 15 полями ввода, каждое из которых представлено отдельным объектом Java (в данном случае String). По каждому из них надо построить один объект, представляющий собой узел XML, а затем выполнить проверку по схеме XML. Затем в код надо включить логику преобразования множества объектов в один объект, подходящий для преобразования в XML. Мы видим, что решение, казавшееся простым уже начинает закручиваться в спираль.

Кроме того, этот вариант не позволяет проводить оптимизацию. Нельзя сохранить схему XML в памяти Java машины, единственным реальным преимуществом является возможность кэширования схемы документа XML (в объектах Document моделей DOM или JDOM). Другими словами, при выполнении многократных проверок производительность не повышается. Хотя эту причину и можно считать не существенной, следует принять во внимание, что проверка повторяется сотни и тысячи раз (особенно проверка данных форм) на каждой странице. При таком положении вещей кэширование или другие способы повышения производительности становятся просто необходимыми. Можно добавить, что разбор XML является затратной операцией, и даже если документ схемы XML был кэширован, связанный с ним объект Java, преобразуемый в документ XML, должен подвергаться разбору при каждом вызове процедуры проверки. Таким образом, идея перехода от Java к XML не так уж хороша.

От XML к Java

Так как преобразование из Java в XML оказалось не столь хорошей идеей, рассмотрим обратное преобразование — от XML к Java. В этом случае отсутствует необходимость привязывать данные Java к XML. Вместо этого следует преобразовывать ограничения схемы XML в объекты Java. Тогда эти объекты могли бы принимать данные и возвращать результат, указывающий, соответствуют ли данные тем ограничениям, которые записаны в объекте, полученном по представлению схемы XML. Такой подход является гораздо более естественным для разработчиков на Java, поскольку позволяет им оставаться в знакомой среде программирования.

Другое преимущество этой методики состоит в том, что она эффективно изолирует схему XML от уравнения. При таком решении схема используется только в процессе преобразования XML в Java, и вовсе не пересекается с целями использования результирующих объектов Java. Другими словами, если изменения претерпевает реализация классов проверки правильности, при преобразовании документа XML (а не схемы) в проверочный объект, разработчик будет иметь дело с тем же самым интерфейсом для проверки правильности; прикладной код не должен измениться. Почему мы обращаем здесь на это внимание? На то есть две причины. Во-первых, стандарт схем XML все еще находится на стадии завершения, и возможны незначительные изменения в спецификации. Использование описанной методики гарантирует, что можно использовать классы проверки в прикладном коде и даже если спецификация на схемы XML и коды классов проверка изменятся, это не повлечет за собой изменений прикладного кода. Во-вторых, все еще распространенно некоторое беспокойство на счет принятия схем XML. Если они не удовлетворят ваших потребности, или если они окажутся слишком сложными для вашего приложения, можно довольно просто переключиться на более простой механизм (например, на простые документы XML или Relax), сохранив рабочими код программ.

Возвращаясь к первоначальным трудностям, отметим, что документ по схеме XML подлежит всего лишь однократному разбору. Схема должна быть преобразована в объекты Java, представляющие ограничения, которые затем хранятся в памяти. Эти объекты можно вновь и вновь использовать для проверки данных без проведения какого-либо дополнительного разбора. Это касается вопросом производительности, которые рассматривались в разделе по преобразованию от Java к XML. Эти вопросы могут стать даже более критическими, когда возникнет необходимость искать схемы XML среди сетевых ресурсов, создавая трафик при каждом разборе синтаксиса.

Теперь кажется очевидным, что преобразование от XML в Java является правильным выбором. В дополнение к сказанному можно отметить, что недостаточно просто получить объект Java (как в случае привязки данных, когда обеспечивается соответствие документа по схеме XML в объекты Java); этот объект должен принять часть данных, и затем вернуть ответ, являются ли данные допустимыми для ограничений, заданных в схеме XML.

План игры

Разобравшись с основными решениями проблемы, переходим к освоению и кодированию классов.

Элементы и атрибуты

В первую очередь предстоит решить, какой набор конструкций схемы XML необходимо поддерживать. Кажется логичным попробовать создать поддержку полной спецификации схем XML, однако эта задача настолько сложна, что только для ее описания потребуется во много раз больше статей, чем в данной серии. Например, одни из основных атрибутов схемы minOccurs и maxOccurs не имеют никакого значения в контексте проверки правильности; значение либо существует, либо нет, а повторно в контексте появляться не может. Отсюда можно сделать вывод, что часть конструкций схем не будет участвовать в проверке.

Фактически, возвращаясь назад к части 1, тэг element схем XML не нужен совсем. Известно, что элементы в XML, как правило, служат для представления сложных, повторяющихся структур данных. Чаще всего они соответствуют объектам Java (в основном, не примитивным), а не отдельным переменным. Например, рассмотрим такую схему:

<?xml version="1.0"?>

 <schema targetNamespace="http://www.enhydra.org"
         xmlns="http://www.w3.org/1999/XMLSchema"
         xmlns:enhydra="http://www.enhydra.org"
 >

  <complexType name="ServiceConfiguration">
     <attribute name="name" type="string" />
     <attribute name="version" type="float" />
   </complexType>

   <element name="serviceConfiguration" 
 	type="ServiceConfiguration" />
 </schema>
 

Эта схема легко преобразуется в объекты Java, показанные ниже:

public class ServiceConfiguration {
     private String name;
     private float version;

     public String getName() {
         return name;
     }

     public void setName(String name) {
         this.name = name;
     }

     public float getVersion() {
         return version;
     }

     public void setVersion(float version) {
         this.version = version;
     }

 }

Здесь атрибуты схемы соответствуют переменным Java (типов данных, подлежащих проверке), в элементы относятся к сложным объектам Java. Что это означает для нас? Фактически, довольно много. По существу, можно обойтись без поддержки элементов в схеме XML. Вместо этого, работу можно упростить, сосредоточившись на типах атрибутов (attribute).

Каждому элементу данных, который следует проверять, необходимо задать имя. Это имя имеет смысл идентификатора данных, отличаясь от имени переменной. Это означает, что передача набора данных с заданным именем в проверочную процедуру приведет к проверке этих данных на соответствие ограничениям этого идентификатора. Затем можно определить ограничения на тип данных (int, String, и т.д.) и разрешенные значения.

Теперь, когда рассмотренные решения начинают воплощать план игры в жизнь, вернемся к схеме XML, рассмотренной в части 1.

<?xml version="1.0"?>

 <schema targetNamespace="http://www.buyShoes.com"
         xmlns="http://www.w3.org/199/XMLSchema"
         xmlns:buyShoes="http://www.buyShoes.com"
 >

   <attribute name="shoeSize">
     <simpleType baseType="integer">
       <minExclusive value="0" />
       <maxInclusive value="20" />
     </simpleType>
   </attribute>

   <attribute name="brand">
     <simpleType baseType="string">
       <enumeration value="Nike" />
       <enumeration value="Adidas" />
       <enumeration value="Dr. Marten" />
       <enumeration value="V-Form" />
       <enumeration value="Mission" />
     </simpleType>
   </attribute>

 </schema>
 

Здесь происходит следующее. Созданы два идентификатора: "shoeSize" и "brand." Размер ботинок должен иметь тип int Java, быть больше 0, и меньше или равен 20. Марка записывается типом Java String, и перечислены несколько ее значений. В дальнейшем мы более детально обсудим каждое из этих ограничений.

Типы данных

Поддержка проверки правильности типа данных довольно проста. Разработка структуры кода начинается с определения основных примитивов Java. Поэтому код проверки правильности подобен коду класса ParameterParser, рассмотренного в части 1. Параметром каждого из разрабатываемых методов проверки является String. Так должно быть почти для всего ввода, особенно из форм HTML. Если Вы еще новичок в этой области, спросите Вашего старшего товарища, и он скажет Вам, что данные почти всегда приходят в виде простых строк. Результатом преобразования будет, конечно же, объект необходимого типа данных:

    public int getIntParameter(String value)
         throws NumberFormatException {
         return Integer.parseInt(value);
     }
 

Этот фрагмент кода представляет общую идею; в следующем разделе мы разработаем код реально действующего класса проверки. При этом мы увидим, что преобразование типов данных представляет собой довольно простую процедуру.

Ограничения значений

Другой важный аспект, поддержку которого следует обеспечить — ограничение значений. Ограничения значений — элементы, которые описывают разрешенные значения, после того, как был установлен тип данных, например ограничения размера обуви, которые гарантирует, что его величина лежит между 1 и 20 (включительно). Ограничения такого типа в общем случае реализуются двумя конструкциями схемы XML: enumeration, которая определяет позволенные символьные значения, и ключевыми словами minXXX и maxXXX, которые определяют минимальное и максимальное разрешенные значения. (как включительно, так и исключительно).

Используя эти два наборов ключевых слов, содержащихся в свойстве схемы simpleType, можно задать представление почти всех основных ограничений на данные. Обратим внимание также, что этим набором почти полностью ограничивается потребность решаемой задачи. В следующей статье, мы коснемся ключевого слова pattern, выполняющего сопоставление с образцом (например, шаблоном поиска) в символьных значениях. Это добавит еще один инструмент ограничения к имеющемуся арсеналу.

Теперь, когда мы прошли стадию дизайна, можно переходить к коду. Рассмотрим подготовительные работы процедуры проверки правильности.

Практика

В общем случае придется создать четыре класса:

Код, разрабатываемый для этой статьи, практически использовался на сервере приложений Enhydra (информацию о котором можно получить в Ресурсах), он помещался в библиотеку org.enhydra.validation. Кроме того, код, представленный в данной серии является открытым, что означает, что его можно менять по своему усмотрению (добавлять свойства и функциональность). Вы можете также отправлять сделанные изменения по электронной почте в список рассылки Enhydra или мне, эти изменения будут добавлены в главное хранилище. Таким образом, мы рассмотрели все детали и переходим к самим классам.

Класс Constraint

Первый класс является, возможно, простейшим и служит для построения компилируемого кода на основе изложенных идей. Класс Constraint представляет набор ограничений для определенного типа данных. Естественно, он определяется идентификатором! А поэтому, Код конструктора просто принимает этот идентификатор в качестве обязательного параметра. Затем необходимы методы для различных типов ограничений, таких как минимальное включительное значение. Для каждого типа ограничений нужно написать методы выборки (методы "get"), изменения (методы "set") и опроса (методы "has"). Так, для ограничения minInclusive, следует реализовать следующие три метода:

    
     /**
      * <p>
      * задание минимального допустимого значения
      * для этого типа данных (включительно).
      * </p>
      *
      * @param minInclusive минимальное допустимое значение
      * (включительно)
      */
     public void setMinInclusive(double minInclusive) {
         this.minInclusive = minInclusive;
     }

     /**
      * <p>
      * выборка минимального допустимого значения
      * для этого типа данных (включительно).
      * </p>
      *
      * @return <code>double</code>
      * — минимальное допустимое 
      * значение (включительно)
      */
     public double getMinInclusive() {
         return minInclusive;
     }

     /**
      * <p>
      * возвращает <code>true</code>, если минимальное
      * допустимое значение (включительно) существует
      * </p>
      *
      * @return <code>boolean</code> 
      * — существует ли ограничение на
      * минимальное значение (включительно)
      */
     public boolean hasMinInclusive() {
         return (minInclusive != Double.NaN);
     }
 

Подобные методы создаются для ограничений minExclusive, maxInclusive, и maxExclusive. Дополнительно потребуется средство добавления допустимых значений, используемых для чтения перечислений enumeration из схемы XML. Также потребуются методы для выборки значений и проверки их существования.

    /**
      * <p>
      * добавить новое значение в список
      * дозволенных для этого типа данных.
      * </p>
      *
      * @param value добавляемое значение, <code>String</code>.
      */
     public void addAllowedValue(String value) {
         allowedValues.add(value);
     }

     /**
      * <p>
      * выбрать список дозволенных
      * значений для этого типа данных.
      * </p>
      *
      * @return <code>List</code> — 
      * разрешенные значения в классе 
      * <code>Constraint</code>.
      */
     public List getAllowedValues() {
         return allowedValues;
     }

     /**
      * <p>
      * проверить существование списка
      * дозволенных значений для этого типа данных.
      * </p>
      *
      * @return <code>boolean</code> — существует ли список 
      * значений для этого типа?
      */
     public boolean hasAllowedValues() {
         if (allowedValues.size() == 0) {
             return true;
         } else {
             return false;
         }
     }
 

Конечно, нужны методы установления типа данных и его выборки из схемы. То есть установления в Java типа, эквивалентного типу, указанному в схеме атрибутами type или baseType Приведем примеры таких методов:

    
     /**
      * <p>
      * установить тип данных ограничения.
      * Тип указывается как Java <code>String</code>.
      * </p>
      *
      * @param dataType <code>String</code>
      * тип данных Java для ограничения
      */
     public void setDataType(String dataType) {
         this.dataType = dataType;
     }

     /**
      * <p>
      * возврат варианта <code>String</code> типа данных Java
      * для данного ограничения.
      * </p>
      *
      * @return <code>String</code> — тип данных для ограничения.
      */
     public String getDataType() {
         return dataType;
     }

Это все, что нужно для создания класса Constraint. В Resources можно найти этот класс целиком. Как видно, класс представляет собой всего лишь набор методов get и set. То есть это лишь представление на Java набора ограничений на данные. Его интенсивно использует следующий класс, который нам предстоит создать, Validator.

Класс Validator

Наиболее заметную роль в коде проверки правильности играет класс Validator — средство разработчика для управления проверкой. Разработчик передает данные, подлежащие проверке, в экземпляр класса и получает простой ответ типа boolean. Затем он может отвергнуть данные, инициировать ошибки или предпринять другие действия.

Однако прежде всего скажем несколько слов о том, как создать такой класс. В отличие от большинства классов, класс Validator лучше не создавать непосредственно (вызовом конструктора по new). Код проверки может использоваться сервлетом, обслуживающим много потоков, несколькими сервлетами в нескольких потоках, а также несколькими классами в нескольких потоках. Если каждый объект будет создавать новый экземпляр класса Validator, это приведет к многократному вызову процедуры разбора схемы, часто одной и той же. Вместо этого, хочется только один раз выполнить разбор схемы XML. Поэтому следует использовать проектный шаблон Singleton, который гарантирует доступность единственного экземпляра данного класса всем потокам в JVM. Однако, можно произвести некоторые изменения. Поскольку один экземпляр не достаточен, а необходим один экземпляр на каждую схему XML, придется использовать несколько экземпляров, каждый из которых привязывается к определенной схеме. Запросы к экземпляру схемы, класс для которой создан, будут возвращаться без повторного выполнения разбора. Поэтому теперь можно создать ядро класса Validator.

package org.enhydra.validation;

 import java.net.URL;
 import java.util.HashMap;
 import java.util.Map;

 /**
  * <p>
  * Класс <code>Validator</code> принимает данные
  * и определяет, являются ли значения правильными
  * для запрошенного типа.
  * </p>
  */
 public class Validator {

     /** Экземпляр используемого класса
      * (единственный проектный шаблон) */
     private static Map instances = null;

     /** URL схемы XML */
     private URL schemaURL;

     /** Ограничения для схемы XML */
     private Map constraints;

     /**
      * <p>
      * Скрытый конструктор, не позволяющий создать
      * класс непосредственно, а только через
      * <code>{@link #getInstance()}</code>.
      * </p>
      */
     private Validator(URL schemaURL) {
         this.schemaURL = schemaURL;
         constraints = new HashMap();

         // разбор схемы XML и создание ограничений
     }

     /**
      * <p>
      * Возврат экземпляра URL заданной схемы XML.
      * Если схема существует, возвращается ее URL
      * (то есть разбор уже проведен), иначе
      * создается и возвращается новый экземпляр.
      * </p>
      *
      * @param schemaURL <code>URL</code> проверочной схемы.
      * @return <code>Validator</code> — экземпляр, готовый
      * к употреблению.
      */
     public static Validator getInstance(URL schemaURL) {
         if (instances != null) {
             if (instances.containsKey(schemaURL.toString())) {
                 return (Validator)instances.get(schemaURL.toString());
             } else {
                 Validator validator = new Validator(schemaURL);
                 instances.put(schemaURL.toString(), validator);
                 return validator;
             }
         } else {
             instances = new HashMap();
             Validator validator = new Validator(schemaURL);
             instances.put(schemaURL.toString(), validator);
             return validator;
         }
     }
 }

Итак, конструктор класса сделан приватным. Для создания схемы для использования ограничений программисты вызовут метод getInstance(). Если экземпляр для заданной схемы существует, он возвращается без нового создания. Однако, если нет ни одного экземпляра, или нет экземпляра для конкретной схемы, создается новый экземпляр класса. В конструкторе вместо метода, который вызывает процедуру разбора и создание списка ограничений, помещен комментарий. Здесь экземпляр готовится к использованию. Мы рассмотрим позднее, что в действительности происходит в классе SchemaParser.

Наконец, надо создать методы, которые позволят разработчикам проверять их данные (вспомним, что это и есть цел наших упражнений!). Это тоже просто, ниже мы рассмотрим идею их реализации. В следующей статье, мы заполним пробелы в логике:

    
     /**
      * <p>
      * Проверка значений данных (в формате <code>String</code>)
      * на соответствие заданным ограничениям,
      * если значение удовлетворяет требованиям
      * ограничений, возвращается <code>true</code>
      * </p>
      *
      * @param constraintName идентификатор
      * ограничения на значения.
      * @param data <code>String</code> данные для проверки.
      * @return <code>boolean</code> — данные
      * соответствуют или нет.
      */
     public boolean isValid(String constraintName, String data) {
         // проверить по заданным ограничениям
         // код в следующей статье

         // пока вернем true
         return true;
     }

На этом прервем изложение. Посмотрим теперь, каково текущее состояние дел и что делать дальше.

Заключение

Мы продвинулись достаточно далеко в концептуальных вопросах. Утверждено детальное руководство по построению оболочки кода, мы знаем, какие классы надо разработать. Один из них, а именно класс Constraint, закончен, второй, класс Validator, описан в виде рабочего проекта с реализацией части кода.

В следующей статье мы рассмотрим важную задачу действительного разбора схем XML и построения списка объектов Constraint для использования в классе Validator. Мы завершим вспомогательный класс DataConverter, а также класс Validator, который начали здесь. Это подведет итог исследованию! Мы рассмотрим несколько примеров, чтобы разобраться с использованием кода, а также обсудим некоторые вопросы расширенного использования, такие как шаблоны работы и сообщения об ошибках. Пока поэкспериментируйте с кодом, до встречи онлайн!

Об авторе

Бретт МакЛафлин — стратег по Enhydra в Lutris Technologies, специализирующийся на архитектурах распределенных систем. Он является автором Java and XML и работает с такими технологиями как сервлеты Java, технология Enterprise JavaBeans, XML, приложения бизнес-ту-бизнес. Недавно он организовал проект JDOM совместно с Джейсоном Хантером, в котором разработал простой API для работы с XML из приложений Java. МакЛафлин активно участвует в разработке проекта Apache Cocoon и сервера EJBoss EJB, и является сооснователем проекта Apache Turbine.

Ресурсы

Интересует JDOM? Читайте статьи JavaWorld:

Reprinted with permission from the October 2000 edition of JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company.
View the original article at: http://www.javaworld.com/ javaworld/jw-10-2000/jw-1013-validation2.html

[an error occurred while processing this directive]
[an error occurred while processing this directive] Перевод на русский © Андрей Ковалев, к.т.н., доцент МИЭТ, 2000
< Вернуться на caйт :: Copyright © 1999 — 2010, IT • archiv.