Статьи

Создание динамических моделей EMF из XSD и загрузка их экземпляров из XML в виде SDO

В этом посте описывается, как читать динамическую модель EMF из набора файлов XML-схем (XSD) и как использовать эту модель для преобразования XML в SDO DataObjects или EMF EObjects, и все это в автономной среде.

Небольшое напоминание об EMF : с EMF вы сначала объявляете модель, например, на основе UML или XSD (см. Мой предыдущий пост для общего краткого введения EMF ). Модель может быть либо статической, в этом случае класс Java создается для каждой модели EClass с обычными получателями и установщиками, либо она может быть динамической, которая не требует генерации кода, а атрибуты доступны только через общий eGet, eSet методы. Затем вы можете создавать, загружать и сохранять экземпляры модели, например, из / в XML.

Чтобы перейти из XSD в XML, преобразованный в объект E [Data] времени выполнения, вам необходимо:

  1. Загрузите элементы модели, определенные в XSD, в EPackages
  2. Зарегистрируйте загруженные пакеты либо в глобальном реестре пакетов, либо в реестре пакетов ResourceSet, который будет использоваться для загрузки XML-файлов.
  3. Сообщите EMF, какие объекты создавать для модели (EMF EObjects или SDO EDataObjects)
  4. Загрузить экземпляр модели 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.

Ресурсы

 

От http://theholyjava.wordpress.com/2011/01/03/creating-dynamic-emf-model-from-xsds-and-loading-its-instances-from-xml-as-sdos/