From 16bb8eb723f703c26e68bbda43f4340072c88f96 Mon Sep 17 00:00:00 2001 From: Mikhail Trofimov Date: Wed, 25 Jun 2025 17:16:00 +0300 Subject: [PATCH] Removed FailureAnswerGenerator and SuccessAnswerGenerator; Added staxReader for exceptions handling from nbki service; Minor refactoring --- .../response/external_errors/Error.java | 23 +++ .../response/external_errors/InputParams.java | 21 ++ .../response/external_errors/SgnlReport.java | 21 ++ .../credit_tracker/facade/SignalFacade.java | 17 -- .../service/MonitoringService.java | 4 + .../service/signal/SignalRestService.java | 195 ++++++++++-------- .../service/xml/FailureAnswerGenerator.java | 53 ----- .../service/xml/SuccessAnswerGenerator.java | 41 ---- .../service/xml/jaxb/XmlUtilService.java | 14 ++ .../service/xml/stax/BaseStaxReader.java | 77 +++++++ .../xml/stax/SgnlReportStatusStaxReader.java | 42 ++++ .../service/xml/stax/StaxReader.java | 9 + .../service/xml/stax/StaxXmlProcessor.java | 6 + .../xml/stax/XmlProcessorStaxImpl.java | 69 +++++++ 14 files changed, 397 insertions(+), 195 deletions(-) create mode 100644 src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/Error.java create mode 100644 src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/InputParams.java create mode 100644 src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/SgnlReport.java delete mode 100644 src/main/java/ru/nbch/credit_tracker/service/xml/FailureAnswerGenerator.java delete mode 100644 src/main/java/ru/nbch/credit_tracker/service/xml/SuccessAnswerGenerator.java create mode 100644 src/main/java/ru/nbch/credit_tracker/service/xml/stax/BaseStaxReader.java create mode 100644 src/main/java/ru/nbch/credit_tracker/service/xml/stax/SgnlReportStatusStaxReader.java create mode 100644 src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxReader.java create mode 100644 src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxXmlProcessor.java create mode 100644 src/main/java/ru/nbch/credit_tracker/service/xml/stax/XmlProcessorStaxImpl.java diff --git a/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/Error.java b/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/Error.java new file mode 100644 index 0000000..7d8aec2 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/Error.java @@ -0,0 +1,23 @@ +package ru.nbch.credit_tracker.entities.response.external_errors; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlType; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "name", + "description" +}) +public class Error { + + @XmlElement(name = "name", required = true) + protected String name; + @XmlElement(name = "description", required = true) + protected String description; +} diff --git a/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/InputParams.java b/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/InputParams.java new file mode 100644 index 0000000..c65a4c5 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/InputParams.java @@ -0,0 +1,21 @@ +package ru.nbch.credit_tracker.entities.response.external_errors; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlType; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "memberCode", + "packCode" +}) +public class InputParams { + + @XmlElement(name = "memberCode", required = true) + protected String memberCode; +} diff --git a/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/SgnlReport.java b/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/SgnlReport.java new file mode 100644 index 0000000..1c0802a --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/entities/response/external_errors/SgnlReport.java @@ -0,0 +1,21 @@ +package ru.nbch.credit_tracker.entities.response.external_errors; + +import jakarta.xml.bind.annotation.*; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "inputParams", + "error" +}) +@XmlRootElement(name = "SgnlReport") +public class SgnlReport { + + @XmlElement(name = "InputParams", required = true) + protected InputParams inputParams; + @XmlElement(name = "Error", required = true) + protected Error error; +} diff --git a/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java b/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java index 3202522..73762e8 100644 --- a/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java +++ b/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java @@ -24,8 +24,6 @@ import ru.nbch.credit_tracker.service.SignalService; import ru.nbch.credit_tracker.service.indic.PhoneService; import ru.nbch.credit_tracker.service.scoring.AuthorizationService; import ru.nbch.credit_tracker.service.signal.SignalRestService; -import ru.nbch.credit_tracker.service.xml.FailureAnswerGenerator; -import ru.nbch.credit_tracker.service.xml.SuccessAnswerGenerator; import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor; import ru.nbch.credit_tracker.service.xml.jaxb.XmlUtilService; import ru.nbch.credit_tracker.utils.CTUtil; @@ -48,8 +46,6 @@ public class SignalFacade { private final AuthorizationService authorizationService; private final SignalService signalService; private final SignalRestService signalRestService; - private final FailureAnswerGenerator failureAnswerGenerator; - private final SuccessAnswerGenerator successAnswerGenerator; private final XmlUtilService xmlUtilService; private final ExecutorService executor; @@ -59,16 +55,12 @@ public class SignalFacade { AuthorizationService authorizationService, SignalService signalService, SignalRestService signalRestService, - FailureAnswerGenerator failureAnswerGenerator, - SuccessAnswerGenerator successAnswerGenerator, XmlUtilService xmlUtilService, @Qualifier("executorService") ExecutorService executor) { this.config = config; this.authorizationService = authorizationService; this.signalService = signalService; this.signalRestService = signalRestService; - this.failureAnswerGenerator = failureAnswerGenerator; - this.successAnswerGenerator = successAnswerGenerator; this.xmlUtilService = xmlUtilService; this.executor = executor; } @@ -256,15 +248,6 @@ public class SignalFacade { file.getOriginalFilename() != null && file.getOriginalFilename().toLowerCase().endsWith(".zip"); } - private ResponseEntity getBadResponse(SignalError error, String tag) { - Resource answer = failureAnswerGenerator.generateFailureAnswer(error, tag); - return ResponseEntity.badRequest().body(answer); - } - - private ResponseEntity getBadResponse(SignalError error) { - return getBadResponse(error, null); - } - public ResponseEntity findActivePackages(MonitoringActivePackagesRequest activePackagesRequest) { try { AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(activePackagesRequest.getUserId(), activePackagesRequest.getPassword()); diff --git a/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java b/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java index d4665a9..4d7b7d9 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java @@ -60,6 +60,10 @@ public class MonitoringService { for (PackageData packageData : packages) { SgnlOnlineReport onlineReports = signalRestService.getReports(packageData.getUserId().substring(0, 6) + "_" + packageData.getPackageName()); + if (onlineReports == null) { + continue; + } + List fileNames = onlineReports.getOperationReport().getReports().getReports().stream().map(Report::getFilename) .filter(fileName -> CTUtil.parseDateFromReportFileName(fileName) != null).toList(); // TODO оптимизировать diff --git a/src/main/java/ru/nbch/credit_tracker/service/signal/SignalRestService.java b/src/main/java/ru/nbch/credit_tracker/service/signal/SignalRestService.java index 20378a4..33a0cb8 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/signal/SignalRestService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/signal/SignalRestService.java @@ -11,18 +11,18 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; +import ru.nbch.credit_tracker.entities.response.external_errors.SgnlReport; import ru.nbch.credit_tracker.entities.response.online_report.SgnlOnlineReport; import ru.nbch.credit_tracker.entities.signal_package.Signals; import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor; import ru.nbch.credit_tracker.service.xml.jaxb.XmlUtilService; +import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor; import java.io.ByteArrayOutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.zip.GZIPOutputStream; @Slf4j @@ -64,13 +64,116 @@ public class SignalRestService { // good response return true; } else if (response.getStatusCode().value() == 200 && response.getBody() != null) { - // bad response - log.error("Ошибка при постановке пакета на мониторинг в официальный сервис сигналов: {}", response.getBody()); + StaxXmlProcessor staxXmlProcessor = xmlUtilService.staxXmlProcessor(); + SgnlReport error = staxXmlProcessor.read(response.getBody(), SgnlReport.class); + if (error.getError().getDescription() != null) { + log.error("Ошибка при постановке пакета на мониторинг в официальный сервис сигналов: {}", error.getError().getDescription()); + } else { + log.error("неизвестная ошибка при постановке пакета на мониторинг в официальный сервис сигналов: {}", response.getBody()); + } return false; } return false; } + public void deletePackage(String username, String packageName) { + String packCode = username.substring(0, 6) + "_" + packageName; + String url = DELETE_URI + "?packCode=" + packCode; + HttpHeaders headers = new HttpHeaders(); + headers.setBasicAuth(authToken); + + HttpEntity> requestEntity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + requestEntity, + String.class + ); + String answer = response.getBody(); + + StaxXmlProcessor staxXmlProcessor = xmlUtilService.staxXmlProcessor(); + SgnlReport error = staxXmlProcessor.read(answer, SgnlReport.class); + if (error.getError().getDescription() != null) { + log.error("Ошибка при удалении пакета из сервиса НБКИ. packCode={}. Описание: {}", packCode, error.getError().getDescription()); + } + } + + public SgnlOnlineReport getReports(String packCode) { + String url = GET_REPORTS_URI + "?packCode=" + packCode; + + HttpHeaders headers = new HttpHeaders(); + headers.setBasicAuth(authToken); + + HttpEntity> requestEntity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + requestEntity, + String.class + ); + + String answer = response.getBody(); + + StaxXmlProcessor staxXmlProcessor = xmlUtilService.staxXmlProcessor(); + SgnlReport error = staxXmlProcessor.read(answer, SgnlReport.class); + if (error.getError().getDescription() != null) { + log.error("Ошибка при получении пакета из сервиса НБКИ. packCode={}. Описание: {}", packCode, error.getError().getDescription()); + return null; + } + try { + IXmlProcessor processor = xmlUtilService.createJaxbProcessor(); + return processor.read(answer, SgnlOnlineReport.class); + } catch (Exception e) { + log.error("Ошибка при получении пакета из сервиса НБКИ. Код ошибки: {} packCode={}", response.getStatusCode().value(), packCode); + return null; + } + } + + public byte[] downloadReport(String reportName) { + String url = DOWNLOAD_REPORT_URI + "?fNameReport=" + reportName; + + HttpHeaders headers = new HttpHeaders(); + headers.setBasicAuth(authToken); + + HttpEntity> requestEntity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + requestEntity, + byte[].class + ); + + byte[] answer = response.getBody(); + if (isXmlError(response.getBody())) { + StaxXmlProcessor staxXmlProcessor = xmlUtilService.staxXmlProcessor(); + SgnlReport error = staxXmlProcessor.read(answer, SgnlReport.class); + if (error.getError().getDescription() != null) { + log.error("Ошибка при скачивание отчета. fNameReport={}. Описание: {}", reportName, error.getError().getDescription()); + return null; + } + log.error("Неизвестная ошибка при скачивание отчета. fNameReport={}", reportName); + return null; + } + + return answer; + } + + private String buildToken(String signalsToken, String signalsuser) throws Exception { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest((signalsuser + "|" + signalsToken).getBytes(StandardCharsets.UTF_8)); + String hash = DatatypeConverter.printHexBinary(messageDigest).toLowerCase(); + Base64.Encoder encoder = Base64.getEncoder(); + return encoder.encodeToString((signalsuser + ":" + hash).getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + log.error("Error building token", e); + throw new Exception("Error building token", e); + } + } + private Resource compressFile(Signals signals) throws Exception { IXmlProcessor processor = xmlUtilService.createJaxbProcessor(); String requestData = processor.asString(signals, false); @@ -88,85 +191,9 @@ public class SignalRestService { }; } - public void deletePackage(String username, String packageName) { - String packCode = username.substring(0, 6) + "_" + packageName; - String url = DELETE_URI + "?packCode=" + packCode; - HttpHeaders headers = new HttpHeaders(); - headers.setBasicAuth(authToken); - - HttpEntity> requestEntity = new HttpEntity<>(headers); - - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.GET, - requestEntity, - String.class - ); - String answer = response.getBody(); - if (response.getStatusCode().value() == 200 && answer != null && answer.contains("")) { - if (answer.contains("")) { - Pattern pattern = Pattern.compile("(.*?)"); - Matcher matcher = pattern.matcher(answer); - if (matcher.find()) { - String description = matcher.group(1); - log.error("Ошибка при удалении пакета из сервиса НБКИ. packCode={}. Описание: {}", packCode, description); - } - } else { - log.error("Ошибка при удалении пакета из сервиса НБКИ. packCode={}", packCode); - } - } else if (response.getStatusCode().value() != 200) { - log.error("Ошибка при удалении пакета из сервиса НБКИ. Код ошибки: {} packCode={}", response.getStatusCode().value(), packCode); - } - } - - public SgnlOnlineReport getReports(String packCode) { - String url = GET_REPORTS_URI + "?packCode=" + packCode; - - HttpHeaders headers = new HttpHeaders(); - headers.setBasicAuth(authToken); - - HttpEntity> requestEntity = new HttpEntity<>(headers); - - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.GET, - requestEntity, - SgnlOnlineReport.class - ); - - // TODO обработка ошибок - - return response.getBody(); - } - - public byte[] downloadReport(String reportName) { - String url = DOWNLOAD_REPORT_URI + "?fNameReport=" + reportName; - - HttpHeaders headers = new HttpHeaders(); - headers.setBasicAuth(authToken); - - HttpEntity> requestEntity = new HttpEntity<>(headers); - - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.GET, - requestEntity, - byte[].class - ); - - return response.getBody(); - } - - private String buildToken(String signalsToken, String signalsuser) throws Exception { - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] messageDigest = md.digest((signalsuser + "|" + signalsToken).getBytes(StandardCharsets.UTF_8)); - String hash = DatatypeConverter.printHexBinary(messageDigest).toLowerCase(); - Base64.Encoder encoder = Base64.getEncoder(); - return encoder.encodeToString((signalsuser + ":" + hash).getBytes(StandardCharsets.UTF_8)); - } catch (Exception e) { - log.error("Error building token", e); - throw new Exception("Error building token", e); - } + private boolean isXmlError(byte[] data) { + if (data == null || data.length < 10) return false; + String start = new String(data, 0, 10).trim(); + return start.startsWith("> staxReader = new ArrayList<>(); + staxReader.add(new SgnlReportStatusStaxReader()); + return new XmlProcessorStaxImpl(staxReader); + } + public void validateXMLByXSD(InputStream xmlStream, String xsdPath) throws IOException, XMLStreamException, SAXException { SchemaFactory sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); Schema schema = sf.newSchema(new File(xsdPath)); diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/BaseStaxReader.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/BaseStaxReader.java new file mode 100644 index 0000000..3e4d6ac --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/BaseStaxReader.java @@ -0,0 +1,77 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; + +public abstract class BaseStaxReader implements StaxReader { + private final Deque stack = new ArrayDeque<>(); + protected final StringBuilder buffer = new StringBuilder(); + protected abstract T createObject(); + + @Override + public T read(XMLStreamReader reader) throws XMLStreamException { + T obj = createObject(); + while (reader.hasNext()) { + boolean breakExecution = false; + int ev = reader.next(); + switch (ev) { + case XMLStreamConstants.START_ELEMENT -> { + buffer.setLength(0); + startElement(reader.getLocalName()); + } + case XMLStreamConstants.CHARACTERS -> { + String text = reader.getText().trim(); + if (!text.isEmpty()) buffer.append(text); + } + case XMLStreamConstants.END_ELEMENT -> { + breakExecution = innerProcess(reader, obj); + endElement(); + } + default -> {} + } + if (breakExecution) break; + } + return obj; + } + + protected abstract boolean innerProcess(XMLStreamReader reader, T obj) throws XMLStreamException; + + + public void startElement(String localName) { + localName = substringAfterColon(localName); + stack.push(localName); + } + + public void endElement() { + if (!stack.isEmpty()) { + stack.pop(); + } + } + + public String getPath() { + StringBuilder sb = new StringBuilder(); + for (Iterator it = stack.descendingIterator(); it.hasNext(); ) { + String name = it.next(); + sb.append('/').append(name); + } + return sb.toString(); + } + + public static String substringAfterColon(String input) { + if (input == null) { + return null; + } + int idx = input.indexOf(':'); + if (idx == -1) { + return input; + } + if (idx == input.length() - 1) { + return ""; + } + return input.substring(idx + 1); + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/SgnlReportStatusStaxReader.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/SgnlReportStatusStaxReader.java new file mode 100644 index 0000000..17f4cb5 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/SgnlReportStatusStaxReader.java @@ -0,0 +1,42 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +import org.springframework.stereotype.Component; +import ru.nbch.credit_tracker.entities.response.external_errors.Error; +import ru.nbch.credit_tracker.entities.response.external_errors.InputParams; +import ru.nbch.credit_tracker.entities.response.external_errors.SgnlReport; + +import javax.xml.stream.XMLStreamReader; + +@Component +public class SgnlReportStatusStaxReader extends BaseStaxReader { + + @Override + public Class type() { + return SgnlReport.class; + } + + @Override + protected SgnlReport createObject() { + SgnlReport report = new SgnlReport(); + InputParams inputParams = new InputParams(); + report.setInputParams(inputParams); + Error error = new Error(); + report.setError(error); + return report; + } + + @Override + protected boolean innerProcess(XMLStreamReader reader, SgnlReport obj) { + String text = buffer.toString(); + String currPath = getPath(); + switch (currPath) { + case "/SgnlReport/InputParams/memberCode" -> + obj.getInputParams().setMemberCode(text); + case "/SgnlReport/Error/name" -> + obj.getError().setName(text); + case "/SgnlReport/Error/description" -> + obj.getError().setDescription(text); + } + return false; + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxReader.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxReader.java new file mode 100644 index 0000000..d594e9b --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxReader.java @@ -0,0 +1,9 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +public interface StaxReader { + Class type(); + T read(XMLStreamReader xmlStreamReader) throws XMLStreamException; +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxXmlProcessor.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxXmlProcessor.java new file mode 100644 index 0000000..762e54b --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxXmlProcessor.java @@ -0,0 +1,6 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +public interface StaxXmlProcessor { + Z read(String xml, Class clazz); + Z read(byte[] xml, Class clazz); +} \ No newline at end of file diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/XmlProcessorStaxImpl.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/XmlProcessorStaxImpl.java new file mode 100644 index 0000000..4041eab --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/XmlProcessorStaxImpl.java @@ -0,0 +1,69 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.ByteArrayInputStream; +import java.io.StringReader; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class XmlProcessorStaxImpl implements StaxXmlProcessor { + private final Logger log = LoggerFactory.getLogger(getClass()); + protected XMLInputFactory xmlInputFactory; + protected Map, StaxReader> staxReaders = new HashMap<>(); + + public XmlProcessorStaxImpl(List> staxReaders) { + this.xmlInputFactory = XMLInputFactory.newInstance(); + this.xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); // полностью выключить DOCTYPE Declaration + // или выключить особо опасные возможности (менее строгое ограничение XXE, но достаточное): + this.xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + this.xmlInputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false); + this.xmlInputFactory.setXMLResolver((publicID, systemID, baseURI, namespace) -> { + throw new XMLStreamException("External entities are disabled"); + }); + staxReaders.forEach(reader -> this.staxReaders.put(reader.type(), reader)); + } + + public XmlProcessorStaxImpl(StaxReader... staxReaders) { + this(Arrays.asList(staxReaders)); + } + + @Override + public Z read(String xml, Class clazz) { + try { + XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new StringReader(xml)); + @SuppressWarnings("unchecked") + StaxReader staxProcessor = (StaxReader) staxReaders.get(clazz); + if (staxProcessor != null) { + return staxProcessor.read(xmlStreamReader); + } + throw new IllegalStateException("no stax reader registered for " + clazz); + } catch (Exception e) { + log.error(ExceptionUtils.getStackTrace(e)); + throw new RuntimeException(e); + } + } + + @Override + public Z read(byte[] xml, Class clazz) { + try { + XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(xml)); + @SuppressWarnings("unchecked") + StaxReader staxProcessor = (StaxReader) staxReaders.get(clazz); + if (staxProcessor != null) { + return staxProcessor.read(xmlStreamReader); + } + throw new IllegalStateException("no stax reader registered for " + clazz); + } catch (Exception e) { + log.error(ExceptionUtils.getStackTrace(e)); + throw new RuntimeException(e); + } + } +}