diff --git a/src/main/java/ru/nbch/credit_tracker/config/xml/XmlConfig.java b/src/main/java/ru/nbch/credit_tracker/config/xml/XmlConfig.java index ae12113..8331a83 100644 --- a/src/main/java/ru/nbch/credit_tracker/config/xml/XmlConfig.java +++ b/src/main/java/ru/nbch/credit_tracker/config/xml/XmlConfig.java @@ -8,7 +8,6 @@ import ru.nbch.credit_tracker.entities.response.monitoring_signal_response.Monit import ru.nbch.credit_tracker.entities.response.online_download.OnlineDownloadReport; import ru.nbch.credit_tracker.entities.response.online_report.SgnlOnlineReport; import ru.nbch.credit_tracker.entities.response.success.MonitoringResponse; -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.XmlProcessorImplAnyClasses; import ru.nbch.credit_tracker.service.xml.stax.*; @@ -27,7 +26,6 @@ public class XmlConfig { OnlineDownloadReport.class, SgnlOnlineReport.class, // marshalling classes - Signals.class, MonitoringError.class, MonitoringResponse.class, MonitoringSignalResponse.class); 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 b53ee2b..845c287 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 @@ -15,10 +15,10 @@ 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.stax.SignalsStaxXmlWriter; 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; @@ -172,11 +172,11 @@ public class SignalRestService { } private Resource compressFile(Signals signals) throws Exception { - String requestData = xmlProcessor.asString(signals, false); + SignalsStaxXmlWriter signalsStaxXmlWriter = new SignalsStaxXmlWriter("windows-1251"); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); try (GZIPOutputStream gzipStream = new GZIPOutputStream(byteStream)) { - gzipStream.write(requestData.getBytes(Charset.forName("windows-1251"))); + signalsStaxXmlWriter.writeWithStax(gzipStream, signals, false); } return new ByteArrayResource(byteStream.toByteArray()) { diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/IndentingXMLEventWriter.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/IndentingXMLEventWriter.java new file mode 100644 index 0000000..f6f6af8 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/IndentingXMLEventWriter.java @@ -0,0 +1,255 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Characters; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; +import java.io.IOException; +import java.io.Writer; + +public class IndentingXMLEventWriter implements XMLEventWriter { + private final XMLEventWriter core; + private String indent = " "; + private final Characters indentEvent = new CharactersImpl() { + public String getData() { + return IndentingXMLEventWriter.this.indent; + } + }; + private String newLine; + private final Characters newLineEvent = new CharactersImpl() { + public String getData() { + return IndentingXMLEventWriter.this.newLine; + } + }; + private int depth = 0; + private boolean hasText; + private boolean hasEndTag; + + public IndentingXMLEventWriter(XMLEventWriter core) { + if (core == null) { + throw new IllegalArgumentException(); + } else { + this.core = core; + + try { + this.newLine = System.getProperty("line.separator"); + } catch (SecurityException var3) { + this.newLine = "\n"; + } + + } + } + + public String getIndent() { + return this.indent; + } + + public void setIndent(String indent) { + if (indent == null) { + throw new IllegalArgumentException(); + } else { + this.indent = indent; + } + } + + public String getNewLine() { + return this.newLine; + } + + public void setNewLine(String newLine) { + if (newLine == null) { + throw new IllegalArgumentException(); + } else { + this.newLine = newLine; + } + } + + public void add(XMLEvent event) throws XMLStreamException { + switch (event.getEventType()) { + case 1: + this.newLine(); + this.core.add(event); + this.hasText = false; + this.hasEndTag = false; + ++this.depth; + return; + case 2: + --this.depth; + if (!this.hasText && this.hasEndTag) { + this.newLine(); + } + + this.core.add(event); + this.hasText = false; + this.hasEndTag = true; + return; + case 3: + case 5: + case 11: + if (!this.hasText) { + this.newLine(); + } + + this.core.add(event); + return; + case 4: + case 6: + case 12: + if (event.asCharacters().isWhiteSpace()) { + return; + } + + this.hasText = true; + this.core.add(event); + return; + case 7: + case 9: + case 10: + default: + this.core.add(event); + return; + case 8: + this.core.add(event); + this.flush(); + } + } + + private void newLine() throws XMLStreamException { + this.core.add(this.newLineEvent); + + for (int i = 0; i < this.depth; ++i) { + this.core.add(this.indentEvent); + } + + } + + public void add(XMLEventReader reader) throws XMLStreamException { + if (reader == null) { + throw new IllegalArgumentException(); + } else { + while (reader.hasNext()) { + this.add(reader.nextEvent()); + } + + } + } + + public void close() throws XMLStreamException { + this.core.close(); + } + + public void flush() throws XMLStreamException { + this.core.flush(); + } + + public NamespaceContext getNamespaceContext() { + return this.core.getNamespaceContext(); + } + + public void setNamespaceContext(NamespaceContext context) throws XMLStreamException { + this.core.setNamespaceContext(context); + } + + public String getPrefix(String uri) throws XMLStreamException { + return this.core.getPrefix(uri); + } + + public void setDefaultNamespace(String uri) throws XMLStreamException { + this.core.setDefaultNamespace(uri); + } + + public void setPrefix(String prefix, String uri) throws XMLStreamException { + this.core.setPrefix(prefix, uri); + } + + private abstract static class CharactersImpl implements Characters { + private CharactersImpl() { + } + + public boolean isWhiteSpace() { + return true; + } + + public boolean isCData() { + return false; + } + + public boolean isIgnorableWhiteSpace() { + return true; + } + + public int getEventType() { + return 4; + } + + public Location getLocation() { + return null; + } + + public boolean isStartElement() { + return false; + } + + public boolean isAttribute() { + return false; + } + + public boolean isNamespace() { + return false; + } + + public boolean isEndElement() { + return false; + } + + public boolean isEntityReference() { + return false; + } + + public boolean isProcessingInstruction() { + return false; + } + + public boolean isCharacters() { + return true; + } + + public boolean isStartDocument() { + return false; + } + + public boolean isEndDocument() { + return false; + } + + public StartElement asStartElement() { + return null; + } + + public EndElement asEndElement() { + return null; + } + + public Characters asCharacters() { + return this; + } + + public QName getSchemaType() { + return null; + } + + public void writeAsEncodedUnicode(Writer writer) throws XMLStreamException { + try { + writer.write(this.getData()); + } catch (IOException var3) { + throw new XMLStreamException(var3); + } + } + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/SignalsStaxXmlWriter.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/SignalsStaxXmlWriter.java new file mode 100644 index 0000000..a836530 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/SignalsStaxXmlWriter.java @@ -0,0 +1,110 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +import lombok.extern.slf4j.Slf4j; +import ru.nbch.credit_tracker.entities.signal_package.*; + +import javax.xml.stream.XMLEventFactory; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLOutputFactory; +import java.io.BufferedOutputStream; +import java.io.OutputStream; + +@Slf4j +public class SignalsStaxXmlWriter extends StaxXmlWriter{ + + public SignalsStaxXmlWriter(String charsetName) { + super(charsetName); + } + + public void writeWithStax(OutputStream outputStream, Signals signals, boolean useStandalone) { + try (BufferedOutputStream bos = new BufferedOutputStream(outputStream, 4 * 1024 * 1024)) { + + XMLOutputFactory output = XMLOutputFactory.newInstance(); + XMLEventFactory xmlEventFactory = XMLEventFactory.newInstance(); + XMLEventWriter writer = output.createXMLEventWriter(bos, charsetName); + writer = new IndentingXMLEventWriter(writer); + + writerWrapper = new WriterWrapper(xmlEventFactory, writer); + + if (useStandalone) { + bos.write("".formatted(charsetName).getBytes(charsetName)); + bos.flush(); + } else { + writerWrapper.addStartDocument(charsetName); + } + + writerWrapper.addStartElement("Signals"); + writerWrapper.addAttribute("online", signals.getOnline()); + writerWrapper.addSimplelement("Id", signals.getId()); + writeAuth(signals.getAuth()); + writeConditions(signals.getConditions()); + + writerWrapper.addEndElement("Signals"); + + if (!useStandalone) { + writerWrapper.addEndDocument(); + } + writerWrapper.flush(); + System.gc(); + } catch (Throwable e) { + log.error("Error serializing object to XML string: {}", e.getMessage(), e); + throw new IllegalStateException("cannot serialize object to XML"); + } + } + + private void writeAuth(Auth auth) throws Exception { + writerWrapper.addStartElement("Auth"); + writerWrapper.addSimplelement("User", auth.getUser()); + writerWrapper.addEndElement("Auth"); + } + + private void writeConditions(Conditions conditions) throws Exception { + writerWrapper.addStartElement("Conditions"); + writePersons(conditions.getPersons()); + writerWrapper.addEndElement("Conditions"); + } + + private void writePersons(Persons persons) throws Exception { + writerWrapper.addStartElement("Persons"); + writerWrapper.writeCollection("p", persons.getPerson(), this::writePerson); + writerWrapper.addEndElement("Persons"); + } + + private void writePerson(Person person, String name) throws Exception { + if (person == null) { + return; + } + writerWrapper.addStartElement(name); + writerWrapper.addAttribute("uid", person.getUid()); + writerWrapper.addAttribute("oksm", person.getOksm()); + writerWrapper.addAttribute("s", person.getS()); + writerWrapper.addAttribute("n", person.getN()); + writerWrapper.addAttribute("m", person.getM()); + writerWrapper.addAttribute("d", person.getD()); + writerWrapper.addAttribute("pn", person.getPn()); + writerWrapper.addAttribute("pd", person.getPd()); + writerWrapper.addAttribute("dt", person.getDt()); + writerWrapper.addAttribute("ip", person.getIp()); + writerWrapper.addAttribute("consentDate", person.getConsentDate()); + writerWrapper.addAttribute("consentPurpose", person.getConsentPurpose()); + writerWrapper.addAttribute("reportUser", person.getReportUser()); + writerWrapper.addAttribute("liability", person.getLiability()); + writerWrapper.addAttribute("consentPeriod", person.getConsentPeriod()); + writerWrapper.addAttribute("agrDate", person.getAgrDate()); + writerWrapper.addAttribute("reportUserRegNum", person.getReportUserRegNum()); + writerWrapper.addAttribute("reportUserTaxID", person.getReportUserTaxID()); + writerWrapper.addAttribute("consentHash", person.getConsentHash()); + writerWrapper.addAttribute("cS", person.getCs()); + writerWrapper.addAttribute("consentN", person.getConsentN()); + writerWrapper.addAttribute("consentM", person.getConsentM()); + writerWrapper.addAttribute("consentBd", person.getConsentBd()); + writerWrapper.addAttribute("consentPn", person.getConsentPn()); + writerWrapper.addAttribute("consentPd", person.getConsentPd()); + writerWrapper.addAttribute("consentDivCode", person.getConsentDivCode()); + writerWrapper.addAttribute("consentDt", person.getConsentDt()); + writerWrapper.addAttribute("inqPurpose", person.getInqPurpose()); + writerWrapper.addEndElement(name); + } + + +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxXmlWriter.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxXmlWriter.java new file mode 100644 index 0000000..4e2a43b --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/StaxXmlWriter.java @@ -0,0 +1,16 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +import java.io.OutputStream; + +public abstract class StaxXmlWriter { + + protected final String charsetName; + protected WriterWrapper writerWrapper; + + public StaxXmlWriter(String charsetName) { + this.charsetName = charsetName; + } + + public abstract void writeWithStax(OutputStream outputStream, T xmlObj, boolean useStandalone); + +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/WriterWrapper.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/WriterWrapper.java new file mode 100644 index 0000000..ddffa76 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/WriterWrapper.java @@ -0,0 +1,109 @@ +package ru.nbch.credit_tracker.service.xml.stax; + +import javax.xml.stream.XMLEventFactory; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.events.XMLEvent; +import java.util.List; + +public class WriterWrapper { + private final XMLEventFactory xmlEventFactory; + private final XMLEventWriter writer; + private final int batchSize = 2500; + + public WriterWrapper(XMLEventFactory xmlEventFactory, XMLEventWriter writer) { + this.xmlEventFactory = xmlEventFactory; + this.writer = writer; + } + + public void flush() throws Exception { + writer.flush(); + } + + + public void addStartDocument(String charsetName, String version, boolean standalone) throws Exception { + writer.add(xmlEventFactory.createStartDocument(charsetName, version, standalone)); + } + + public void addStartDocument(String charsetName) throws Exception { + writer.add(xmlEventFactory.createStartDocument(charsetName)); + } + + public void addDTD(String value) throws Exception { + writer.add(xmlEventFactory.createDTD(value)); + } + + public void addEndDocument() throws Exception { + writer.add(xmlEventFactory.createEndDocument()); + } + + public void addStartElement(String name) throws Exception { + writer.add(createStartElement(name)); + } + + public void addEndElement(String name) throws Exception { + writer.add(createEndElement(name)); + } + + public void addAttribute(String name, String value) throws Exception { + if(value != null) { + writer.add(createAttribute(name, value)); + } + } + + public void addSimplelement(String name, String value) throws Exception { + if (value == null) { + return; + } + writer.add(createStartElement(name)); + writer.add(createCharacters(value)); + writer.add(createEndElement(name)); + } + + public void addCharacters(String value) throws Exception { + if (value == null) { + return; + } + writer.add(createCharacters(value)); + } + + public void writeCollection(String name, List collection, ObjectWriterAction action) throws Exception { + if (collection == null || collection.isEmpty()) { + return; + } + + for (int i = 0; i < collection.size(); i++) { + T obj = collection.get(i); + if (obj != null) { + action.write(obj, name); + } + + if (i % batchSize == 0) { + writer.flush(); + } + } + } + + public void add(XMLEvent event) throws Exception { + writer.add(event); + } + + private XMLEvent createCharacters(String text) { + return xmlEventFactory.createCharacters(text); + } + + public XMLEvent createStartElement(String name) { + return xmlEventFactory.createStartElement("", "", name); + } + + public XMLEvent createEndElement(String name) { + return xmlEventFactory.createEndElement("", "", name); + } + + private XMLEvent createAttribute(String name, String value) { + return xmlEventFactory.createAttribute(name, value); + } + + public interface ObjectWriterAction { + void write(T obj, String name) throws Exception; + } +}