diff --git a/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Logging.kt b/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Logging.kt index b7a9aef..2fb382b 100644 --- a/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Logging.kt +++ b/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Logging.kt @@ -2,6 +2,7 @@ package net.woggioni.gbcs.base import org.slf4j.Logger import org.slf4j.LoggerFactory +import org.slf4j.event.Level import java.nio.file.Files import java.nio.file.Path import java.util.logging.LogManager @@ -52,6 +53,12 @@ inline fun log(log : Logger, } } +inline fun Logger.log(level : Level, messageBuilder : () -> String) { + if(isEnabledForLevel(level)) { + makeLoggingEventBuilder(level).log(messageBuilder()) + } +} + inline fun Logger.trace(messageBuilder : () -> String) { if(isTraceEnabled) { trace(messageBuilder()) diff --git a/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Xml.kt b/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Xml.kt index 6e61248..e4dcc39 100644 --- a/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Xml.kt +++ b/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Xml.kt @@ -1,6 +1,7 @@ package net.woggioni.gbcs.base import org.slf4j.LoggerFactory +import org.slf4j.event.Level import org.w3c.dom.Document import org.w3c.dom.Element import org.w3c.dom.Node @@ -80,28 +81,17 @@ class Xml(val doc: Document, val element: Element) { private val log = LoggerFactory.getLogger(ErrorHandler::class.java) } - override fun warning(ex: SAXParseException) { - log.warn( - "Problem at {}:{}:{} parsing deployment configuration: {}", - fileURL, ex.lineNumber, ex.columnNumber, ex.message - ) - } + override fun warning(ex: SAXParseException)= err(ex, Level.WARN) - override fun error(ex: SAXParseException) { - log.error( - "Problem at {}:{}:{} parsing deployment configuration: {}", - fileURL, ex.lineNumber, ex.columnNumber, ex.message - ) + private fun err(ex: SAXParseException, level: Level) { + log.log(level) { + "Problem at ${fileURL}:${ex.lineNumber}:${ex.columnNumber} parsing deployment configuration: ${ex.message}" + } throw ex } - override fun fatalError(ex: SAXParseException) { - log.error( - "Problem at {}:{}:{} parsing deployment configuration: {}", - fileURL, ex.lineNumber, ex.columnNumber, ex.message - ) - throw ex - } + override fun error(ex: SAXParseException) = err(ex, Level.ERROR) + override fun fatalError(ex: SAXParseException) = err(ex, Level.ERROR) } companion object { diff --git a/gbcs-cli/src/main/java/module-info.java b/gbcs-cli/src/main/java/module-info.java index 51539bd..e97ded5 100644 --- a/gbcs-cli/src/main/java/module-info.java +++ b/gbcs-cli/src/main/java/module-info.java @@ -6,6 +6,7 @@ module net.woggioni.gbcs.cli { requires net.woggioni.gbcs.client; requires kotlin.stdlib; requires net.woggioni.jwo; + requires net.woggioni.gbcs.api; exports net.woggioni.gbcs.cli.impl.converters to info.picocli; opens net.woggioni.gbcs.cli.impl.commands to info.picocli; diff --git a/gbcs-cli/src/main/kotlin/net/woggioni/gbcs/cli/impl/commands/ServerCommand.kt b/gbcs-cli/src/main/kotlin/net/woggioni/gbcs/cli/impl/commands/ServerCommand.kt index ac5d878..5662960 100644 --- a/gbcs-cli/src/main/kotlin/net/woggioni/gbcs/cli/impl/commands/ServerCommand.kt +++ b/gbcs-cli/src/main/kotlin/net/woggioni/gbcs/cli/impl/commands/ServerCommand.kt @@ -2,11 +2,11 @@ package net.woggioni.gbcs.cli.impl.commands import net.woggioni.gbcs.GradleBuildCacheServer import net.woggioni.gbcs.GradleBuildCacheServer.Companion.DEFAULT_CONFIGURATION_URL +import net.woggioni.gbcs.api.Configuration import net.woggioni.gbcs.base.contextLogger import net.woggioni.gbcs.base.debug import net.woggioni.gbcs.base.info import net.woggioni.gbcs.cli.impl.GbcsCommand -import net.woggioni.gbcs.client.GbcsClient import net.woggioni.jwo.Application import net.woggioni.jwo.JWO import picocli.CommandLine @@ -42,8 +42,8 @@ class ServerCommand(app : Application) : GbcsCommand() { ) private var configurationFile: Path = findConfigurationFile(app, "gbcs-server.xml") - val configuration : GbcsClient.Configuration by lazy { - GbcsClient.Configuration.parse(configurationFile) + val configuration : Configuration by lazy { + GradleBuildCacheServer.loadConfiguration(configurationFile) } override fun run() { diff --git a/gbcs-cli/src/main/resources/net/woggioni/gbcs/cli/logback.xml b/gbcs-cli/src/main/resources/net/woggioni/gbcs/cli/logback.xml index 217d42e..b3e8682 100644 --- a/gbcs-cli/src/main/resources/net/woggioni/gbcs/cli/logback.xml +++ b/gbcs-cli/src/main/resources/net/woggioni/gbcs/cli/logback.xml @@ -15,8 +15,6 @@ - - \ No newline at end of file diff --git a/gbcs-client/src/jmh/java/net/woggioni/gbcs/benchmark/Main.java b/gbcs-client/src/jmh/java/net/woggioni/gbcs/benchmark/Main.java deleted file mode 100644 index ee58d3c..0000000 --- a/gbcs-client/src/jmh/java/net/woggioni/gbcs/benchmark/Main.java +++ /dev/null @@ -1,262 +0,0 @@ -package net.woggioni.gbcs.benchmark; - -import lombok.Getter; -import lombok.SneakyThrows; -import net.woggioni.jwo.Fun; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.KeyStore; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.Random; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; - -public class Main { - - @SneakyThrows - private static Properties loadProperties() { - Properties properties = new Properties(); - try (final var is = Main.class.getResourceAsStream("/benchmark.properties")) { - properties.load(is); - } - return properties; - } - - private static final Properties properties = loadProperties(); - - @State(Scope.Thread) - public static class ExecutionPlan { - private final Random random = new Random(101325); - - @Getter - private final HttpClient client = createHttpClient(); - - private final Map entries = new HashMap<>(); - - - private HttpClient createHttpClient() { - final var clientBuilder = HttpClient.newBuilder(); - getSslContext().ifPresent(clientBuilder::sslContext); - return clientBuilder.build(); - } - - public final Map getEntries() { - return Collections.unmodifiableMap(entries); - } - - public Map.Entry newEntry() { - final var keyBuffer = new byte[0x20]; - random.nextBytes(keyBuffer); - final var key = Base64.getUrlEncoder().encodeToString(keyBuffer); - final var value = new byte[0x1000]; - random.nextBytes(value); - return Map.entry(key, value); - } - - @SneakyThrows - public HttpRequest.Builder newRequestBuilder(String key) { - final var requestBuilder = HttpRequest.newBuilder() - .uri(getServerURI().resolve(key)); - String user = getUser(); - if (user != null) { - requestBuilder.header("Authorization", buildAuthorizationHeader(user, getPassword())); - } - return requestBuilder; - } - - @SneakyThrows - public URI getServerURI() { - return new URI(properties.getProperty("gbcs.server.url")); - } - - @SneakyThrows - public Optional getClientTrustStorePassword() { - return Optional.ofNullable(properties.getProperty("gbcs.client.ssl.truststore.password")) - .filter(Predicate.not(String::isEmpty)); - - } - - @SneakyThrows - public Optional getClientTrustStore() { - return Optional.ofNullable(properties.getProperty("gbcs.client.ssl.truststore.file")) - .filter(Predicate.not(String::isEmpty)) - .map(Path::of) - .map((Fun) keyStoreFile -> { - final var keyStore = KeyStore.getInstance("PKCS12"); - try (final var is = Files.newInputStream(keyStoreFile)) { - keyStore.load(is, getClientTrustStorePassword().map(String::toCharArray).orElse(null)); - } - return keyStore; - }); - - } - - @SneakyThrows - public Optional getClientKeyStore() { - return Optional.ofNullable(properties.getProperty("gbcs.client.ssl.keystore.file")) - .filter(Predicate.not(String::isEmpty)) - .map(Path::of) - .map((Fun) keyStoreFile -> { - final var keyStore = KeyStore.getInstance("PKCS12"); - try (final var is = Files.newInputStream(keyStoreFile)) { - keyStore.load(is, getClientKeyStorePassword().map(String::toCharArray).orElse(null)); - } - return keyStore; - }); - - } - - @SneakyThrows - public Optional getClientKeyStorePassword() { - return Optional.ofNullable(properties.getProperty("gbcs.client.ssl.keystore.password")) - .filter(Predicate.not(String::isEmpty)); - - } - - @SneakyThrows - public Optional getClientKeyPassword() { - return Optional.ofNullable(properties.getProperty("gbcs.client.ssl.key.password")) - .filter(Predicate.not(String::isEmpty)); - - } - - @SneakyThrows - public String getUser() { - return Optional.ofNullable(properties.getProperty("gbcs.server.username")) - .filter(Predicate.not(String::isEmpty)) - .orElse(null); - - } - - @SneakyThrows - public String getPassword() { - return Optional.ofNullable(properties.getProperty("gbcs.server.password")) - .filter(Predicate.not(String::isEmpty)) - .orElse(null); - } - - private String buildAuthorizationHeader(String user, String password) { - final var b64 = Base64.getEncoder().encode(String.format("%s:%s", user, password).getBytes(StandardCharsets.UTF_8)); - return "Basic " + new String(b64); - } - - @SneakyThrows - private Optional getSslContext() { - return getClientKeyStore().map((Fun) clientKeyStore -> { - final var kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(clientKeyStore, getClientKeyStorePassword().map(String::toCharArray).orElse(null)); - - - // Set up trust manager factory with the truststore - final var trustManagers = getClientTrustStore().map((Fun) ts -> { - final var tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(ts); - return tmf.getTrustManagers(); - }).orElse(new TrustManager[0]); - - // Create SSL context with the key and trust managers - final var sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), trustManagers, null); - return sslContext; - }); - } - - @SneakyThrows - @Setup(Level.Trial) - public void setUp() { - final var client = getClient(); - for (int i = 0; i < 1000; i++) { - final var pair = newEntry(); - final var requestBuilder = newRequestBuilder(pair.getKey()) - .header("Content-Type", "application/octet-stream") - .PUT(HttpRequest.BodyPublishers.ofByteArray(pair.getValue())); - final var response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString()); - if (201 != response.statusCode()) { - throw new IllegalStateException(Integer.toString(response.statusCode())); - } else { - entries.put(pair.getKey(), pair.getValue()); - } - } - } - - @TearDown - public void tearDown() { - client.close(); - } - - - private Iterator> it = null; - - private Map.Entry nextEntry() { - if (it == null || !it.hasNext()) { - it = getEntries().entrySet().iterator(); - } - return it.next(); - } - } - - @SneakyThrows - @Benchmark - @BenchmarkMode(Mode.Throughput) - @OutputTimeUnit(TimeUnit.SECONDS) - public void get(ExecutionPlan plan) { - final var client = plan.getClient(); - final var entry = plan.nextEntry(); - final var requestBuilder = plan.newRequestBuilder(entry.getKey()) - .header("Accept", "application/octet-stream") - .GET(); - final var response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray()); - if (200 != response.statusCode()) { - throw new IllegalStateException(Integer.toString(response.statusCode())); - } else { - if (!Arrays.equals(entry.getValue(), response.body())) { - throw new IllegalStateException("Retrieved unexpected value"); - } - } - } - - - @SneakyThrows - @Benchmark - @BenchmarkMode(Mode.Throughput) - @OutputTimeUnit(TimeUnit.SECONDS) - public void put(Main.ExecutionPlan plan) { - final var client = plan.getClient(); - final var entry = plan.nextEntry(); - - final var requestBuilder = plan.newRequestBuilder(entry.getKey()) - .header("Content-Type", "application/octet-stream") - .PUT(HttpRequest.BodyPublishers.ofByteArray(entry.getValue())); - - final var response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray()); - if (201 != response.statusCode()) { - throw new IllegalStateException(Integer.toString(response.statusCode())); - } - } -} diff --git a/gbcs-client/src/jmh/resources/benchmark.properties b/gbcs-client/src/jmh/resources/benchmark.properties deleted file mode 100644 index aa7d29e..0000000 --- a/gbcs-client/src/jmh/resources/benchmark.properties +++ /dev/null @@ -1,6 +0,0 @@ -gbcs.server.url= https://gbcs.woggioni.net:443 -gbcs.client.ssl.keystore.file=conf/woggioni@c962475fa38.p12 -gbcs.client.ssl.keystore.password=password -gbcs.client.ssl.key.password=password -gbcs.client.ssl.truststore.file=conf/truststore.pfx -gbcs.client.ssl.truststore.password=password \ No newline at end of file diff --git a/src/main/kotlin/net/woggioni/gbcs/GradleBuildCacheServer.kt b/src/main/kotlin/net/woggioni/gbcs/GradleBuildCacheServer.kt index bd4b92d..533d674 100644 --- a/src/main/kotlin/net/woggioni/gbcs/GradleBuildCacheServer.kt +++ b/src/main/kotlin/net/woggioni/gbcs/GradleBuildCacheServer.kt @@ -507,9 +507,9 @@ class GradleBuildCacheServer(private val cfg: Configuration) { val DEFAULT_CONFIGURATION_URL by lazy { "classpath:net/woggioni/gbcs/gbcs-default.xml".toUrl() } fun loadConfiguration(configurationFile: Path): Configuration { - val dbf = Xml.newDocumentBuilderFactory(null) - val db = dbf.newDocumentBuilder() - val doc = Files.newInputStream(configurationFile).use(db::parse) + val doc = Files.newInputStream(configurationFile).use { + Xml.parseXml(configurationFile.toUri().toURL(), it) + } return Parser.parse(doc) } diff --git a/src/main/kotlin/net/woggioni/gbcs/configuration/Parser.kt b/src/main/kotlin/net/woggioni/gbcs/configuration/Parser.kt index 10f886e..d969838 100644 --- a/src/main/kotlin/net/woggioni/gbcs/configuration/Parser.kt +++ b/src/main/kotlin/net/woggioni/gbcs/configuration/Parser.kt @@ -19,7 +19,6 @@ import org.w3c.dom.TypeInfo import java.nio.file.Paths object Parser { - fun parse(document: Document): Configuration { val root = document.documentElement val anonymousUser = User("", null, emptySet())