Nem tudo precisa (ou deve) ser resolvido com DataWeave. Quando o problema pede código de verdade, o MuleSoft 4 te dá uma porta de entrada oficial para o Java. Este é o primeiro artigo de uma série sobre integrar Java e low-code de forma equilibrada.
Uma das primeiras coisas que a documentação do MuleSoft deixa claro é a filosofia do Mule 4: a plataforma foi construída para minimizar a necessidade de código personalizado e para que você consiga entregar integrações sem precisar saber Java. Na maioria dos casos, conectores e DataWeave dão conta.
Mas a própria documentação reconhece que alguns casos avançados pedem código Java — por exemplo, reaproveitar uma biblioteca existente (como uma de cálculo de impostos), usar um modelo de objetos canônico da organização, ou executar uma lógica específica que ficaria difícil de outra forma. É exatamente para isso que existe o Java Module.
Neste artigo você vai entender quando e como usar o Java Module no Mule 4, no contexto de uma necessidade concreta: uma API de cálculo de frete.
O cenário: uma API REST de cálculo de frete
Vamos construir o seguinte: o cliente faz uma chamada HTTP para a nossa API, enviando os dados do frete que quer calcular — origem, destino, taxa por quilômetro, a distância e os itens do pedido. A API processa esses dados usando uma classe Java e devolve, na resposta, o valor do frete (com e sem desconto).
Um exemplo de requisição que o cliente envia:
POST /calc-frete
{
"origem": "São Paulo",
"destino": "Rio de Janeiro",
"taxaPorKm": 2.5,
"distanciaKm": 430,
"itens": ["Notebook", "Mouse"],
"percentualDesconto": 15
}
E a resposta que ele recebe de volta:
{
"rota": "São Paulo -> Rio de Janeiro",
"itens": 2,
"freteBase": 1075.0,
"freteComDesconto": 913.75
}
No meio do caminho, entre receber a requisição e montar a resposta, está o trabalho de verdade — e é nele que o Java Module entra. O fluxo da API, em visão de cima, fica assim:
Diagrama de sequência

- HTTP Listener recebe a requisição com os dados do frete.
- New → cria o objeto
CalculadoraFretecom os dados do cliente. - Invoke
adicionarItem→ adiciona cada item do pedido. - Invoke
calcularFrete→ calcula o valor com base na distância. - Invoke static
aplicarDesconto→ aplica o desconto informado. - Transform Message → monta o JSON de resposta.
- O HTTP Listener devolve essa resposta ao cliente.
Repare que os passos 2 a 5 são justamente os três tipos de chamada que o Java Module oferece: criar um objeto (New), chamar métodos desse objeto (Invoke) e chamar um método utilitário sem objeto nenhum (Invoke static). É essa a estrutura que o resto do artigo vai detalhar. Mas antes, vale entender por que precisamos do módulo.
Por que o DataWeave sozinho não resolve tudo
Vale entender o motivo técnico, porque ele explica a existência do módulo. No Mule 3, a linguagem de expressão era o MEL, que servia de ponte para criar objetos Java e executar métodos. No Mule 4, o MEL foi substituído pelo DataWeave, que é uma linguagem funcional.
E aqui está o detalhe: linguagens funcionais não produzem efeitos colaterais sobre seus argumentos. Não faz sentido, dentro da lógica do DataWeave, sair executando métodos de instância arbitrários em objetos arbitrários. Por isso, o DataWeave puro só consegue chamar métodos estáticos do Java — e nada mais. Para criar objetos e chamar métodos de instância, você precisa do Java Module.
Em resumo: chamar Java no Mule 4 não é uma gambiarra nem um “escape” do low-code. É um recurso de primeira classe, com um módulo oficial, interface visual no Anypoint Studio e suporte a DataSense (a metadata que ajuda o Studio a sugerir os campos certos).
A classe que processa o frete: CalculadoraFrete
Esta é a classe Java que a API vai usar. Ela tem estado (origem, destino, taxa, itens do pedido) e também utilitários puros (descontos e conversões que não dependem de nenhum pedido específico):
package com.integradordedados.examples;
import java.util.ArrayList;
import java.util.List;
public class CalculadoraFrete {
private final String origem;
private final String destino;
private final double taxaPorKm;
private final List<String> itens = new ArrayList<>();
public CalculadoraFrete(String origem, String destino, double taxaPorKm) {
if (taxaPorKm < 0) {
throw new IllegalArgumentException("A taxa por km não pode ser negativa.");
}
this.origem = origem;
this.destino = destino;
this.taxaPorKm = taxaPorKm;
}
// Método de instância que altera o estado (não retorna nada)
public void adicionarItem(String item) {
if (item == null || item.trim().isEmpty()) {
throw new IllegalArgumentException("O item não pode ser vazio.");
}
itens.add(item.trim());
}
// Método de instância que usa o estado E recebe parâmetro
public double calcularFrete(double distanciaKm) {
if (distanciaKm < 0) {
throw new IllegalArgumentException("A distância não pode ser negativa.");
}
double frete = distanciaKm * taxaPorKm;
if (itens.size() > 3) {
frete = frete * 0.9; // 10% de desconto para pedidos grandes
}
return frete;
}
public String getOrigem() { return origem; }
public String getDestino() { return destino; }
public int getQuantidadeItens() { return itens.size(); }
// Métodos ESTÁTICOS: utilitários puros, não dependem de estado
public static double gramasParaKg(double gramas) {
return gramas / 1000.0;
}
public static double aplicarDesconto(double valor, double percentual) {
if (percentual < 0 || percentual > 100) {
throw new IllegalArgumentException("O percentual deve estar entre 0 e 100.");
}
return valor - (valor * percentual / 100.0);
}
}
A divisão é proposital e ensina um conceito importante: calcularFrete e adicionarItem dependem do pedido (do estado do objeto), então são métodos de instância. Já gramasParaKg e aplicarDesconto são funções puras — entra valor, sai valor — então são static. Esse critério (depende de estado ou não?) é o que decide entre uma coisa e outra, e também decide qual operação do Java Module você vai usar para cada uma.
Antes de tudo: dois pré-requisitos que evitam dor de cabeça
A documentação destaca dois pontos que, se ignorados, fazem o fluxo quebrar com erros confusos.
1. A classe precisa ser exportada
Segundo a documentação, as classes usadas com o Java Module precisam ser exportadas (a menos que estejam no pacote padrão). Se não forem, a execução falha com o erro JAVA:CLASS_NOT_FOUND. Em um projeto do Studio, isso é feito na configuração de recursos exportados do Mule. Como na prática você quase sempre coloca a classe em um pacote nomeado (boa prática), preste atenção nisso.
2. Decida agora: argumentos por nome ou por posição
Esse é o ponto que mais confunde quem está começando, e a documentação é explícita sobre ele. Quando você passa os argumentos para um construtor ou método, as chaves do mapa de args determinam como os parâmetros são passados. Há dois jeitos:
- Por nome (
origem,destino, …): mais legível e — vantagem importante — independente da ordem. Você pode listar os argumentos em qualquer sequência que o Mule casa cada um pelo nome. Mas isso só funciona se a classe for compilada com a flag-parameters. - Por posição (
arg0,arg1, …): funciona sempre, sem configuração extra. São os nomes canônicos de cada parâmetro, e aqui a ordem importa:arg0é sempre o primeiro parâmetro declarado,arg1o segundo, e assim por diante.
Para usar os nomes em um projeto do Studio, o maven-compiler-plugin precisa estar configurado no pom.xml com a flag -parameters:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
Na prática: os projetos criados pelo Anypoint Studio normalmente já vêm com essa flag configurada, então passar argumentos por nome costuma funcionar “de primeira” — a ponto de muita gente nem perceber que a flag está lá. O detalhe é que, se a flag não estiver ativa (por exemplo, ao usar uma classe vinda de um JAR de terceiros compilado sem ela), os nomes deixam de funcionar e você é obrigado a usar
arg0,arg1.
E o erro que aparece nesse caso é bem específico. Se você passar { taxaPorKm: ..., origem: ..., destino: ... } para um construtor cuja classe não foi compilada com -parameters, o Mule responde algo como:
Failed to instantiate Class 'com.integradordedados.examples.CalculadoraFrete'
using the Constructor 'CalculadoraFrete(java.lang.String, java.lang.String, double)'.
Expected arguments are [java.lang.String arg0, java.lang.String arg1, double arg2]
and invocation was attempted with arguments
[java.lang.Double taxaPorKm, java.lang.String origem, java.lang.String destino].
Missing parameters are [arg0, arg1, arg2].
A mensagem entrega o diagnóstico: o Mule esperava arg0, arg1, arg2 (sinal de que não enxergou os nomes reais) e recebeu chaves com nome. Vendo “Missing parameters are [arg0, arg1, arg2]”, você já sabe que ou ativa a flag -parameters, ou passa os argumentos por posição.
Neste artigo vou usar a forma por nome, que é a mais legível e funciona nos projetos padrão do Studio. Se em algum momento você esbarrar no erro acima, é só trocar as chaves por arg0, arg1, arg2, respeitando a ordem dos parâmetros.
Cenário 1 — New: criar a instância com os dados da requisição
A operação New chama um construtor da sua classe e devolve o objeto criado. Por padrão, o resultado vai para o payload, mas você pode (e geralmente deve) guardá-lo numa variável com target, para não atropelar o payload existente.
Como os dados vêm da requisição do cliente, os argumentos apontam para o payload de entrada:
<java:new
class="com.integradordedados.examples.CalculadoraFrete"
constructor="CalculadoraFrete(String, String, double)"
target="calc">
<java:args><![CDATA[#[{
origem: payload.origem,
destino: payload.destino,
taxaPorKm: payload.taxaPorKm
}]]]></java:args>
</java:new>
Pontos importantes:
- O
constructorinclui os tipos dos parâmetros —CalculadoraFrete(String, String, double). Isso identifica qual construtor chamar (uma classe pode ter vários). A documentação observa que você pode até usar o nome de pacote completo dos tipos, comojava.lang.String, mas isso só é necessário para desfazer ambiguidades. - Os
argsusam os nomes dos parâmetros (origem,destino,taxaPorKm), lidos do JSON enviado pelo cliente. Como explicamos, isso depende da flag-parameters— que os projetos do Studio normalmente já trazem. Se preferir não depender dela, troque pelas chavesarg0,arg1,arg2na ordem dos parâmetros. - O
target="calc"guarda o objeto emvars.calc, deixando o payload original (a requisição) intacto, caso você precise dele depois.
A partir daqui, vars.calc carrega um objeto CalculadoraFrete com os dados daquele cliente.
Cenário 2 — Invoke: chamar métodos da instância
A operação Invoke chama um método de um objeto que você já criou. O que a diferencia do estático é o campo instance, que aponta justamente para o objeto.
Primeiro, adicionamos os itens do pedido. Como a requisição traz uma lista de itens, normalmente você faria isso dentro de um For Each, iterando sobre payload.itens. O adicionarItem não retorna nada (void) — é um método que só altera o estado do objeto:
<foreach collection="#[payload.itens]">
<java:invoke
instance="#[vars.calc]"
class="com.integradordedados.examples.CalculadoraFrete"
method="adicionarItem(String)">
<java:args><![CDATA[#[{ item: payload }]]]></java:args>
</java:invoke>
</foreach>
Dentro do For Each, o
payloadrepresenta o item atual da iteração — por issoitem: payloadpassa cada elemento da lista para o método. (Sem a flag-parameters, seriaarg0: payload.)
Com todos os itens já adicionados, e agora fora do For Each, calculamos o frete uma única vez. Aqui o método usa o estado (a taxaPorKm definida no construtor e a quantidade de itens acumulada no loop) e recebe um parâmetro (a distância vinda da requisição), guardando o retorno em vars.valorFrete:
<java:invoke
instance="#[vars.calc]"
class="com.integradordedados.examples.CalculadoraFrete"
method="calcularFrete(double)"
target="valorFrete">
<java:args><![CDATA[#[{ distanciaKm: payload.distanciaKm }]]]></java:args>
</java:invoke>
Repare como o method também leva o tipo do parâmetro: calcularFrete(double). É a mesma lógica do construtor — a assinatura completa remove ambiguidade quando há sobrecarga de métodos.
Detalhe importante: como vimos, dentro do For Each o
payloadé o item da vez. Ao sair do loop, opayloadvolta a ser a requisição original — por isso conseguimos lerpayload.distanciaKmaqui. Se você precisasse de algum dado da requisição lá dentro do loop, teria que guardá-lo antes numa variável.
Uma vantagem do Studio que vale mencionar: o Java Module tem suporte a DataSense na operação invoke, o que significa que o Studio consegue descobrir o tipo de retorno do método e ajudar a montar os argumentos visualmente, arrastando e soltando os campos.
Cenário 3 — Invoke static: aplicar o desconto
Para métodos estáticos, você não precisa de New nem de instance. A operação Invoke static chama o método direto. É a única forma de chamada Java que o próprio DataWeave também conseguiria fazer sozinho, mas pelo módulo você ganha a interface visual.
Aplicando o desconto informado pelo cliente sobre o frete calculado:
<java:invoke-static
class="com.integradordedados.examples.CalculadoraFrete"
method="aplicarDesconto(double, double)"
target="valorFinal">
<java:args><![CDATA[#[{
valor: vars.valorFrete,
percentual: payload.percentualDesconto
}]]]></java:args>
</java:invoke-static>
Aqui aplicarDesconto recebe dois parâmetros: valor (o frete calculado no passo anterior) e percentual (vindo da requisição). Como estamos passando por nome, a ordem entre eles não importaria — mas mantê-la na ordem da assinatura ajuda na leitura. Como o método é uma função pura, ele não precisa saber de nenhum pedido — só recebe números e devolve um número.
Bônus: validando o tipo de um objeto
Além de criar e invocar, o Java Module tem uma terceira capacidade útil: verificar se um objeto é instância de uma determinada classe. Isso aparece de duas formas, com comportamentos diferentes — e a distinção entre elas é importante.
Para ilustrar, vamos usar um par de classes simples com herança (à parte da CalculadoraFrete, só para o exemplo):
package com.integradordedados.examples;
public class Employee extends Person {
private String company;
public Employee(String name, Integer age, String company) {
super(name, age);
this.company = company;
}
}
package com.integradordedados.examples;
public class Person {
private String name;
private Integer age;
public Person() {
this.name = "Max Mule";
this.age = 10;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
Repare que Funcionario estende Pessoa — ou seja, todo Funcionario é também um Pessoa. Guarde isso, porque é o que o segundo tópico vai explorar. Suponha que criamos um objeto Funcionario e guardamos em vars.jane com a operação New.
A operação Validate Type
A operação Validate Type testa se um objeto é instância de uma classe. O comportamento dela é peculiar e precisa ser entendido: se o objeto for do tipo esperado, o fluxo segue normalmente e o payload não é alterado; se não for, a operação lança um erro, interrompendo o fluxo e passando o controle para o error handler.
<java:validate-type doc:name="Employee"
doc:id="d3a2cdd4-3281-4e2d-ac51-f83e33b9928b"
class="com.integradordedados.examples.Employee"
instance="#[vars.employee]" />
Como vars.jane é um Funcionario, essa validação passa e o fluxo continua. Pense nela como uma “asserção”: ou o tipo está certo e a execução prossegue, ou ela barra o fluxo ali mesmo. É útil quando um tipo errado é uma condição de erro de verdade, que você quer tratar no error handler.
Verificando subtipos (herança)
Aqui entra a herança. A operação Validate Type tem o parâmetro acceptSubtypes, que controla se subclasses são aceitas. Ele pode ser true, false ou uma expressão, e o padrão é true quando omitido.
Como Funcionario é um subtipo de Pessoa, validar o objeto contra Pessoa com acceptSubtypes="true" passa normalmente:
<java:validate-type doc:name="Person"
doc:id="7db531f9-9c04-48c7-bc12-39b05af879d7"
class="com.integradordedados.examples.Person"
instance="#[vars.employee]" />
E como true é o padrão, o código acima equivale a omitir o parâmetro:
<java:validate-type class="com.integradordedados.examples.Person" instance="#[vars.employee]" />
Mas se você desligar a aceitação de subtipos, a história muda. Validar um Funcionario contra Pessoa com acceptSubtypes="false" exige que o objeto seja exatamente um Pessoa — e, como ele é um Funcionario, a operação lança erro:
<java:validate-type
class="com.integradordedados.examples.Employee"
instance="#[vars.employee]"
acceptSubtypes="false" />
Em resumo: acceptSubtypes="true" (o padrão) pergunta “é um Pessoa ou qualquer coisa que herde de Pessoa?”; acceptSubtypes="false" pergunta “é exatamente um Pessoa, nada mais?”.
A função isInstanceOf no DataWeave
A operação Validate Type é radical: quando falha, derruba o fluxo. Mas muitas vezes você só quer saber se o objeto é de um tipo, sem que isso vire um erro — por exemplo, para decidir um caminho num Choice. Para isso o módulo oferece a função DataWeave isInstanceOf, que retorna true ou false e não lança erro quando o resultado é false:
<choice doc:name="Choice"
doc:id="80cd0fba-45c4-481b-a590-013c045e8d81">
<when
expression="#[Java::isInstanceOf(vars.employee, 'com.integradordedados.examples.Employee')]">
<logger level="INFO" doc:name="Logger"
doc:id="68b2681c-0f53-45d1-9fc7-2b842c663e75" />
</when>
</choice>
A diferença prática entre as duas é simples e vale memorizar: use a operação Validate Type quando um tipo errado deve interromper o fluxo (é um erro); use a função isInstanceOf quando você só quer ramificar a lógica com base no tipo, sem quebrar nada.
Montando a resposta da API
Com tudo calculado, o último passo é o Transform Message que monta o JSON de resposta para o cliente. O DataWeave consegue ler as propriedades do objeto a partir dos getters (o getter getOrigem() é lido como a propriedade origem):
%dw 2.0
output application/json
---
{
rota: vars.calc.origem ++ " -> " ++ vars.calc.destino,
itens: vars.calc.quantidadeItens,
freteBase: vars.valorFrete,
freteComDesconto: vars.valorFinal
}
Esse é o objeto que o HTTP Listener devolve na resposta — exatamente o JSON que mostramos lá no começo. O ciclo se fecha: a requisição entrou com os parâmetros, o Java fez o cálculo, e o cliente recebe o resultado.
De onde vêm essas propriedades?
Vale entender o que acontece nesse Transform Message, porque é um ponto que costuma gerar dúvida. Note que estamos escrevendo vars.calc.origem, vars.calc.destino e vars.calc.quantidadeItens — mas a classe CalculadoraFrete não tem atributos chamados exatamente assim acessíveis de fora. O que ela tem são getters.
O DataWeave aplica a convenção JavaBeans: para ler uma propriedade x, ele procura um método getX() no objeto. Removendo o prefixo get e deixando a inicial minúscula, o getter vira o nome da propriedade. Então:
vars.calc.origem→ chamagetOrigem()→ devolve o campoorigem.vars.calc.destino→ chamagetDestino()→ devolve o campodestino.vars.calc.quantidadeItens→ chamagetQuantidadeItens().
O último é o mais interessante e foi o que pode ter te deixado em dúvida: não existe um campo quantidadeItens na classe. Olhe o que o getter realmente faz:
private final List<String> itens = new ArrayList<>();
public void adicionarItem(String item) {
// ...
itens.add(item.trim());
}
public int getQuantidadeItens() {
return itens.size(); // conta os itens da lista, na hora
}
Ou seja, quantidadeItens é uma propriedade derivada: ela não guarda um número, ela calcula itens.size() toda vez que é chamada. Cada adicionarItem dentro do For Each colocou um elemento na lista itens; quando o DataWeave lê vars.calc.quantidadeItens, o getQuantidadeItens() devolve o total acumulado naquele momento. No exemplo da API (Notebook + Mouse), o resultado é 2.
Esse é, na prática, o conceito central do artigo em ação: o objeto carrega estado (a lista de itens que foi sendo preenchida ao longo do fluxo) e o expõe de forma calculada através de um getter. É algo que seria desajeitado de fazer só com variáveis soltas no Mule, e que o Java resolve de forma natural.
O projeto completo
Ao longo do artigo, cada conector foi mostrado isoladamente nas seções de New, Invoke e Invoke static — que é o que importa para entender o módulo. O projeto Mule completo, com o fluxo montado de ponta a ponta, a classe CalculadoraFrete.java e o pom.xml já configurado com a flag -parameters, está disponível no GitHub:
➡️ github.com/Weslleyw10/mule-java-module-examples
Para testar, clone o repositório, suba a aplicação no Anypoint Studio e envie um POST para http://localhost:8081/calc-frete com o JSON da requisição que mostramos no início. A resposta deve trazer a rota, a quantidade de itens e os dois valores de frete.
Veja em vídeo
Se você prefere acompanhar a implementação passo a passo, gravei um vídeo construindo essa API de frete do zero — criando a classe, configurando o Java Module e montando o fluxo no Anypoint Studio. Assista no canal:
🎥 @integradordedados no YouTube
Erros comuns ao usar o Java Module
JAVA:CLASS_NOT_FOUND: quase sempre é a classe não exportada (em pacote nomeado) ou o nome qualificado escrito errado.- Esquecer os tipos na assinatura: escrever
calcularFreteem vez decalcularFrete(double). O módulo precisa da assinatura completa. - Usar nomes de parâmetro sem a flag
-parameters: se a classe não foi compilada com-parameterse você passa{ origem: ... }, o Mule reclama com “Missing parameters are [arg0, arg1, …]”. Ou ative a flag nopom.xml, ou passe por posição (arg0,arg1). - Tentar chamar método de instância com
invoke-static: o estático não teminstance. Para métodos de instância, éNew+Invoke. - Tipos do JSON x tipos do Java: valores numéricos vindos do JSON podem precisar de atenção quanto ao tipo esperado pelo método (por exemplo, um
double). Se aparecer erro de assinatura mesmo com os tipos certos, vale conferir a conversão dos argumentos.
Conclusão
O Java Module mostra que a pergunta “low-code ou código?” é mal colocada. A API de frete continua sendo uma API Mule de ponta a ponta — o HTTP Listener recebe e responde, o DataWeave monta o JSON, o fluxo orquestra tudo. O Java entrou cirurgicamente apenas no cálculo, que é onde a lógica fazia sentido em código. Você cria objetos com New, chama o comportamento deles com Invoke, usa utilitários com Invoke static e ainda pode validar tipos com Validate Type / isInstanceOf — tudo dentro do mesmo fluxo visual.
Essa é a base. Nos próximos artigos da série, vamos aplicar isso a um caso real de integração — incluindo um problema que era penoso no DataWeave e ficou trivial com uma classe Java de poucas linhas.
Quer ir além do Java Module?
Dominar o Java Module é uma peça do quebra-cabeça. Mas construir uma carreira sólida em integração de sistemas no mundo corporativo envolve muito mais: entender arquitetura de integrações, boas práticas com MuleSoft, como pensar soluções de ponta a ponta e como crescer profissionalmente nessa área.
Foi para reunir tudo isso em um caminho claro que escrevi o ebook “Do Zero à Engenharia de Integrações” — um guia para quem quer entrar e evoluir na carreira de integrações entre sistemas, com MuleSoft como ferramenta central.
📘 Conheça o ebook “Do Zero à Engenharia de Integrações”
Se este artigo te ajudou, o ebook é o próximo passo para ver o cenário completo.
Referências
- MuleSoft Documentation — Java Module 2.0 (visão geral e motivação): https://docs.mulesoft.com/java-module/latest/
- MuleSoft Documentation — Create an Instance of a Class (operação
new,target, flag-parameters): https://docs.mulesoft.com/java-module/latest/java-create-instance - MuleSoft Documentation — Invoke Methods (operações
invokeeinvoke-static, DataSense): https://docs.mulesoft.com/java-module/latest/java-invoke-method - MuleSoft Documentation — Test for an Instance of a Class (operação
validate-type,acceptSubtypes, funçãoisInstanceOf): https://docs.mulesoft.com/java-module/latest/java-instanceof - Código-fonte completo (projeto Mule + classe Java): https://github.com/Weslleyw10/mule-java-module-examples
- Vídeo da implementação no canal: https://www.youtube.com/@integradordedados
Nota de versão: os exemplos seguem o Java Module 2.0, que requer Mule 4.9.4 ou posterior. Em runtimes 4.x anteriores, a versão do Java Module pode diferir; valide a sintaxe na documentação correspondente à sua versão. A documentação também alerta que o suporte padrão a Java 8 e 11 se encerra em agosto de 2026 para o 4.6 LTS — vale considerar o caminho de atualização.
Compartilhe com sua rede:




Pingback: Como obter as dimensões de uma imagem com Java no Mule 4