Configura, revisa e debuga integração HTTP externa em Sankhya Addon Studio usando Retrofit + Moshi + OkHttp — declara dependências `moduleLib` no `build.gradle`, escreve interface client (`@GET`/`@POST`/`@Body`/`@Path`/`@Query`/`@Headers`), faz wiring Guice via `@Provides @Singleton` em `@CustomModule`, cria interceptors OkHttp (autenticação, logging, headers). Use ao integrar API REST/JSON externa, consumir endpoint de terceiro, adicionar lib Retrofit/Moshi/OkHttp, montar HTTP client, criar interceptor, ou diagnosticar `NoClassDefFoundError` runtime de classe Retrofit/OkHttp.
Configura, revisa e debuga integração HTTP externa em Sankhya Addon Studio usando Retrofit + Moshi + OkHttp — declara dependências `moduleLib` no `build.gradle`, escreve interface client (`@GET`/`@POST`/`@Body`/`@Path`/`@Query`/`@Headers`), faz wiring Guice via `@Provides @Singleton` em `@CustomModule`, cria interceptors OkHttp (autenticação, logging, headers). Use ao integrar API REST/JSON externa, consumir endpoint de terceiro, adicionar lib Retrofit/Moshi/OkHttp, montar HTTP client, criar interceptor, ou diagnosticar `NoClassDefFoundError` runtime de classe Retrofit/OkHttp.
Stack oficial para integração HTTP externa: Retrofit + Moshi + OkHttp. Substitui HttpClient nativo, URLConnection, Apache HTTP Client.
Esta skill cobre: declaração de deps no Gradle, interface client, wiring Guice, interceptors. Decisões arquiteturais (factory wrapper vs builder direto, retry, URL dinâmica) são do projeto — a skill mostra patterns válidos sem opinar.
1. Dependências (build.gradle do módulo)
NÃO declarar no build.gradle raiz. Vai no build.gradle do módulo (model/build.gradle, etc.).
Use somente moduleLib — a configuração custom do plugin br.com.sankhya.addonstudio já adiciona o JAR ao classpath de compilação e empacota no EJB final. Não duplicar com implementation.
Indicado quando addon tem múltiplas integrações HTTP — encapsula timeouts/converter padrão em um lugar só, deixa @Provides enxuto. Trocar a lib base no futuro vira mudança em arquivo único.
Pipeline OkHttp: cada interceptor envolve o próximo (request desce, response sobe).
Atenção — segundo interceptor @Component quebra o deploy: cada @Component é bindado automaticamente às interfaces que implementa; dois @Component implements Interceptor no mesmo addon geram [Guice/BindingAlreadySet]: okhttp3.Interceptor was bound multiple times na subida do Wildfly (build e testes passam). A partir do segundo interceptor, remova o stereotype e proveja via @Provides @Singleton do tipo concreto — ver skill dependency-injection (seção 9).
5. Executando chamadas
O retorno Call<T> é preguiçoso — só executa quando você chama .execute() (sync) ou .enqueue() (async).
Boilerplate de tratamento (status, body nulo, IOException) repete em toda chamada. Projetos costumam extrair um RetrofitCallExecutor — ver bloco "Patterns avançados" abaixo.
6. Configuração de base URL
Três abordagens válidas. Skill não opina.
Abordagem
Quando usar
Constante static final no módulo
URL fixa, mudou só com release
Campo @Value(value = "INTEGRATION_PARCEIRO_URL", type = ValueType.ENV_VAR, defaultValue = "...") num @Component de config, injetado como parâmetro do @Provides
URL varia por ambiente (dev/homol/prod) — sintaxe completa na skill value
Tabela de configuração no banco
URL gerenciada pelo cliente final em runtime — usar factory pattern (avançado)
7. Patterns avançados — menção curta
Estes patterns são comuns mas opcionais. Skill não detalha — implemente conforme a necessidade do projeto.
URL dinâmica (config no banco): crie interface funcional ParceiroApiFactory { ParceiroApi create(String urlBase); } e binde via @Provides retornando lambda. Útil quando gateway lê URL de ConfiguracaoPlataforma.
Retry com backoff: envolva call.execute() em um RetryExecutor próprio — distinga exceções de rede (retry) das de API (não retry). Considere okhttp3.ConnectionPool e timeouts antes de retry.
Logging:okhttp3.logging.HttpLoggingInterceptor (dep adicional moduleLib 'com.squareup.okhttp3:logging-interceptor:3.14.9'). Configurar nível BODY só em dev.
SOAP/XML: use converter-scalars + interceptor que envelopa request em <soap:Envelope> e extrai <soap:Body> da response. DTOs ficam só com conteúdo funcional. Anote método Retrofit com @Headers("SOAPAction: ...").
Executor compartilhado:RetrofitCallExecutor (@Component @Singleton) que recebe Call<T> e devolve T, centralizando tratamento de status/body/IOException. Reduz boilerplate em gateways.
8. Erros Comuns
Erro
Causa
Correção
NoClassDefFoundError: retrofit2/Retrofit em runtime
Declarado só como implementation, sem moduleLib
Trocar para moduleLib (ou ambos em versões antigas do plugin Gradle)
IllegalArgumentException: Illegal URL no Retrofit.Builder
Base URL sem barra final
Adicionar / no fim: "https://api.x.com/"
EOFException em response com 204 No Content
Tentou desserializar corpo vazio
Use Call<Void> ou cheque response.body() == null
MalformedJsonException ao deserializar
DTO não bate com JSON
Adicionar @Json(name = "...") em campos com nome diferente
@Inject de javax.inject no interceptor
Pacote errado
com.google.inject.Inject (ver skill addon-studio)
Token expirou mid-request
Auth interceptor sem refresh
Implementar okhttp3.Authenticator separado para 401, ou refresh proativo
Timeouts disparam sob carga
Defaults OkHttp são baixos
Configurar connectTimeout/readTimeout/writeTimeout no OkHttpClient.Builder
Method not annotated with HTTP method type
Falta @GET/@POST/etc.
Adicionar verbo HTTP no método da interface
9. Checklist
Nova integração HTTP
Adicionar deps moduleLib (retrofit + converter-moshi + moshi + okhttp) no build.gradle do módulo.
Criar interface XxxApi com anotações Retrofit. Retorno Call<T>.
Criar DTOs Java 8 com Lombok (@Data) — usar @Json(name=...) quando JSON não casar.
Criar @CustomModule com @Provides @Singleton retornando o cliente.
Se múltiplas APIs no addon, considerar RetrofitClientFactory (opção B).
Criar interceptors necessários (auth, logging) como @Component @Singleton.
Gateway/adapter (@Component) injeta o client e expõe métodos de domínio.
Tratar response status, body nulo, IOException — extrair executor compartilhado se boilerplate repetir.