Валидация XML файлов в Atlassian Stash
Date February 4th, 2014 Author Vitaly Agapov
Я уже касался как-то темы написания хука для репозитория в Atlassian Stash. На этот раз появилась необходимость проверять валидность синтаксиса XML-файлов, которые коммитят в репозиторий. То есть задача состоит в том, чтобы в каждый момент времени обеспечить в репозитории набор валидных XML-файлов. Сама задача разбивается на две логические подзадачи: 1. получить содержимое изменённых файлов; 2. Произвести непосредственно проверку синтаксиса.
Более или менее рабочий пример можно посмотреть у меня в GitHub. А тут расскажу про решение подзадач.
Доступ к телу файла в Stash
Вообще для доступа к содержимому файлов в API Stash есть интерфейс ContentService, который в связке с HistoryService может вроде как сделать всё, что нам нужно. Но я к сожалению так и не смог его побороть и заставить делать то, что мне нужно. Сказывается куцая документация и отсутствие хороших примеров.
Но зато в том же API есть интерфейс GitScm для прямого вызова команд Git из плагина. Для его использования нужно добавить в pom.xml новую зависимость:
<dependency> <groupId>com.atlassian.stash</groupId> <artifactId>stash-scm-git-api</artifactId> <scope>provided</scope> <version>${stash.version}</version> </dependency>
Теперь в классе PreReceiveRepositoryHook можно будет импортировать:
import com.atlassian.stash.scm.git.GitCommandBuilderFactory; import com.atlassian.stash.scm.git.GitScm; import com.atlassian.stash.scm.git.GitScmCommandBuilder;
и создать объект private final GitScm gitScm и инициализировать его в конструкторе.
Теперь с его помощью можно, например, получить список всех файлов, которые изменились в текущих коммитах:
for (RefChange refChange : refChanges) { String result = gitScm.getCommandBuilderFactory().builder(context.getRepository()) .command("diff") .argument("--name-only") .argument(refChange.getFromHash()) .argument(refChange.getToHash()) .build(new StringCommandOutputHandler()) .call(); }
После этого полученный список можно разбить на имена файлов, выбрать из них те, которые имеют расширение .xml, и получить содержимое файла в последнем коммите.
String[] files = result.split("\n"); for (String file : files) { if (file.matches(".+\\.xml$")) { String result = gitScm.getCommandBuilderFactory().builder(context.getRepository()) .command("show") .argument(refChange.getToHash() + ":" + file) .build(new StringCommandOutputHandler()) .call(); } }
Для работы ещё потребуется реализовать класс StringCommandOutputHandler. Самое простое – это реализация класса CommandOutputHandler и наследник StringOutputHandler:
package com.atlassian.stash.plugin; import com.atlassian.stash.scm.CommandOutputHandler; import com.atlassian.utils.process.StringOutputHandler; public class StringCommandOutputHandler extends StringOutputHandler implements CommandOutputHandler<String> { }
Правда, если запустить Stash и включить сейчас этот хук, то при его вызове свалится ошибка:
Unsatisfied dependency expressed through constructor argument with index 0 of type [com.atlassian.stash.scm.git.GitScm]: : No unique bean of type [com.atlassian.stash.scm.git.GitScm] is defined: Unsatisfied dependency of type [interface com.atlassian.stash.scm.git.GitScm]: expected at least 1 matching bean; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.atlassian.stash.scm.git.GitScm] is defined: Unsatisfied dependency of type [interface com.atlassian.stash.scm.git.GitScm]: expected at least 1 matching bean
Это из-за того, что GitScm не является частью ядра Stash и Maven-зависимости недостаточно. Нужно добавить этот интерфейс как компонент в atlassian-plugin.xml:
<component-import key="gitScm" interface="com.atlassian.stash.scm.git.GitScm" />
Половина дела сделана – текст файла есть у нас в String переменной. Следующий шаг:
Проверка синтаксиса XML
Проверять будем с помощью javax.xml.parsers.DocumentBuilder и javax.xml.parsers.DocumentBuilderFactory. В общем, выглядит решение так:
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import java.io.StringReader; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; ... try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(new InputSource(new StringReader(result))); } catch (ParserConfigurationException e) { hookResponse.out().println(e); return false; } catch (SAXException e) { hookResponse.out().println(file + " XML bad syntax: " + e); return false; } catch (IOException e) { hookResponse.out().println(e); return false; } return true;
Tags: Atlassian, Java
Category:
Java |
No comments »