• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wmixvideo / nfe / #9519

28 Jul 2026 03:08PM UTC coverage: 51.085%. First build
#9519

push

web-flow
Merge 00094c686 into 0beb7b6c6

68 of 72 new or added lines in 4 files covered. (94.44%)

15201 of 29756 relevant lines covered (51.09%)

0.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.68
/src/main/java/com/fincatto/documentofiscal/utils/DFSoapEnvelope.java
1
package com.fincatto.documentofiscal.utils;
2

3
import org.w3c.dom.Document;
4
import org.w3c.dom.Element;
5
import org.w3c.dom.Node;
6
import org.xml.sax.InputSource;
7

8
import javax.xml.XMLConstants;
9
import javax.xml.parsers.DocumentBuilderFactory;
10
import javax.xml.transform.OutputKeys;
11
import javax.xml.transform.Transformer;
12
import javax.xml.transform.TransformerFactory;
13
import javax.xml.transform.dom.DOMSource;
14
import javax.xml.transform.stream.StreamResult;
15
import java.io.StringReader;
16
import java.io.StringWriter;
17

18
/**
19
 * Monta e desempacota o envelope SOAP 1.2 no mesmo formato que os stubs gerados pelo Axis2
20
 * (via WSDL2Java) produziam para os webservices da SEFAZ: um unico elemento "wrapper"
21
 * (ex.: {@code nfeDadosMsg}/{@code nfeResultMsg}) dentro de {@code soap:Body}, contendo o
22
 * XML de negocio (ja serializado pelo Simple XML Framework) como filho direto.
23
 * <p>
24
 * Substitui a montagem via {@code Axiom}/{@code ADBBean} por concatenacao de texto na ida
25
 * (o XML de negocio ja chega pronto como {@code String}) e parsing DOM na volta.
26
 * @author Marcos Lombardi de Andrade
27
 */
28
public final class DFSoapEnvelope {
29

30
    private static final String SOAP12_NS = "http://www.w3.org/2003/05/soap-envelope";
31

32
    private DFSoapEnvelope() {
33
    }
34

35
    /**
36
     * Envelopa o XML de negocio dentro de {@code soap:Envelope/soap:Body/wrapperElemento}.
37
     *
38
     * @param wrapperNamespace namespace do elemento wrapper, definido pelo WSDL do servico (ex.: {@code .../wsdl/NFeStatusServico4}).
39
     * @param wrapperElemento nome local do elemento wrapper (ex.: {@code nfeDadosMsg}).
40
     * @param xmlNegocio XML de negocio ja serializado (ex.: {@code consStatServ}), com seu proprio namespace.
41
     * @return o envelope SOAP 1.2 completo, pronto para ser enviado no corpo da requisicao HTTP.
42
     */
43
    public static String envelopar(final String wrapperNamespace, final String wrapperElemento, final String xmlNegocio) {
44
        return "<soap:Envelope xmlns:soap=\"" + SOAP12_NS + "\">"
1✔
45
                + "<soap:Body>"
46
                + "<" + wrapperElemento + " xmlns=\"" + wrapperNamespace + "\">" + xmlNegocio + "</" + wrapperElemento + ">"
47
                + "</soap:Body>"
48
                + "</soap:Envelope>";
49
    }
50

51
    /**
52
     * Desempacota a resposta SOAP 1.2 devolvida pela SEFAZ, retornando o XML de negocio
53
     * (o filho do elemento wrapper, ex.: {@code retConsStatServ}) pronto para ser lido pelo
54
     * {@link org.simpleframework.xml.core.Persister}.
55
     *
56
     * @param respostaXml corpo da resposta HTTP, um envelope SOAP 1.2 completo.
57
     * @return o XML de negocio de dentro do wrapper.
58
     * @throws DFSoapFaultException se a resposta for um {@code soap:Fault}.
59
     */
60
    public static String desempacotar(final String respostaXml) throws DFSoapFaultException {
61
        return DFSoapEnvelope.desempacotar(respostaXml, 1);
1✔
62
    }
63

64
    /**
65
     * Variante de {@link #desempacotar(String)} para operacoes cujo WSDL aninha o XML de
66
     * negocio dentro de mais de um elemento wrapper (ex.: NFeDistribuicaoDFe, cujo corpo SOAP e
67
     * {@code nfeDistDFeInteresseResponse > NFeDistDFeInteresseResult > <retDistDFeInt>...},
68
     * dois niveis de wrapper em vez de um).
69
     *
70
     * @param niveisDeWrapper quantidade de elementos wrapper entre {@code soap:Body} e o XML de
71
     * negocio (1 para o caso comum, coberto pelo overload de um argumento).
72
     */
73
    public static String desempacotar(final String respostaXml, final int niveisDeWrapper) throws DFSoapFaultException {
74
        try {
75
            final Document documento = criarDocumentBuilderFactory().newDocumentBuilder().parse(new InputSource(new StringReader(respostaXml)));
1✔
76

77
            final Element body = (Element) documento.getElementsByTagNameNS(SOAP12_NS, "Body").item(0);
1✔
78
            Element wrapperAtual = primeiroElementoFilho(body);
1✔
79

80
            if ("Fault".equals(wrapperAtual.getLocalName())) {
1✔
81
                throw new DFSoapFaultException(textoDoFault(wrapperAtual));
1✔
82
            }
83

84
            // desce um nivel de wrapper por vez ate sobrar so o penultimo, cujo filho e o XML de negocio
85
            for (int nivel = 1; nivel < niveisDeWrapper; nivel++) {
1✔
86
                wrapperAtual = primeiroElementoFilho(wrapperAtual);
1✔
87
            }
88

89
            return serializar(primeiroElementoFilho(wrapperAtual));
1✔
90
        } catch (final DFSoapFaultException e) {
1✔
91
            throw e;
1✔
92
        } catch (final Exception e) {
1✔
93
            throw new IllegalStateException("Resposta da SEFAZ nao e um envelope SOAP 1.2 valido", e);
1✔
94
        }
95
    }
96

97
    /**
98
     * DocumentBuilderFactory endurecido contra XXE (entidades externas/DTD) - a resposta e um
99
     * XML vindo pela rede (o webservice da SEFAZ), entao nao deve ser tratada como confiavel
100
     * por padrao para fins de parsing.
101
     */
102
    private static DocumentBuilderFactory criarDocumentBuilderFactory() throws javax.xml.parsers.ParserConfigurationException {
103
        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
1✔
104
        factory.setNamespaceAware(true);
1✔
105
        factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
1✔
106
        factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
1✔
107
        factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
1✔
108
        factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
1✔
109
        factory.setXIncludeAware(false);
1✔
110
        factory.setExpandEntityReferences(false);
1✔
111
        return factory;
1✔
112
    }
113

114
    private static Element primeiroElementoFilho(final Element pai) {
115
        Node filho = pai.getFirstChild();
1✔
116
        while (filho != null && filho.getNodeType() != Node.ELEMENT_NODE) {
1✔
NEW
117
            filho = filho.getNextSibling();
×
118
        }
119
        if (filho == null) {
1✔
NEW
120
            throw new IllegalStateException("Elemento <" + pai.getLocalName() + "> nao tem filho");
×
121
        }
122
        return (Element) filho;
1✔
123
    }
124

125
    private static String textoDoFault(final Element fault) {
126
        final org.w3c.dom.NodeList textos = fault.getElementsByTagNameNS(SOAP12_NS, "Text");
1✔
127
        return textos.getLength() > 0 ? textos.item(0).getTextContent() : fault.getTextContent();
1✔
128
    }
129

130
    private static String serializar(final Element elemento) throws Exception {
131
        final TransformerFactory transformerFactory = TransformerFactory.newInstance();
1✔
132
        // Hardening extra contra XXE (defesa em profundidade - o elemento aqui ja vem de um DOM
133
        // parseado com o DocumentBuilderFactory endurecido em criarDocumentBuilderFactory(), entao
134
        // nao ha DTD/stylesheet externo a resolver neste transform). Alguns providers JAXP alternativos
135
        // (ex.: o Xalan empacotado pelo WildFly/JBoss, diferente do Xalan interno do JDK) nao suportam
136
        // esses atributos e lancam IllegalArgumentException - nesse caso, seguimos sem eles.
137
        try {
138
            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
1✔
139
            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
1✔
NEW
140
        } catch (final IllegalArgumentException e) {
×
141
            // provider JAXP nao suporta o atributo - segue com o comportamento padrao dele
142
        }
1✔
143
        final Transformer transformer = transformerFactory.newTransformer();
1✔
144
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
1✔
145
        final StringWriter writer = new StringWriter();
1✔
146
        transformer.transform(new DOMSource(elemento), new StreamResult(writer));
1✔
147
        return writer.toString();
1✔
148
    }
149
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc