diff --git a/.mvn/maven.config b/.mvn/maven.config
new file mode 100644
index 0000000..ebbe288
--- /dev/null
+++ b/.mvn/maven.config
@@ -0,0 +1 @@
+-T 1C
diff --git a/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/config/ExternalServiceConfig.java b/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/config/ExternalServiceConfig.java
index 097b91a..d0d79d6 100644
--- a/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/config/ExternalServiceConfig.java
+++ b/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/config/ExternalServiceConfig.java
@@ -1,29 +1,20 @@
package org.opendevstack.apiservice.externalservice.aap.config;
+import lombok.extern.slf4j.Slf4j;
+import org.opendevstack.apiservice.externalservice.api.http.RestClientFactory;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.util.StringUtils;
-import org.springframework.web.client.RestTemplate;
-
-import lombok.extern.slf4j.Slf4j;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.security.GeneralSecurityException;
-import java.security.cert.X509Certificate;
+import org.springframework.web.client.RestClient;
/**
- * Configuration class for external service components.
+ * Configuration class for the Ansible Automation Platform external service.
+ *
+ *
SSL wiring is delegated to {@link RestClientFactory} in {@code external-service-api};
+ * no SSL boilerplate lives here.
*/
@Configuration
@EnableAsync
@@ -33,85 +24,25 @@ public class ExternalServiceConfig {
private final SslProperties sslProperties;
+ @Value("${automation.platform.ansible.timeout:30000}")
+ private int timeoutMs;
+
public ExternalServiceConfig(@Qualifier("aapSslProperties") SslProperties sslProperties) {
this.sslProperties = sslProperties;
}
/**
- * Creates a RestTemplate bean for HTTP client operations with configurable SSL settings.
+ * {@link RestClient} bean for the Ansible Automation Platform.
+ *
+ *
SSL and timeouts are configured via {@code automation.platform.ansible.ssl.*}
+ * and {@code automation.platform.ansible.timeout} properties respectively.
*
- * @return RestTemplate instance with SSL configuration
+ * @param builder Spring prototype builder (injected fresh per bean definition)
+ * @return configured {@link RestClient}
*/
@Bean
- public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
- if (!sslProperties.isVerifyCertificates()) {
- log.warn("SSL certificate verification is DISABLED - this should only be used in development environments");
- return createInsecureRestTemplate();
- } else {
- log.info("SSL certificate verification is ENABLED");
- return createSecureRestTemplate();
- }
- }
-
- private RestTemplate createInsecureRestTemplate() {
- try {
- // Create a trust manager that accepts all certificates
- // WARNING: This is insecure and should only be used in development environments
- TrustManager[] trustAllCerts = new TrustManager[] {
- new X509TrustManager() {
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0]; // Return empty array instead of null
- }
- public void checkClientTrusted(X509Certificate[] certs, String authType) {
- // Intentionally empty - accepts all client certificates (insecure)
- }
- public void checkServerTrusted(X509Certificate[] certs, String authType) {
- // Intentionally empty - accepts all server certificates (insecure)
- }
- }
- };
-
- // Install the all-trusting trust manager
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
-
- // Create hostname verifier that accepts all hostnames (insecure)
- HostnameVerifier allHostsValid = (hostname, session) -> true;
-
- // Create a custom request factory that uses our SSL configuration
- SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory() {
- @Override
- protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
- if (connection instanceof HttpsURLConnection httpsConnection) {
- httpsConnection.setSSLSocketFactory(sslContext.getSocketFactory());
- httpsConnection.setHostnameVerifier(allHostsValid);
- }
- super.prepareConnection(connection, httpMethod);
- }
- };
-
- return new RestTemplate(requestFactory);
-
- } catch (GeneralSecurityException e) {
- log.warn("Failed to create insecure RestTemplate, falling back to default: {}", e.getMessage());
- return new RestTemplate();
- }
- }
-
- private RestTemplate createSecureRestTemplate() {
- try {
- // If custom trust store is provided, configure it
- if (StringUtils.hasText(sslProperties.getTrustStorePath())) {
- log.info("Custom trust store specified: {} (custom trust store support can be added in future versions)",
- sslProperties.getTrustStorePath());
- }
-
- // Return default RestTemplate with system SSL settings
- return new RestTemplate();
-
- } catch (Exception e) {
- log.warn("Failed to create secure RestTemplate with custom trust store, using default: {}", e.getMessage());
- return new RestTemplate();
- }
+ public RestClient aapRestClient(RestClient.Builder builder) {
+ log.info("Creating AAP RestClient (connect/read timeout={}ms)", timeoutMs);
+ return RestClientFactory.build(builder, sslProperties, timeoutMs, timeoutMs);
}
}
diff --git a/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/config/SslProperties.java b/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/config/SslProperties.java
index 4bd4e94..c2a7f1d 100644
--- a/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/config/SslProperties.java
+++ b/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/config/SslProperties.java
@@ -1,67 +1,16 @@
package org.opendevstack.apiservice.externalservice.aap.config;
+import org.opendevstack.apiservice.externalservice.api.http.ExternalServiceSslProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
- * Configuration properties for SSL settings in external service calls.
+ * SSL configuration properties for the Ansible Automation Platform external service.
+ * Binds to the {@code automation.platform.ansible.ssl} prefix.
*/
@Component("aapSslProperties")
@ConfigurationProperties(prefix = "automation.platform.ansible.ssl")
-public class SslProperties {
-
- /**
- * Whether to verify SSL certificates when making external service calls.
- * Default is true for security.
- */
- private boolean verifyCertificates = true;
-
- /**
- * Path to the trust store file for SSL certificate validation.
- * Optional - if not provided, uses system default trust store.
- */
- private String trustStorePath;
-
- /**
- * Password for the trust store.
- */
- private String trustStorePassword;
-
- /**
- * Type of the trust store (JKS, PKCS12, etc.).
- * Default is JKS.
- */
- private String trustStoreType = "JKS";
-
- public boolean isVerifyCertificates() {
- return verifyCertificates;
- }
-
- public void setVerifyCertificates(boolean verifyCertificates) {
- this.verifyCertificates = verifyCertificates;
- }
-
- public String getTrustStorePath() {
- return trustStorePath;
- }
-
- public void setTrustStorePath(String trustStorePath) {
- this.trustStorePath = trustStorePath;
- }
-
- public String getTrustStorePassword() {
- return trustStorePassword;
- }
-
- public void setTrustStorePassword(String trustStorePassword) {
- this.trustStorePassword = trustStorePassword;
- }
-
- public String getTrustStoreType() {
- return trustStoreType;
- }
-
- public void setTrustStoreType(String trustStoreType) {
- this.trustStoreType = trustStoreType;
- }
-}
\ No newline at end of file
+public class SslProperties extends ExternalServiceSslProperties {
+ // All fields inherited from ExternalServiceSslProperties.
+ // Add AAP-specific SSL overrides here if ever needed.
+}
diff --git a/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/service/impl/AnsibleAutomationPlatformService.java b/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/service/impl/AnsibleAutomationPlatformService.java
index 7a3395d..dc834f6 100644
--- a/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/service/impl/AnsibleAutomationPlatformService.java
+++ b/external-service-aap/src/main/java/org/opendevstack/apiservice/externalservice/aap/service/impl/AnsibleAutomationPlatformService.java
@@ -1,22 +1,20 @@
package org.opendevstack.apiservice.externalservice.aap.service.impl;
+import lombok.extern.slf4j.Slf4j;
import org.opendevstack.apiservice.externalservice.aap.exception.AutomationPlatformException;
import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult;
import org.opendevstack.apiservice.externalservice.aap.model.AutomationJobStatus;
import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.ResponseEntity;
+import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriUtils;
-import lombok.extern.slf4j.Slf4j;
-
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@@ -25,13 +23,13 @@
/**
* Implementation of AutomationPlatformService for Ansible Automation Platform.
- * This service provides integration with Ansible AWX/Tower for executing workflows and modules.
+ * Provides integration with Ansible AWX/Tower for executing workflows and modules.
*/
@Service("automationPlatformService")
@Slf4j
public class AnsibleAutomationPlatformService implements AutomationPlatformService {
- private final RestTemplate restTemplate;
+ private final RestClient restClient;
@Value("${automation.platform.ansible.base-url:http://localhost:8080/api/v2}")
private String baseUrl;
@@ -42,46 +40,45 @@ public class AnsibleAutomationPlatformService implements AutomationPlatformServi
@Value("${automation.platform.ansible.password:password}")
private String password;
- @Value("${automation.platform.ansible.timeout:30000}")
- private int timeoutMs;
-
- public AnsibleAutomationPlatformService(RestTemplate restTemplate) {
- this.restTemplate = restTemplate;
+ public AnsibleAutomationPlatformService(RestClient aapRestClient) {
+ this.restClient = aapRestClient;
}
@Override
- public AutomationExecutionResult executeWorkflow(String workflowName, Map parameters) throws AutomationPlatformException {
+ public AutomationExecutionResult executeWorkflow(String workflowName, Map parameters)
+ throws AutomationPlatformException {
log.info("Executing workflow '{}' with parameters: {}", workflowName, parameters);
-
+
try {
- // Create headers with authentication
- HttpHeaders headers = createAuthHeaders();
-
- // Prepare request body
Map requestBody = new HashMap<>();
requestBody.put("extra_vars", parameters);
-
- HttpEntity