001/* 002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.benchmarking.common.base; 017 018import de.cuioss.benchmarking.common.http.HttpClientFactory; 019import de.cuioss.tools.logging.CuiLogger; 020 021import java.io.IOException; 022import java.net.URI; 023import java.net.http.HttpClient; 024import java.net.http.HttpRequest; 025import java.net.http.HttpResponse; 026import java.time.Duration; 027 028/** 029 * Abstract base class for all benchmarks in the OAuth Sheriff project. 030 * Provides common functionality and fields to reduce code duplication 031 * across different benchmark implementations. 032 * 033 * <p>This class provides: 034 * <ul> 035 * <li>Logging infrastructure</li> 036 * <li>HTTP client management</li> 037 * <li>Common configuration fields</li> 038 * <li>Utility methods for HTTP operations</li> 039 * <li>Metrics export hooks</li> 040 * </ul> 041 * 042 * @author Oliver Wolff 043 */ 044public abstract class AbstractBenchmarkBase { 045 046 static { 047 // Set the logging manager to prevent JBoss LogManager errors in forked JVMs 048 System.setProperty("java.util.logging.manager", "java.util.logging.LogManager"); 049 } 050 051 private static final CuiLogger LOGGER = new CuiLogger(AbstractBenchmarkBase.class); 052 053 protected String serviceUrl; 054 protected String benchmarkResultsDir; 055 protected HttpClient httpClient; 056 057 058 /** 059 * Base setup method that initializes common resources. 060 * Subclasses should call this from their @Setup method. 061 * 062 * <p>This method: 063 * <ul> 064 * <li>Initializes the benchmark results directory</li> 065 * <li>Performs additional setup via template method</li> 066 * <li>Creates the HTTP client after serviceUrl is set</li> 067 * <li>Performs post-initialization setup after HTTP client is ready</li> 068 * </ul> 069 */ 070 protected void setupBase() { 071 // Initialize benchmark results directory 072 benchmarkResultsDir = System.getProperty("benchmark.results.dir", "target/benchmark-results"); 073 074 // Call template method for subclass-specific setup (sets serviceUrl) 075 performAdditionalSetup(); 076 077 // Initialize HttpClient AFTER serviceUrl has been set 078 initializeHttpClient(); 079 080 LOGGER.debug("Base benchmark setup completed"); 081 } 082 083 /** 084 * Initialize the HTTP client. This is called after performAdditionalSetup() 085 * to allow serviceUrl to be set first for URL-based client caching. 086 * Subclasses can override this to customize HTTP client initialization. 087 */ 088 protected void initializeHttpClient() { 089 httpClient = HttpClientFactory.getInsecureClient(); 090 LOGGER.debug("Using shared Java HttpClient"); 091 } 092 093 /** 094 * Template method for subclasses to perform additional setup. 095 * Override this method to add specific initialization logic. 096 */ 097 protected abstract void performAdditionalSetup(); 098 099 /** 100 * Teardown method called after benchmark execution. 101 * Subclasses should call this from their @TearDown method 102 * or override to add additional cleanup logic. 103 */ 104 protected void tearDown() { 105 LOGGER.debug("Benchmark teardown initiated"); 106 107 // Call template method for subclass-specific teardown 108 performAdditionalTeardown(); 109 110 LOGGER.debug("Benchmark teardown completed"); 111 } 112 113 /** 114 * Template method for subclasses to perform additional teardown. 115 * Override this method to add specific cleanup logic. 116 */ 117 protected void performAdditionalTeardown() { 118 // Default implementation does nothing 119 } 120 121 /** 122 * Creates a basic HTTP request builder with common headers. 123 * 124 * @param url the full URL to send the request to 125 * @return configured request builder 126 */ 127 protected HttpRequest.Builder createBaseRequest(String url) { 128 return HttpRequest.newBuilder() 129 .uri(URI.create(url)) 130 .header("Content-Type", "application/json") 131 .header("Accept", "application/json") 132 .timeout(Duration.ofSeconds(30)); 133 } 134 135 /** 136 * Creates a basic HTTP request builder with common headers using a base URL and path. 137 * 138 * @param baseUrl the base URL 139 * @param path the path to append to the base URL 140 * @return configured request builder 141 */ 142 protected HttpRequest.Builder createBaseRequest(String baseUrl, String path) { 143 return createBaseRequest(baseUrl + path); 144 } 145 146 /** 147 * Sends an HTTP request and returns the response. 148 * 149 * @param request the request to send 150 * @return the HTTP response 151 * @throws IOException if an I/O error occurs 152 * @throws InterruptedException if the operation is interrupted 153 */ 154 protected HttpResponse<String> sendRequest(HttpRequest request) throws IOException, InterruptedException { 155 if (httpClient == null) { 156 throw new IllegalStateException("HTTP client not initialized. Ensure setupBase() was called."); 157 } 158 159 // Send request using Java HttpClient 160 return httpClient.send(request, HttpResponse.BodyHandlers.ofString()); 161 } 162 163 /** 164 * Utility method to handle common error scenarios in benchmarks. 165 * 166 * @param response the response to check 167 * @param expectedStatus the expected HTTP status code 168 * @throws IllegalStateException if the response status doesn't match expected 169 */ 170 protected void validateResponse(HttpResponse<String> response, int expectedStatus) { 171 if (response.statusCode() != expectedStatus) { 172 throw new IllegalStateException("Expected status %d but got %d. Response: %s".formatted( 173 expectedStatus, response.statusCode(), response.body())); 174 } 175 } 176 177 /** 178 * Export metrics at the end of benchmark execution. 179 * Subclasses should override this method to implement specific metrics export logic. 180 */ 181 public abstract void exportBenchmarkMetrics(); 182}