Category Archive: Веб-сервисы

Встраивание атрибута Id в тег Body SOAP-запроса

С некоторой версии Java (примерно с 7u25 см. http://stackoverflow.com/questions/17331187/xml-dig-sig-error-after-upgrade-to-java7u25) начались проблемы со встаиванием атрибута Id в тег Body SOAP-запроса. Такое встраивание необходимо с целью подписания запроса ЭЦП. Пока я встретил две ошибки в зависимости каким путем идет встраивание атрибута Id:
  1. com.sun.org.apache.xml.internal.security.utils.resolver.implementations.ResolverFragment
  2. org.apache.xml.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID ...
Чтобы решить проблему нужно специальным образом пометить атрибут Id как ID (простите за тавтологию) Первая ошибка возникает в коде такого вида:
Attr attr = doc.createAttribute("id");
attr.setValue("body");
body.getAttributes().setNamedItem(attr);
Подкорректированная версия:
Attr attr = doc.createAttribute("id");
attr.setValue("body");
body.getAttributes().setNamedItem(attr);
((Element) body).setIdAttributeNode(attr, true);//fix
Вторая ошибка возникает в коде вида:
SOAPBody body = env.getBody();
body.addAttribute(new QName("Id"), id);
Подкорректированная версия:
SOAPBody body = env.getBody();
body.addAttribute(new QName("Id"), id);
body.setIdAttribute("Id", true);//fix
Если помог отпишитесь 🙂

Набор методов для маршалинга и анмаршалинга

При создании и обращении к веб-сервисам в большинстве случаем мы можем забыть и не знать о маршалинге и анмаршалинге (демаршалинге). Но бывают интеграции, где каким-то текстовым полем передается XML-ка. Вот тут нужно с одной стороны уметь делать маршалинг, а с другой анмаршалинг. Предлагаю набор методов для проведения данных операций:
import javax.xml.bind.*;
import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;

/**
 * Набор методов для маршалинга и анмаршалинга
 */
public class XmlUtils {

	/**
	 * Маршалинг в строку
	 * @param t объект
	 * @param  тип объекта
	 * @return XML в виде строки
	 * @throws JAXBException
	 */
	public static String marshalRoot(T t) throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(t.getClass());
		Marshaller marshaller = context.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
		marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
		StringWriter sw = new StringWriter();
		marshaller.marshal(t,sw);
		return sw.toString();
	}

	/**
	 * Маршалинг в файл
	 * @param t объект
	 * @param file файл
	 */
	public static void marshalRoot(T t,File file){
		marshal(t,file,false);
	}

	/**
	 * Маршалинг в файл с возможностью вывода в консоль
	 * @param t объект
	 * @param file файл
	 * @param isAddToLog делать вывод в консоль или нет
	 */
	public static void marshal(T t,File file,boolean isAddToLog){
		String filePath = file.getAbsolutePath();
		try {
			JAXBContext jaxbContext = JAXBContext.newInstance(t.getClass());
			Marshaller marshaller = jaxbContext.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
			marshaller.marshal(t,file);
			if(isAddToLog){
				marshaller.marshal(t,System.out);
			}
		} catch (JAXBException e) {
			System.out.println("Не удалось сбросить xml в файл: " + filePath);
			e.printStackTrace();
		}
		System.out.println("Xml saved to: " + filePath);
	}

	/**
	 * Анмаршалинг (Демаршалинг) XML в объект
	 * @param xml входной XML
	 * @param _class класс объекта, который ходит получить
	 * @return объект
	 * @throws JAXBException
	 */
	@SuppressWarnings("unchecked")
	public static  T unmarshall(String xml,Class _class) throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(_class);
		Unmarshaller unmarshaller = context.createUnmarshaller();
		JAXBElement element = (JAXBElement)unmarshaller.unmarshal(new StringReader(xml));
		return element.getValue();
	}

	/**
	 * Анмаршалинг XML root-элемента (@XmlRootElement) в объект
	 * @param xml входной XML
	 * @param _class класс объекта, который ходит получить
	 * @return объект
	 * @throws JAXBException
	 */
	@SuppressWarnings("unchecked")
	public static  T unmarshallRoot(String xml,Class _class) throws JAXBException{
		JAXBContext context = JAXBContext.newInstance(_class);
		Unmarshaller unmarshaller = context.createUnmarshaller();
		return (T)unmarshaller.unmarshal(new StringReader(xml));
	}


}

Генерация Java-классов Web-сервиса по WSDL

Допустим, у вас есть WSDL и вы хотите написать либо сам веб-сервис, либо клиента для сервиса. Для этого по WSDL нужно сгенерировать Java-классы с помощью утилиты wsimport, входящую в состав JDK. Стоит отметить, что сгенерированный код может использовать и на сервере, и на клиенте. Создайте новую папку и положите в нее вашу WSDL и всё, что к ней относится (это может быть другие WSDL или XSD). Увидеть все, что импортирует ваша основная WSDL можно в теге import. Например:

В данном примере следует заменить интернет адрес XSD на локальный:

Разумеется, файл example.xsd должен лежать c WSDL в одной директории. Теперь напишем скрипт для генерирования классов. Рассмотрим сперва Windows-версию скрипта, а потом Linux-версию. Скрипт для Windows Создайте файл w-gen.bat в любом текстовом редакторе и скопируйте в него следующий код:
cls
set GEN_DIR=kz
rmdir %GEN_DIR% /s/q
"%JAVA_HOME%/bin/wsimport" -keep -Xnocompile -p kz.kesh.blog.v1 myBlog.wsdl
pause
cls - очистка консоли GEN_DIR - директория пакета верхнего уровня rmdir - полное удаление директории wsimport - утилита для генерации Java-классов из WSDL keep - сохраняет сгенерированные файлы Xnocompile - не компилирует сгенерированные Java-файлы kz.kesh.blog.v1 - имя пакета, в котором будет сгенерированые классы myBlog.wsdl - наша WSDL pause - пауза, чтобы консольное окно сразу не закрылось Данный скрипт генерирует *.java файлы, но вы можете немного переработать скрипт и генерировать скопилированные файлы *.class. Так же вы можете доработать упаковку файлов и WSDL в jar. Например:
"%JAVA_HOME%/bin/jar" cf keskBlog.jar kz
jar - стандартная утилита из JDK для создания jar-файлов cf - создает новый архив с указанным именем keskBlog.jar - имя вашего jar-файла kz - директория, которая упаковывается в jar, она соответствует названию верхнего пакета Скрипт для Linux Создайте файл w-gen.sh:
#!/bin/bash
GEN_DIR="kz"
rm -r $GEN_DIR
wsimport -keep -Xnocompile -p kz.kesh.blog.v1 myBlog.wsdl
Не забудьте дать скрипту права на выполенение:
chmod +x w-gen.sh
#!/bin/bash - обязательная строчка для bash-скриптов GEN_DIR - директория пакета верхнего уровня rm - рекурсивное удаление директории wsimport - утилита для генерации Java-классов из WSDL keep - сохраняет сгенерированные файлы Xnocompile - не компилирует сгенерированные Java-файлы kz.kesh.blog.v1 - имя пакета, в котором будет сгенерированы классы myBlog.wsdl - наша WSDL