В этом посте описывается, как читать динамическую модель EMF из набора файлов XML-схем (XSD) и как использовать эту модель для преобразования XML в SDO DataObjects или EMF EObjects, и все это в автономной среде.
Небольшое напоминание об EMF : с EMF вы сначала объявляете модель, например, на основе UML или XSD (см. Мой предыдущий пост для общего краткого введения EMF ). Модель может быть либо статической, в этом случае класс Java создается для каждой модели EClass с обычными получателями и установщиками, либо она может быть динамической, которая не требует генерации кода, а атрибуты доступны только через общий eGet, eSet методы. Затем вы можете создавать, загружать и сохранять экземпляры модели, например, из / в XML.
Чтобы перейти из XSD в XML, преобразованный в объект E [Data] времени выполнения, вам необходимо:
- Загрузите элементы модели, определенные в XSD, в EPackages
- Зарегистрируйте загруженные пакеты либо в глобальном реестре пакетов, либо в реестре пакетов ResourceSet, который будет использоваться для загрузки XML-файлов.
- Сообщите EMF, какие объекты создавать для модели (EMF EObjects или SDO EDataObjects)
- Загрузить экземпляр модели XML
Зачем беспокоиться?
Вы можете удивиться, зачем делать такую сложную вещь, как эта. Что ж, для нас ответ прост — мы хотим повторно использовать какой-то старый код, который использует SDO DataObjects, и его необходимо предоставлять через веб-сервисы. Простейший способ добиться этого без добавления других зависимостей, таких как Apache Tuscany или Websphere SCA Fix Pack, заключается в следующем. Мы запускаем его на Websphere, и поэтому EMF 2.2.1 находится в нашем распоряжении. Но, безусловно, есть и другие случаи, когда хотя бы часть этого подхода может быть полезной.
Вы также можете спросить, зачем использовать динамическую модель, которая менее эффективна, чем статическая (хотя доступ к отражению в EMF по-прежнему быстрее, чем в Java), и, конечно, гораздо менее читабельна и проста в использовании со странным eSet (функция EStructuralFeature, Object новое_значение). Ну, причина в гибкости — если ваша модель меняется, вам нужно только обновить ваши XSD (которые можно было бы скачать откуда-либо или сохранить в базе данных). Вам не нужно восстанавливать какие-либо классы и повторно развертывать приложение. Если вы знаете бюрократию крупных компаний, вы понимаете, что это может сэкономить вам недели или даже месяцы. Конечно, все зависит от плюсов и минусов.
Загрузите элементы модели, определенные в XSD, в EPackages
Прежде всего вам нужно создать динамическую модель EMF из XSD, что делается с помощью XSDEcoreBuilder :
import org.eclipse.xsd.ecore.XSDEcoreBuilder;
import org.eclipse.emf.ecore.EPackage;
...
public class EmfSdoModel {
...
private ResourceSet loadedModelResources = null;
/** Load EMF/SDO model from XSDs and set the this.loadedModelResources ResourceSet with the EPackages found. */
public void initModelFromXsd() {
final Collection<Object> loadedPackagesEtc = new XSDEcoreBuilder().generate(getSchemaUris());
final Collection<EPackage> eCorePackages = new LinkedList<EPackage>();
for (Object loadedObject : loadedPackagesEtc) {
if (loadedObject instanceof EPackage) {
eCorePackages.add((EPackage) loadedObject);
} else {
final String typeInfo = (loadedObject == null)?
"N/A" : loadedObject.getClass().getName();
LOG.info("initModelFromXsd: A non-EPackage in the input: " + typeInfo);
}
}
// TODO Fail if no packages found
this.loadedModelResources = registerDynamicPackages(eCorePackages);
}
...
}
Если вы заинтересованы в загрузке XSD из InputStream, посмотрите FAQ по EMF. Как я могу загрузить XSDSchema из простой строки Java или из дерева DOM? ,
Затем загруженные EPackages должны быть зарегистрированы в EMF под их URI пространства имен, чтобы он мог найти подходящий пакет при анализе XML (см. Мой предыдущий пост относительно объявлений пространства имен и EMF ). Но давайте сначала посмотрим, как ссылки на загружаемые XSD:
import org.eclipse.emf.common.util.URI;
...
private Collection<URI> getSchemaUris() {
final Collection<URI> result = new LinkedList<URI>();
for (String schemaOnCp : this.schemasOnClasspath) {
final URL xsdUrl = getClass().getResource(schemaOnCp); // fail if null
result.add(URI.createURI(
xsdUrl.toExternalForm()));
}
return result;
}
XSD находятся на пути к классам (в WEB-INF / classes /), а их пути, например «/xsd/AbstractBridgeMessage.xsd», преобразуются в абсолютные URL-адреса, а затем в URI-адреса EMF.
Работа с типами, определенными в WSDL
Если вы хотите использовать EMF для создания экземпляров модели на основе сообщения веб-службы, а некоторые типы — вероятно, «контейнерные» типы для запроса и ответа — определяются во встроенной схеме xsd: в ее файле WSDL, как показано ниже:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions ...>
<wsdl:types>
<xsd:schema targetNamespace="http://w3.ibm.com/xmlns/ibmww/hr/learning/lms/br/la"
xmlns:bons1="http://w3.ibm.com/xmlns/ibmww/hr/learning/lms/br"
xmlns:tns="http://w3.ibm.com/xmlns/ibmww/hr/learning/lms/br/la"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://w3.ibm.com/xmlns/ibmww/hr/learning/lms/br" schemaLocation="../xsd-includes/http.w3.ibm.com.xmlns.ibmww.hr.learning.lms.br.xsd"/>
<xsd:include schemaLocation="LearningActivityMessage.xsd"/>
<xsd:element name="updateLearningActivity">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="learningActivityMsg" nillable="true" type="tns:LearningActivityMessage"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="updateLearningActivityResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="result" nillable="true" type="bons1:TransactionResponseMessage"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
...
</wsdl:definitions>
затем один из способов сообщить EMF о них — это извлечь схему вручную в собственный XSD и объявить типы для элементов (единственное изменение — замена элемента xsd: на его вложенный xsd: complexType при сохранении название):
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://w3.ibm.com/xmlns/ibmww/hr/learning/lms/br/la"
xmlns:bons1="http://w3.ibm.com/xmlns/ibmww/hr/learning/lms/br"
xmlns:tns="http://w3.ibm.com/xmlns/ibmww/hr/learning/lms/br/la"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://w3.ibm.com/xmlns/ibmww/hr/learning/lms/br"
schemaLocation="../xsd-includes/http.w3.ibm.com.xmlns.ibmww.hr.learning.lms.br.xsd" />
<xsd:include schemaLocation="LearningActivityMessage.xsd" />
<!-- Originally xsd.elements turned to xsd:complexType nam -->
<xsd:complexType name="updateLearningActivity">
<xsd:sequence>
<xsd:element name="learningActivityMsg" nillable="true"
type="tns:LearningActivityMessage" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="updateLearningActivityResponse">
<xsd:sequence>
<xsd:element name="result" nillable="true"
type="bons1:TransactionResponseMessage" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Зарегистрируйте загруженные пакеты в реестре пакетов
Вышеупомянутый метод registerDynamicPackages создает ResourceSet и регистрирует импортированную динамическую модель EMF вместе с ним, чтобы его можно было использовать для загрузки экземпляров своей модели из XML (помните, что EMF должна быть в состоянии найти EPackage, соответствующий любому элементу XML, с которым она сталкивается, что является сделано через поиск в реестре):
private ResourceSet registerDynamicPackages( final Collection<EPackage> eCorePackages) { final ResourceSet resourceSet = new ResourceSetImpl(); // This is necessary when running standalone for no factories have been registered yet: resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "xml", new XMLResourceFactoryImpl()); for (EPackage ePackage: eCorePackages) { resourceSet.getPackageRegistry().put(ePackage.getNsURI(), ePackage); // or register globally: EPackage.Registry.INSTANCE.put(ePackage.getNsURI(), ePackage); // Create SDO's EDataObjects or EMF's EObjects or st. else? ePackage.setEFactoryInstance(createModelObjectFactory()); } return resourceSet; }
Метод createModelObjectFactory () описан в следующем разделе.
Важное замечание: Если у входного XML нет пространства имен, объявленного в корневом элементе, нам нужно зарегистрировать пакет, который будет использоваться для анализа XML (также) по умолчанию для пустого пространства имен, см. Мой предыдущий пост об EMF и объявлении пространства имен во входных XML-файлах. ,
Скажите EMF, какие объекты производить для модели
По умолчанию EMF 2.2 создает объекты EObject при импорте экземпляра модели, но мы можем заставить его создать, например, реализацию Commonj.sdo.DataObject для EMF SDO , в частности DynamicEDataObjectImpl , установив фабрику для каждого пакета EPackage:
import org.eclipse.emf.ecore.sdo.impl.DynamicEDataObjectImpl; ... private ResourceSet registerDynamicPackages(final Collection<EPackage> c) { ... ePackage.setEFactoryInstance(createModelObjectFactory()); ... } private FactoryImpl createModelObjectFactory() { return new DynamicEDataObjectImpl.FactoryImpl(); }
Загрузить экземпляр модели XML
Наконец, когда мы импортировали модель и подготовили ResourceSet для загрузки ее экземпляров из XML, мы можем сделать это:
public DataObject loadFromXml(final InputStream xmlStream) throws IOException {
final Resource resource = loadedModelResources.createResource(
URI.createURI("inputStream://dummyUriWithValidSuffix.xml")); // fake URI
resource.load(xmlStream, createXmlResourceDeSerializationOptions());
// May throw org.eclipse.emf.ecore.resource.Resource$IOWrappedException: Class 'myRootElement' not found.
// <= ecore.xmi.ClassNotFoundException: Class 'myRootElement' not found.
// if no EClass found for the root XML element given its name and namespace
LOG.info("Resource loaded:" + resource + ", contents:" + resource.getContents());
// => [DynamicEObjectImpl (eClass: EClassImpl(name: myRootElement) (instanceClassName: null) (abstract: false, interface: false))]
final EDataObject loadedEObject = (EDataObject) resource.getContents().get(0);
return loadedEObject;
}
Обратите внимание, что для загрузки XML из потока нам нужно создать поддельный URI с расширением, сопоставленным с требуемой фабрикой ресурсов (в данном случае — XML), и передать InputStream ( источник ).
Метод createXmlResourceDeSerializationOptions () устанавливает только параметры OPTION_EXTENDED_META_DATA и OPTION_ENCODING, как описано в моем предыдущем посте в разделе Общие замечания по сохранению / загрузке XML в EMF.
Собираем все вместе
Наконец, мы создадим веб-сервис, который преобразует входные данные XML в объект SDO. Я пропустил несвязанные строки и методы, вы можете найти их в моем предыдущем посте Создание веб-сервиса JAX-WS с использованием объектов служебных данных (SDO) вместо связанных с JAXB POJO . Соответствующий код:
@javax.xml.ws.ServiceMode(value=javax.xml.ws.Service.Mode.PAYLOAD)
@javax.xml.ws.WebServiceProvider(...)
public class MyRawXmlServiceImpl implements Provider<Source> {
...
private EmfSdoModel emfSdoModel;
@javax.annotation.PostConstruct
public void initializeEmfModel() {
emfSdoModel = new EmfSdoModel();
emfSdoModel.initModelFromXsd();
}
public Source invoke(final Source request) {
final String requestXml = convertRequestToXml(request);
DataObject requestSDO;
try {
final InputStream xmlStream = new ByteArrayInputStream(
requestXml.getBytes("UTF-8"));
requestSDO = emfSdoModel.loadFromXml(xmlStream);
} catch (IOException e) {
throw new RuntimeException("XML->SDO covnersion failed: " + e, e);
}
final DataObject responseSDO = sdoInstance_.updateLearningActivity(requestSDO);
return convertResponse(responseSDO);
}
...
}
По сути, мы просто загружаем модель при запуске, а затем используем ее для анализа XML.
При запуске этого кода в Websphere Application Server 7.0 вам не нужны дополнительные библиотеки. При работе в другой среде, проверьте библиотеки, необходимые в EMF FAQ .
Резюме
Я продемонстрировал, как создать динамическую модель EMF на основе XSD в веб-приложении и как использовать эту модель для анализа XML-файлов в SDO DataObjects или EMF EObjects, а также как интегрировать ее с веб-сервисом JAX-WS.
Ресурсы
- Статья Создание метамоделей с помощью динамической EMF — Создание динамических моделей на основе Ecore по запросу без создания классов реализации Java (2007) — Создание EClasses, добавление к ним EAttributes в виде EStructuralFeatures, создание EPackage и добавление туда классов)
- Как создать динамический объект DataObject с атрибутом карты e-enabled из форума EclipseZone — я не использовал его, но кому-то это может понадобиться
- Как создать динамическую модель SDO из статьи dW Введение в объекты данных службы
- EMF wiki: EMF / Создание динамического экора из XML-схемы
- Мои похожие посты в блоге: