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

wmixvideo / nfe / #9530

28 Jul 2026 07:26PM UTC coverage: 51.181%. First build
#9530

push

web-flow
Merge 2b4db697b into 0beb7b6c6

78 of 81 new or added lines in 4 files covered. (96.3%)

15234 of 29765 relevant lines covered (51.18%)

0.51 hits per line

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

93.02
/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
     * @throws IllegalArgumentException se {@code niveisDeWrapper} for menor que 1.
73
     */
74
    public static String desempacotar(final String respostaXml, final int niveisDeWrapper) throws DFSoapFaultException {
75
        if (niveisDeWrapper < 1) {
1✔
76
            throw new IllegalArgumentException("niveisDeWrapper deve ser no minimo 1, recebido: " + niveisDeWrapper);
1✔
77
        }
78
        try {
79
            final Document documento = criarDocumentBuilderFactory().newDocumentBuilder().parse(new InputSource(new StringReader(respostaXml)));
1✔
80

81
            final Element body = (Element) documento.getElementsByTagNameNS(SOAP12_NS, "Body").item(0);
1✔
82
            Element wrapperAtual = primeiroElementoFilho(body);
1✔
83

84
            if ("Fault".equals(wrapperAtual.getLocalName())) {
1✔
85
                throw new DFSoapFaultException(textoDoFault(wrapperAtual));
1✔
86
            }
87

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

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

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

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

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

134
    private static String serializar(final Element elemento) throws Exception {
135
        final TransformerFactory transformerFactory = TransformerFactory.newInstance();
1✔
136
        // Hardening extra contra XXE (defesa em profundidade - o elemento aqui ja vem de um DOM
137
        // parseado com o DocumentBuilderFactory endurecido em criarDocumentBuilderFactory(), entao
138
        // nao ha DTD/stylesheet externo a resolver neste transform). Alguns providers JAXP alternativos
139
        // (ex.: o Xalan empacotado pelo WildFly/JBoss, diferente do Xalan interno do JDK) nao suportam
140
        // esses atributos e lancam IllegalArgumentException - nesse caso, seguimos sem eles.
141
        try {
142
            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
1✔
143
            transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
1✔
NEW
144
        } catch (final IllegalArgumentException e) {
×
145
            // provider JAXP nao suporta o atributo - segue com o comportamento padrao dele
146
        }
1✔
147
        final Transformer transformer = transformerFactory.newTransformer();
1✔
148
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
1✔
149
        final StringWriter writer = new StringWriter();
1✔
150
        transformer.transform(new DOMSource(elemento), new StreamResult(writer));
1✔
151
        return writer.toString();
1✔
152
    }
153
}
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