Apereo CAS Core Utilities - A comprehensive utility library providing functional programming constructs, encryption utilities, configuration helpers, and core infrastructure components for the Central Authentication Service framework
—
Robust HTTP client implementations with factory patterns, request/response utilities, and integration with Apache HttpClient for reliable network communications.
Core interface defining HTTP client behavior with support for various messaging patterns.
public interface HttpClient {
// Bean names for different client configurations
String BEAN_NAME_HTTPCLIENT_TRUST_STORE = "httpClientTrustStore";
String BEAN_NAME_HTTPCLIENT_NO_REDIRECT = "httpClientNoRedirect";
String BEAN_NAME_HTTPCLIENT = "httpClient";
// Core messaging operations
boolean sendMessageToEndPoint(HttpMessage message);
HttpMessage sendMessageToEndPoint(URL url);
// Endpoint validation
boolean isValidEndPoint(String url);
boolean isValidEndPoint(URL url);
// Access to underlying client
org.apache.hc.client5.http.classic.HttpClient wrappedHttpClient();
// Factory access
HttpClientFactory httpClientFactory();
}Basic HTTP operations:
@Service
public class ExternalApiService {
private final HttpClient httpClient;
public ExternalApiService(@Qualifier("httpClient") HttpClient httpClient) {
this.httpClient = httpClient;
}
public boolean notifyEndpoint(String endpoint, String payload) {
try {
URL url = new URL(endpoint);
// Validate endpoint first
if (!httpClient.isValidEndPoint(url)) {
log.warn("Invalid endpoint: {}", endpoint);
return false;
}
// Create and send message
HttpMessage message = new HttpMessage(url, "POST");
message.setContentType("application/json");
message.setEntity(payload);
return httpClient.sendMessageToEndPoint(message);
} catch (Exception e) {
log.error("Failed to send notification", e);
return false;
}
}
public String fetchData(String endpoint) {
try {
URL url = new URL(endpoint);
HttpMessage response = httpClient.sendMessageToEndPoint(url);
return response != null ? response.getMessage() : null;
} catch (Exception e) {
log.error("Failed to fetch data from {}", endpoint, e);
return null;
}
}
}Simple HTTP client implementation providing basic HTTP operations with reasonable defaults.
public class SimpleHttpClient implements HttpClient {
// Constructors
public SimpleHttpClient();
public SimpleHttpClient(HttpClientFactory httpClientFactory);
// HttpClient interface implementation
@Override
public boolean sendMessageToEndPoint(HttpMessage message);
@Override
public HttpMessage sendMessageToEndPoint(URL url);
@Override
public boolean isValidEndPoint(String url);
@Override
public boolean isValidEndPoint(URL url);
@Override
public org.apache.hc.client5.http.classic.HttpClient wrappedHttpClient();
@Override
public HttpClientFactory httpClientFactory();
}Simple HTTP client configuration:
@Configuration
public class HttpClientConfiguration {
@Bean
@Primary
public HttpClient defaultHttpClient() {
return new SimpleHttpClient();
}
@Bean("httpClientNoRedirect")
public HttpClient noRedirectHttpClient() {
HttpClientFactory factory = createNoRedirectFactory();
return new SimpleHttpClient(factory);
}
private HttpClientFactory createNoRedirectFactory() {
return new SimpleHttpClientFactory() {
@Override
public org.apache.hc.client5.http.classic.HttpClient getHttpClient() {
return HttpClients.custom()
.disableRedirectHandling()
.build();
}
};
}
}Spring factory bean for creating and configuring HTTP clients in application contexts.
public class SimpleHttpClientFactoryBean implements FactoryBean<HttpClient>,
InitializingBean,
DisposableBean {
// Configuration properties
public void setConnectionTimeout(int connectionTimeout);
public void setReadTimeout(int readTimeout);
public void setMaxTotalConnections(int maxTotalConnections);
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute);
// SSL configuration
public void setTrustManagerFactory(TrustManagerFactory trustManagerFactory);
public void setSslContext(SSLContext sslContext);
public void setHostnameVerifier(HostnameVerifier hostnameVerifier);
// Proxy configuration
public void setProxyHost(String proxyHost);
public void setProxyPort(int proxyPort);
public void setProxyCredentials(Credentials proxyCredentials);
// FactoryBean implementation
@Override
public HttpClient getObject() throws Exception;
@Override
public Class<?> getObjectType();
@Override
public boolean isSingleton();
// Lifecycle methods
@Override
public void afterPropertiesSet() throws Exception;
@Override
public void destroy() throws Exception;
}Spring configuration with factory bean:
@Configuration
public class HttpClientConfiguration {
@Bean
public SimpleHttpClientFactoryBean httpClientFactory() {
SimpleHttpClientFactoryBean factory = new SimpleHttpClientFactoryBean();
// Connection settings
factory.setConnectionTimeout(30000); // 30 seconds
factory.setReadTimeout(60000); // 60 seconds
factory.setMaxTotalConnections(100);
factory.setMaxConnectionsPerRoute(20);
// Proxy settings if needed
factory.setProxyHost("proxy.example.com");
factory.setProxyPort(8080);
return factory;
}
@Bean
public HttpClient httpClient(SimpleHttpClientFactoryBean factory) throws Exception {
return factory.getObject();
}
}SSL configuration:
@Configuration
public class SecureHttpClientConfiguration {
@Bean
public SimpleHttpClientFactoryBean secureHttpClientFactory() throws Exception {
SimpleHttpClientFactoryBean factory = new SimpleHttpClientFactoryBean();
// SSL configuration
SSLContext sslContext = createCustomSSLContext();
factory.setSslContext(sslContext);
factory.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
return factory;
}
private SSLContext createCustomSSLContext() throws Exception {
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
KeyStore trustStore = loadTrustStore();
tmf.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
return sslContext;
}
}Factory interface for creating HTTP client instances with customizable configurations.
public interface HttpClientFactory {
// Core factory method
org.apache.hc.client5.http.classic.HttpClient getHttpClient();
// Configuration methods
void configure(HttpClientBuilder builder);
void configureConnectionManager(PoolingHttpClientConnectionManager connectionManager);
void configureRequestConfig(RequestConfig.Builder requestConfigBuilder);
}Wrapper class for HTTP execution requests providing additional context and configuration.
public class HttpExecutionRequest {
// Request properties
private final HttpUriRequest request;
private final HttpContext context;
private final ResponseHandler<String> responseHandler;
// Constructor
public HttpExecutionRequest(HttpUriRequest request);
public HttpExecutionRequest(HttpUriRequest request, HttpContext context);
public HttpExecutionRequest(HttpUriRequest request,
HttpContext context,
ResponseHandler<String> responseHandler);
// Getters
public HttpUriRequest getRequest();
public HttpContext getContext();
public ResponseHandler<String> getResponseHandler();
}Custom HTTP execution:
@Service
public class AdvancedHttpService {
private final HttpClient httpClient;
public String executeRequest(String url, Map<String, String> headers) {
try {
// Create request
HttpGet request = new HttpGet(url);
headers.forEach(request::setHeader);
// Create context
HttpContext context = new BasicHttpContext();
context.setAttribute(HttpClientContext.REQUEST_CONFIG,
RequestConfig.custom()
.setSocketTimeout(30000)
.setConnectTimeout(10000)
.build());
// Custom response handler
ResponseHandler<String> handler = response -> {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
return EntityUtils.toString(response.getEntity());
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
// Execute with wrapper
HttpExecutionRequest execRequest = new HttpExecutionRequest(request, context, handler);
return executeHttpRequest(execRequest);
} catch (Exception e) {
log.error("Failed to execute HTTP request", e);
throw new RuntimeException("HTTP request failed", e);
}
}
private String executeHttpRequest(HttpExecutionRequest execRequest) throws Exception {
org.apache.hc.client5.http.classic.HttpClient client = httpClient.wrappedHttpClient();
return client.execute(execRequest.getRequest(),
execRequest.getContext(),
execRequest.getResponseHandler());
}
}Utility methods for HTTP request processing and manipulation.
@UtilityClass
public class HttpRequestUtils {
// Request extraction utilities
public static String getRequestParameter(HttpServletRequest request, String name);
public static String getRequestParameter(HttpServletRequest request, String name, String defaultValue);
// Header utilities
public static String getRequestHeader(HttpServletRequest request, String name);
public static Map<String, String> getRequestHeaders(HttpServletRequest request);
// URL utilities
public static String getFullRequestURL(HttpServletRequest request);
public static String getRequestURL(HttpServletRequest request, boolean includeQueryString);
// Client information
public static String getClientIpAddress(HttpServletRequest request);
public static String getUserAgent(HttpServletRequest request);
// Request validation
public static boolean isAjaxRequest(HttpServletRequest request);
public static boolean isMultipartRequest(HttpServletRequest request);
}HTTP request utilities:
@RestController
public class ApiController {
@PostMapping("/api/data")
public ResponseEntity<?> handleRequest(HttpServletRequest request) {
// Extract request information
String clientIp = HttpRequestUtils.getClientIpAddress(request);
String userAgent = HttpRequestUtils.getUserAgent(request);
String fullUrl = HttpRequestUtils.getFullRequestURL(request);
// Get parameters with defaults
String format = HttpRequestUtils.getRequestParameter(request, "format", "json");
// Check request type
boolean isAjax = HttpRequestUtils.isAjaxRequest(request);
boolean isMultipart = HttpRequestUtils.isMultipartRequest(request);
// Process based on request characteristics
if (isAjax) {
return handleAjaxRequest(request);
} else if (isMultipart) {
return handleMultipartRequest(request);
}
return handleStandardRequest(request);
}
@GetMapping("/api/info")
public Map<String, Object> getRequestInfo(HttpServletRequest request) {
return Map.of(
"clientIp", HttpRequestUtils.getClientIpAddress(request),
"userAgent", HttpRequestUtils.getUserAgent(request),
"fullUrl", HttpRequestUtils.getFullRequestURL(request),
"headers", HttpRequestUtils.getRequestHeaders(request),
"isAjax", HttpRequestUtils.isAjaxRequest(request)
);
}
}General HTTP utilities for common HTTP operations and validations.
@UtilityClass
public class HttpUtils {
// URL validation and manipulation
public static boolean isValidHttpUrl(String url);
public static URL createURL(String url);
public static URI createURI(String uri);
// Content type utilities
public static boolean isJsonContentType(String contentType);
public static boolean isXmlContentType(String contentType);
public static boolean isFormContentType(String contentType);
// Response utilities
public static void setNoCacheHeaders(HttpServletResponse response);
public static void setCorsHeaders(HttpServletResponse response);
public static void setContentType(HttpServletResponse response, String contentType);
// Status code utilities
public static boolean isSuccessfulStatus(int statusCode);
public static boolean isRedirectStatus(int statusCode);
public static boolean isErrorStatus(int statusCode);
}HTTP utilities in action:
@Component
public class HttpUtilityService {
public boolean validateAndProcessUrl(String urlString) {
// Validate URL
if (!HttpUtils.isValidHttpUrl(urlString)) {
log.warn("Invalid URL provided: {}", urlString);
return false;
}
try {
URL url = HttpUtils.createURL(urlString);
return processValidUrl(url);
} catch (Exception e) {
log.error("Failed to create URL", e);
return false;
}
}
public void configureResponse(HttpServletResponse response, String content, String format) {
// Set appropriate content type
if ("json".equalsIgnoreCase(format)) {
HttpUtils.setContentType(response, "application/json");
} else if ("xml".equalsIgnoreCase(format)) {
HttpUtils.setContentType(response, "application/xml");
}
// Configure caching and CORS
HttpUtils.setNoCacheHeaders(response);
HttpUtils.setCorsHeaders(response);
// Write response
try {
response.getWriter().write(content);
} catch (IOException e) {
log.error("Failed to write response", e);
}
}
public void handleHttpStatus(int statusCode, String operation) {
if (HttpUtils.isSuccessfulStatus(statusCode)) {
log.info("Operation {} completed successfully: {}", operation, statusCode);
} else if (HttpUtils.isRedirectStatus(statusCode)) {
log.info("Operation {} resulted in redirect: {}", operation, statusCode);
} else if (HttpUtils.isErrorStatus(statusCode)) {
log.error("Operation {} failed with status: {}", operation, statusCode);
throw new HttpStatusException(statusCode, "HTTP operation failed");
}
}
}@Service
@Slf4j
public class ExternalServiceClient {
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
public ExternalServiceClient(HttpClient httpClient, ObjectMapper objectMapper) {
this.httpClient = httpClient;
this.objectMapper = objectMapper;
}
public <T> T get(String url, Class<T> responseType) {
// Validate endpoint
if (!httpClient.isValidEndPoint(url)) {
throw new IllegalArgumentException("Invalid endpoint: " + url);
}
try {
// Send GET request
HttpMessage response = httpClient.sendMessageToEndPoint(new URL(url));
if (response != null && response.getMessage() != null) {
return objectMapper.readValue(response.getMessage(), responseType);
}
return null;
} catch (Exception e) {
log.error("GET request failed for URL: {}", url, e);
throw new RuntimeException("HTTP GET failed", e);
}
}
public <T> boolean post(String url, T request) {
try {
// Validate endpoint
if (!HttpUtils.isValidHttpUrl(url)) {
log.warn("Invalid URL: {}", url);
return false;
}
// Create message
HttpMessage message = new HttpMessage(new URL(url), "POST");
message.setContentType("application/json");
// Serialize request body
String json = objectMapper.writeValueAsString(request);
message.setEntity(json);
// Send request
return httpClient.sendMessageToEndPoint(message);
} catch (Exception e) {
log.error("POST request failed for URL: {}", url, e);
return false;
}
}
public boolean ping(String url) {
return httpClient.isValidEndPoint(url);
}
}This HTTP client library provides a comprehensive foundation for HTTP communications in CAS applications, with support for various configuration patterns, SSL/TLS, proxies, and integration with Spring Framework.
Install with Tessl CLI
npx tessl i tessl/maven-org-apereo-cas--cas-server-core-util-api