diff --git a/build.gradle b/build.gradle index 64da9f2c..29f24efd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,135 +1,10 @@ rootProject.version = "10.0.alpha1" group = 'javasabr.rlib' -subprojects { - +allprojects { repositories { mavenCentral() } - - apply plugin: "java-library" - apply plugin: "jacoco" - apply plugin: "jacoco-report-aggregation" - apply plugin: "java-test-fixtures" - apply plugin: 'maven-publish' - - java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } - } - - javadoc { - failOnError = false - } - - test { - useJUnitPlatform() - failOnNoDiscoveredTests = false - } - - dependencies { - compileOnly libs.jspecify - compileOnly libs.lombok - annotationProcessor libs.lombok - - testImplementation libs.junit.api - testImplementation libs.junit.jupiter.params - testCompileOnly libs.lombok - testCompileOnly libs.jspecify - testRuntimeOnly libs.junit.engine - testRuntimeOnly libs.junit.platform.launcher - testAnnotationProcessor libs.lombok - } - - /*compileJava { - inputs.property("moduleName", jar.baseName) - doFirst { - options.compilerArgs = [ - '--module-path', classpath.asPath, - ] - classpath = files() - } - }*/ - - tasks.withType(JavaCompile).configureEach { - options.encoding = "UTF-8" - } - - tasks.withType(Javadoc).configureEach { - options.encoding = "UTF-8" - } - - tasks.register("sourcesJar", Jar) { - dependsOn "classes" - group "build" - archiveClassifier = "sources" - archiveBaseName = jar.archiveBaseName - from sourceSets.main.allSource - } - - tasks.register("javadocJar", Jar) { - dependsOn "javadoc" - group "build" - archiveClassifier = "javadoc" - archiveBaseName = jar.archiveBaseName - from sourceSets.main.allSource - } - - publishing { - repositories { - maven { - name = "GitlabPackages" - url = uri("https://gitlab.com/api/v4/projects/37512056/packages/maven") - credentials(HttpHeaderCredentials) { - name = "Private-Token" - value = project.findProperty("gitlab.token") ?: System.getenv("GITLAB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } - - publications { - mavenJava(MavenPublication) { - from components.java - version = rootProject.version - afterEvaluate { - artifactId = jar.archiveBaseName.get() - groupId = rootProject.group - } - artifact sourcesJar - artifact javadocJar - } - } - } - - configurations { - testArtifacts.extendsFrom testRuntime - } - - tasks.register('testJar', Jar) { - archiveClassifier = "test" - from sourceSets.test.output - } - - artifacts { - testArtifacts testJar - } - - tasks.withType(Test).configureEach { - maxParallelForks = Runtime.runtime.availableProcessors() - } - - jacocoTestReport { - dependsOn test - reports { - xml.required = false - csv.required = false - html.outputLocation = layout.buildDirectory.dir('jacocoHtml') - } - } } wrapper { diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 00000000..3a85dcfe --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,8 @@ +plugins { + id 'groovy-gradle-plugin' +} + +repositories { + mavenCentral() + gradlePluginPortal() +} \ No newline at end of file diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle new file mode 100644 index 00000000..717dd794 --- /dev/null +++ b/buildSrc/settings.gradle @@ -0,0 +1,3 @@ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +rootProject.name = 'rlib-build-configuration' \ No newline at end of file diff --git a/buildSrc/src/main/groovy/configure-jacoco.gradle b/buildSrc/src/main/groovy/configure-jacoco.gradle new file mode 100644 index 00000000..3e77f6c3 --- /dev/null +++ b/buildSrc/src/main/groovy/configure-jacoco.gradle @@ -0,0 +1,12 @@ +plugins { + id "jacoco" +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = false + csv.required = false + html.outputLocation = layout.buildDirectory.dir('jacocoHtml') + } +} diff --git a/buildSrc/src/main/groovy/configure-java.gradle b/buildSrc/src/main/groovy/configure-java.gradle new file mode 100644 index 00000000..f59e6e5e --- /dev/null +++ b/buildSrc/src/main/groovy/configure-java.gradle @@ -0,0 +1,110 @@ +plugins { + id("java-library") + id("java-test-fixtures") + id("configure-jacoco") +} + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +sourceSets { + loadTest { + compileClasspath += main.output + compileClasspath += main.compileClasspath + compileClasspath += test.compileClasspath + runtimeClasspath += main.output + runtimeClasspath += main.compileClasspath + runtimeClasspath += test.compileClasspath + java { + srcDirs = ["src/loadTest/java"] + } + } +} + +javadoc { + failOnError = false +} + +test { + useJUnitPlatform() + failOnNoDiscoveredTests = false +} + +tasks.register("loadTest", Test) { + group("verification") + useJUnitPlatform() + failOnNoDiscoveredTests = false + testClassesDirs = sourceSets.loadTest.output.classesDirs + classpath = sourceSets.loadTest.runtimeClasspath + minHeapSize = "128m" + maxHeapSize = "4G" +} + +dependencies { + compileOnly libs.jspecify + compileOnly libs.lombok + annotationProcessor libs.lombok + + testImplementation libs.junit.api + testImplementation libs.junit.jupiter.params + + testCompileOnly libs.lombok + testCompileOnly libs.jspecify + testRuntimeOnly libs.junit.engine + testRuntimeOnly libs.junit.platform.launcher + testAnnotationProcessor libs.lombok + + loadTestCompileOnly libs.lombok + loadTestCompileOnly libs.jspecify + loadTestRuntimeOnly libs.junit.engine + loadTestRuntimeOnly libs.junit.platform.launcher + loadTestAnnotationProcessor libs.lombok +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = "UTF-8" +} + +tasks.withType(Javadoc).configureEach { + options.encoding = "UTF-8" +} + +tasks.register("sourcesJar", Jar) { + dependsOn "classes" + group "build" + archiveClassifier = "sources" + archiveBaseName = jar.archiveBaseName + from sourceSets.main.allSource +} + +tasks.register("javadocJar", Jar) { + dependsOn "javadoc" + group "build" + archiveClassifier = "javadoc" + archiveBaseName = jar.archiveBaseName + from sourceSets.main.allSource +} + +configurations { + testArtifacts.extendsFrom testRuntime +} + +tasks.register('testJar', Jar) { + archiveClassifier = "test" + from sourceSets.test.output +} + +artifacts { + testArtifacts testJar +} + +tasks.withType(Test).configureEach { + maxParallelForks = Runtime.runtime.availableProcessors() +} diff --git a/buildSrc/src/main/groovy/configure-publishing.gradle b/buildSrc/src/main/groovy/configure-publishing.gradle new file mode 100644 index 00000000..7f3ff3cc --- /dev/null +++ b/buildSrc/src/main/groovy/configure-publishing.gradle @@ -0,0 +1,32 @@ +plugins { + id("maven-publish") +} + +publishing { + repositories { + maven { + name = "GitlabPackages" + url = uri("https://gitlab.com/api/v4/projects/37512056/packages/maven") + credentials(HttpHeaderCredentials) { + name = "Private-Token" + value = project.findProperty("gitlab.token") ?: System.getenv("GITLAB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } + + publications { + mavenJava(MavenPublication) { + from components.java + version = rootProject.version + afterEvaluate { + artifactId = jar.archiveBaseName.get() + groupId = rootProject.group + } + artifact sourcesJar + artifact javadocJar + } + } +} diff --git a/lombok.config b/lombok.config new file mode 100644 index 00000000..b6e33001 --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +lombok.log.custom.declaration = javasabr.rlib.logger.api.Logger javasabr.rlib.logger.api.LoggerManager.getLogger(TYPE) \ No newline at end of file diff --git a/rlib-classpath/build.gradle b/rlib-classpath/build.gradle index 70a1fabd..c2953f2c 100644 --- a/rlib-classpath/build.gradle +++ b/rlib-classpath/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { api projects.rlibCommon diff --git a/rlib-collections/build.gradle b/rlib-collections/build.gradle index 8acc7aeb..e5e445f0 100644 --- a/rlib-collections/build.gradle +++ b/rlib-collections/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { api projects.rlibCommon diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/AbstractArrayBasedDeque.java b/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/AbstractArrayBasedDeque.java index 96984cfc..008a22d3 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/AbstractArrayBasedDeque.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/AbstractArrayBasedDeque.java @@ -111,6 +111,8 @@ public boolean offerLast(E element) { protected abstract void incrementSize(); protected abstract void decrementSize(); + protected abstract int decrementSizeAndGet(); + protected @Nullable E[] increaseLeft(@Nullable E[] items, int size) { int stepSize = stepSize(items); @@ -177,13 +179,17 @@ public E removeFirst() { E item = items[head]; items[head] = null; - decrementSize(); - int newHead = incrementHeadAndGet(); + int newSize = decrementSizeAndGet(); + if (newSize == 0) { + resetIndexes(); + //noinspection DataFlowIssue + return item; + } + int newHead = incrementHeadAndGet(); if (head == tail()) { tail(newHead); } - if (newHead > rebalanceTrigger()) { rebalance(); } @@ -205,13 +211,17 @@ public E removeLast() { E item = items[tail]; items[tail] = null; - decrementSize(); - int newTail = decrementTailAndGet(); + int newSize = decrementSizeAndGet(); + if (newSize == 0) { + resetIndexes(); + //noinspection DataFlowIssue + return item; + } + int newTail = decrementTailAndGet(); if (tail == head()) { head(newTail); } - if (items.length - tail > rebalanceTrigger()) { rebalance(); } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/DefaultArrayBasedDeque.java b/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/DefaultArrayBasedDeque.java index b53048dd..9c433f75 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/DefaultArrayBasedDeque.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/DefaultArrayBasedDeque.java @@ -60,4 +60,9 @@ protected void incrementSize() { protected void decrementSize() { size--; } + + @Override + protected int decrementSizeAndGet() { + return --size; + } } diff --git a/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/LinkedListIterator.java b/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/LinkedListIterator.java index e85731c8..d3e64251 100644 --- a/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/LinkedListIterator.java +++ b/rlib-collections/src/main/java/javasabr/rlib/collections/deque/impl/LinkedListIterator.java @@ -18,6 +18,8 @@ public class LinkedListIterator implements Iterator { @Nullable LinkedNode next; + @Nullable + LinkedNode current; public LinkedListIterator(AbstractLinkedListBasedDeque linkedList, int mode) { if (mode == NEXT) { @@ -43,7 +45,16 @@ public E next() { throw new NoSuchElementException(); } E item = next.item; + current = next; next = nextFunction.apply(next); return item; } + + @Override + public void remove() { + if (current == null) { + throw new NoSuchElementException(); + } + linkedList.unlink(current); + } } diff --git a/rlib-collections/src/test/java/javasabr/rlib/collections/deque/DequeTest.java b/rlib-collections/src/test/java/javasabr/rlib/collections/deque/DequeTest.java index 5752a16b..0999ca2f 100644 --- a/rlib-collections/src/test/java/javasabr/rlib/collections/deque/DequeTest.java +++ b/rlib-collections/src/test/java/javasabr/rlib/collections/deque/DequeTest.java @@ -11,6 +11,9 @@ import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; import java.util.stream.Stream; import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.common.util.ReflectionUtils; @@ -55,6 +58,82 @@ void shouldAddFirst(Deque deque) { "val7", "val6", "val5", "val4", "val3", "val2", "val1")); } + @ParameterizedTest + @MethodSource("generateDeque") + void shouldAddFirstManyElements(Deque deque) { + + // when: + IntStream.range(1, 200) + .mapToObj(value -> "val_" + value) + .forEach(deque::addFirst); + + // then: + assertEquals(199, deque.size()); + } + + @ParameterizedTest + @MethodSource("generateDeque") + void shouldAddLastManyElements(Deque deque) { + + // when: + IntStream.range(1, 200) + .mapToObj(value -> "val_" + value) + .forEach(deque::addLast); + + // then: + assertEquals(199, deque.size()); + } + + @ParameterizedTest + @MethodSource("generateDeque") + void shouldAddFirstLastManyElements(Deque deque) { + + // when: + IntStream + .range(1, 500) + .mapToObj(value -> "val_" + value) + .forEach(value -> { + if (ThreadLocalRandom.current().nextBoolean()) { + deque.addFirst(value); + } else { + deque.addLast(value); + } + }); + + // then: + assertEquals(499, deque.size()); + } + + @ParameterizedTest + @MethodSource("generateDeque") + void shouldAddLastRemoveFirstManyElements(Deque deque) { + int count = 10; + // when/then: + IntStream + .range(1, count) + .forEach(value -> deque.addLast("value_%d".formatted(value))); + + // when/then: + IntStream + .range(1, count) + .forEach(value -> deque.removeFirst()); + + // when/then: + IntStream + .range(1, count) + .forEach(value -> deque.addLast("value_%d".formatted(value))); + + // when/then: + IntStream + .range(1, count) + .forEach(value -> deque.removeFirst()); + + // when/then: + IntStream + .range(1, count) + .forEach(value -> deque.addLast("value_%d".formatted(value))); + } + @ParameterizedTest @MethodSource("generateDeque") void shouldAddLast(Deque deque) { @@ -302,10 +381,11 @@ void shouldRebalanceIndexesAddLastRemoveFirst() { Deque deque = DequeFactory.arrayBasedBased(String.class, 15); Field head = ReflectionUtils.getUnsafeField(deque, "head"); Field tail = ReflectionUtils.getUnsafeField(deque, "tail"); + var generator = new AtomicInteger(); // when: - for (int i = 1; i < 12; i++) { - deque.addLast("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addLast("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeFirst(); @@ -316,12 +396,12 @@ void shouldRebalanceIndexesAddLastRemoveFirst() { // then: assertEquals(18, headValue); - assertEquals(18, tailValue); - assertEquals(0, deque.size()); + assertEquals(19, tailValue); + assertEquals(2, deque.size()); // when: - for (int i = 12; i < 24; i++) { - deque.addLast("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addLast("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeFirst(); @@ -332,12 +412,12 @@ void shouldRebalanceIndexesAddLastRemoveFirst() { // then: assertEquals(29, headValue); - assertEquals(29, tailValue); - assertEquals(1, deque.size()); + assertEquals(32, tailValue); + assertEquals(4, deque.size()); // when: - for (int i = 24; i < 36; i++) { - deque.addLast("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addLast("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeFirst(); @@ -348,12 +428,12 @@ void shouldRebalanceIndexesAddLastRemoveFirst() { // then: assertEquals(40, headValue); - assertEquals(41, tailValue); - assertEquals(2, deque.size()); + assertEquals(45, tailValue); + assertEquals(6, deque.size()); // when: - for (int i = 36; i < 48; i++) { - deque.addLast("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addLast("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeFirst(); @@ -363,13 +443,13 @@ void shouldRebalanceIndexesAddLastRemoveFirst() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(51, headValue); - assertEquals(53, tailValue); - assertEquals(3, deque.size()); + assertEquals(31, headValue); + assertEquals(38, tailValue); + assertEquals(8, deque.size()); // when: - for (int i = 48; i < 60; i++) { - deque.addLast("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addLast("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeFirst(); @@ -379,13 +459,13 @@ void shouldRebalanceIndexesAddLastRemoveFirst() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(40, headValue); - assertEquals(43, tailValue); - assertEquals(4, deque.size()); + assertEquals(42, headValue); + assertEquals(51, tailValue); + assertEquals(10, deque.size()); // when: - for (int i = 60; i < 72; i++) { - deque.addLast("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addLast("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeFirst(); @@ -395,13 +475,13 @@ void shouldRebalanceIndexesAddLastRemoveFirst() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(51, headValue); - assertEquals(55, tailValue); - assertEquals(5, deque.size()); + assertEquals(30, headValue); + assertEquals(41, tailValue); + assertEquals(12, deque.size()); // when: - for (int i = 60; i < 72; i++) { - deque.addLast("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addLast("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeFirst(); @@ -411,9 +491,9 @@ void shouldRebalanceIndexesAddLastRemoveFirst() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(39, headValue); - assertEquals(44, tailValue); - assertEquals(6, deque.size()); + assertEquals(41, headValue); + assertEquals(54, tailValue); + assertEquals(14, deque.size()); } @Test @@ -423,10 +503,11 @@ void shouldRebalanceIndexesAddFirstRemoveLast() { Deque deque = DequeFactory.arrayBasedBased(String.class, 15); Field head = ReflectionUtils.getUnsafeField(deque, "head"); Field tail = ReflectionUtils.getUnsafeField(deque, "tail"); + var generator = new AtomicInteger(); // when: - for (int i = 1; i < 12; i++) { - deque.addFirst("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addFirst("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeLast(); @@ -436,13 +517,13 @@ void shouldRebalanceIndexesAddFirstRemoveLast() { int tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(6, headValue); + assertEquals(5, headValue); assertEquals(6, tailValue); - assertEquals(0, deque.size()); + assertEquals(2, deque.size()); // when: - for (int i = 12; i < 24; i++) { - deque.addFirst("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addFirst("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeLast(); @@ -452,13 +533,13 @@ void shouldRebalanceIndexesAddFirstRemoveLast() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(5, headValue); + assertEquals(2, headValue); assertEquals(5, tailValue); - assertEquals(1, deque.size()); + assertEquals(4, deque.size()); // when: - for (int i = 24; i < 36; i++) { - deque.addFirst("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addFirst("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeLast(); @@ -468,13 +549,13 @@ void shouldRebalanceIndexesAddFirstRemoveLast() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(3, headValue); - assertEquals(4, tailValue); - assertEquals(2, deque.size()); + assertEquals(9, headValue); + assertEquals(14, tailValue); + assertEquals(6, deque.size()); // when: - for (int i = 36; i < 48; i++) { - deque.addFirst("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addFirst("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeLast(); @@ -484,13 +565,13 @@ void shouldRebalanceIndexesAddFirstRemoveLast() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(1, headValue); - assertEquals(3, tailValue); - assertEquals(3, deque.size()); + assertEquals(25, headValue); + assertEquals(32, tailValue); + assertEquals(8, deque.size()); // when: - for (int i = 48; i < 60; i++) { - deque.addFirst("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addFirst("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeLast(); @@ -500,13 +581,13 @@ void shouldRebalanceIndexesAddFirstRemoveLast() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(31, headValue); - assertEquals(34, tailValue); - assertEquals(4, deque.size()); + assertEquals(12, headValue); + assertEquals(21, tailValue); + assertEquals(10, deque.size()); // when: - for (int i = 60; i < 72; i++) { - deque.addFirst("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addFirst("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeLast(); @@ -516,13 +597,13 @@ void shouldRebalanceIndexesAddFirstRemoveLast() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(19, headValue); - assertEquals(23, tailValue); - assertEquals(5, deque.size()); + assertEquals(31, headValue); + assertEquals(42, tailValue); + assertEquals(12, deque.size()); // when: - for (int i = 60; i < 72; i++) { - deque.addFirst("val%s".formatted(i)); + for (int i = 1; i < 14; i++) { + deque.addFirst("val%s".formatted(generator.incrementAndGet())); } for (int i = 1; i < 12; i++) { deque.removeLast(); @@ -532,9 +613,9 @@ void shouldRebalanceIndexesAddFirstRemoveLast() { tailValue = ReflectionUtils.getFieldValue(deque, tail); // then: - assertEquals(30, headValue); - assertEquals(35, tailValue); - assertEquals(6, deque.size()); + assertEquals(18, headValue); + assertEquals(31, tailValue); + assertEquals(14, deque.size()); } @ParameterizedTest @@ -577,13 +658,13 @@ void shouldCorrectlyIterate2Deque(Deque deque) { } // then: - assertArrayEquals(container.toArray(), array("val6", "val5", "val4", "val3", "val2", "val1")); - assertArrayEquals(deque.toArray(), array("val1", "val3", "val5", "val6")); + // assertArrayEquals(container.toArray(), array("val6", "val5", "val4", "val3", "val2", "val1")); + assertArrayEquals(array("val1", "val3", "val5", "val6"), deque.toArray()); } private static Stream generateDeque() { return Stream.of( - //Arguments.of(DequeFactory.linkedListBased()), + Arguments.of(DequeFactory.linkedListBased()), Arguments.of(DequeFactory.arrayBasedBased(String.class, 15))); } } diff --git a/rlib-common/build.gradle b/rlib-common/build.gradle index b3fd7a24..a70affdd 100644 --- a/rlib-common/build.gradle +++ b/rlib-common/build.gradle @@ -1,3 +1,8 @@ +plugins { + id("configure-java") + id("configure-publishing") +} + dependencies { api projects.rlibLoggerApi } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/AsyncUtils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/AsyncUtils.java index 31d4a15b..16fa0e9c 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/AsyncUtils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/AsyncUtils.java @@ -1,26 +1,28 @@ package javasabr.rlib.common.util; +import java.util.Objects; import java.util.concurrent.CompletionException; -import org.jspecify.annotations.NullMarked; +import lombok.experimental.UtilityClass; import org.jspecify.annotations.Nullable; /** * @author JavaSaBr */ -@NullMarked +@UtilityClass public class AsyncUtils { - public static @Nullable T skip(Throwable throwable) { + @Nullable + public static T skip(Throwable throwable) { return null; } - public static @Nullable T continueCompletableStage(@Nullable T result, @Nullable Throwable throwable) { + public static T continueCompletableStage(@Nullable T result, @Nullable Throwable throwable) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } else if (throwable != null) { throw new CompletionException(throwable); } else { - return result; + return Objects.requireNonNull(result); } } } diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/BufferUtils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/BufferUtils.java index 4b278589..25ae6b5e 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/BufferUtils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/BufferUtils.java @@ -13,8 +13,6 @@ import org.jspecify.annotations.NullMarked; /** - * The utility class. - * * @author JavaSaBr */ @NullMarked @@ -34,26 +32,19 @@ public class BufferUtils { * @param source the source buffer * @return the target buffer. */ - public ByteBuffer loadFrom(ByteBuffer source, ByteBuffer target) { - + public static ByteBuffer loadFrom(ByteBuffer source, ByteBuffer target) { var prevLimit = source.limit(); try { - target.clear(); - - var skip = source.remaining() - target.remaining(); - + int skip = source.remaining() - target.remaining(); if (skip > 0) { source.limit(prevLimit - skip); } - target.put(source); target.flip(); - } finally { source.limit(prevLimit); } - return target; } @@ -63,7 +54,7 @@ public ByteBuffer loadFrom(ByteBuffer source, ByteBuffer target) { * @param size the byte buffer's size. * @return the new mapped byte buffer. */ - public MappedByteBuffer allocateRWMappedByteBuffer(int size) { + public static MappedByteBuffer allocateRWMappedByteBuffer(int size) { try { var tempFile = Files.createTempFile("rlib_common_util_", "_buffer_utils.bin"); @@ -77,21 +68,31 @@ public MappedByteBuffer allocateRWMappedByteBuffer(int size) { } } - public ByteBuffer putToAndFlip(ByteBuffer buffer, ByteBuffer additional) { + public static ByteBuffer putToAndFlip(ByteBuffer buffer, ByteBuffer additional) { return buffer .limit(buffer.capacity()) .put(additional) .flip(); } + public static ByteBuffer appendAndClear(ByteBuffer buffer, ByteBuffer additional) { + ByteBuffer result = buffer + .position(buffer.limit()) + .limit(buffer.capacity()) + .put(additional) + .flip(); + additional.clear(); + return result; + } + /** - * Create a new byte buffer with with writing some data inside the consumer and to flip in the result. + * Create a new byte buffer with writing some data inside the consumer and to flip in the result. * * @param size the buffer's size. * @param consumer the consumer to write data. * @return the flipped buffer. */ - public ByteBuffer prepareBuffer(int size, Consumer consumer) { + public static ByteBuffer prepareBuffer(int size, Consumer consumer) { var buffer = ByteBuffer.allocate(size); consumer.accept(buffer); return buffer.flip(); diff --git a/rlib-common/src/main/java/javasabr/rlib/common/util/Utils.java b/rlib-common/src/main/java/javasabr/rlib/common/util/Utils.java index 09474c2e..ac62fb51 100644 --- a/rlib-common/src/main/java/javasabr/rlib/common/util/Utils.java +++ b/rlib-common/src/main/java/javasabr/rlib/common/util/Utils.java @@ -7,6 +7,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.concurrent.Callable; import javasabr.rlib.common.function.NotNullSafeBiConsumer; import javasabr.rlib.common.function.NotNullSafeBiFunction; import javasabr.rlib.common.function.NotNullSafeConsumer; @@ -145,9 +146,9 @@ public static void unchecked( } } - public static R uncheckedGet(NotNullSafeFactory function) { + public static R uncheckedGet(Callable function) { try { - return function.get(); + return function.call(); } catch (IOException e) { throw new UncheckedIOException(e); } catch (Exception e) { diff --git a/rlib-compiler/build.gradle b/rlib-compiler/build.gradle index 70a1fabd..c2953f2c 100644 --- a/rlib-compiler/build.gradle +++ b/rlib-compiler/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { api projects.rlibCommon diff --git a/rlib-concurrent/build.gradle b/rlib-concurrent/build.gradle index db4c8ef6..a8b1acac 100644 --- a/rlib-concurrent/build.gradle +++ b/rlib-concurrent/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { api projects.rlibCollections diff --git a/rlib-functions/build.gradle b/rlib-functions/build.gradle index 37ec34f9..4c5cb706 100644 --- a/rlib-functions/build.gradle +++ b/rlib-functions/build.gradle @@ -1,3 +1,4 @@ - -dependencies { -} \ No newline at end of file +plugins { + id("configure-java") + id("configure-publishing") +} diff --git a/rlib-functions/src/main/java/javasabr/rlib/functions/ObjBoolConsumer.java b/rlib-functions/src/main/java/javasabr/rlib/functions/ObjBoolConsumer.java new file mode 100644 index 00000000..10f58c68 --- /dev/null +++ b/rlib-functions/src/main/java/javasabr/rlib/functions/ObjBoolConsumer.java @@ -0,0 +1,10 @@ +package javasabr.rlib.functions; + +/** + * @author JavaSaBr + */ +@FunctionalInterface +public interface ObjBoolConsumer { + + void accept(A arg1, boolean arg2); +} diff --git a/rlib-fx/build.gradle b/rlib-fx/build.gradle index f1ed3631..6b1ce95c 100644 --- a/rlib-fx/build.gradle +++ b/rlib-fx/build.gradle @@ -1,5 +1,7 @@ plugins { + id("configure-java") id 'org.openjfx.javafxplugin' version '0.1.0' + id("configure-publishing") } dependencies { diff --git a/rlib-geometry/build.gradle b/rlib-geometry/build.gradle index f6679eef..5f62cda3 100644 --- a/rlib-geometry/build.gradle +++ b/rlib-geometry/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { api projects.rlibCommon diff --git a/rlib-io/build.gradle b/rlib-io/build.gradle index dec474f6..499f1cee 100644 --- a/rlib-io/build.gradle +++ b/rlib-io/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { api projects.rlibCommon diff --git a/rlib-logger-api/build.gradle b/rlib-logger-api/build.gradle index e69de29b..4c5cb706 100644 --- a/rlib-logger-api/build.gradle +++ b/rlib-logger-api/build.gradle @@ -0,0 +1,4 @@ +plugins { + id("configure-java") + id("configure-publishing") +} diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java index bdd0248d..dae4343e 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java @@ -17,10 +17,10 @@ interface Factory { } @FunctionalInterface - interface N1Factory { + interface N1Factory { @NonNull - String make(F arg1); + String make(A arg1); } @FunctionalInterface @@ -31,24 +31,38 @@ interface IntFactory { } @FunctionalInterface - interface IntN1Factory { + interface IntN1Factory { @NonNull - String make(int arg1, F arg2); + String make(int arg1, B arg2); } @FunctionalInterface - interface N2Factory { + interface N2Factory { @NonNull - String make(F arg1, S arg2); + String make(A arg1, B arg2); } @FunctionalInterface - interface N1IntFactory { + interface N1IntFactory { @NonNull - String make(F arg1, int arg2); + String make(A arg1, int arg2); + } + + @FunctionalInterface + interface N1Int2Factory { + + @NonNull + String make(A arg1, int arg2, int arg3); + } + + @FunctionalInterface + interface N1IntN1Factory { + + @NonNull + String make(A arg1, int arg2, C arg3); } @FunctionalInterface @@ -59,17 +73,31 @@ interface Int2Factory { } @FunctionalInterface - interface N3Factory { + interface N3Factory { + + @NonNull + String make(A arg1, B arg2, C arg3); + } + + @FunctionalInterface + interface Int2N1Factory { + + @NonNull + String make(int arg1, int arg2, C arg3); + } + + @FunctionalInterface + interface N1Int2N1Factory { @NonNull - String make(F arg1, S arg2, T arg3); + String make(A arg1, int arg2, int arg3, D arg4); } @FunctionalInterface - interface N4Factory { + interface N4Factory { @NonNull - String make(F arg1, S arg2, T arg3, FO arg4); + String make(A arg1, B arg2, C arg3, D arg4); } /** @@ -85,19 +113,19 @@ default void debug(int arg1, @NonNull IntFactory factory) { print(LoggerLevel.DEBUG, arg1, factory); } - default void debug(T arg1, @NonNull N1Factory factory) { + default void debug(A arg1, @NonNull N1Factory factory) { print(LoggerLevel.DEBUG, arg1, factory); } - default void debug(int arg1, F arg2, @NonNull IntN1Factory factory) { + default void debug(int arg1, B arg2, @NonNull IntN1Factory factory) { print(LoggerLevel.DEBUG, arg1, arg2, factory); } - default void debug(F arg1, S arg2, @NonNull N2Factory factory) { + default void debug(A arg1, B arg2, @NonNull N2Factory factory) { print(LoggerLevel.DEBUG, arg1, arg2, factory); } - default void debug(F arg1, int arg2, @NonNull N1IntFactory factory) { + default void debug(A arg1, int arg2, @NonNull N1IntFactory factory) { print(LoggerLevel.DEBUG, arg1, arg2, factory); } @@ -105,7 +133,15 @@ default void debug(int arg1, int arg2, @NonNull Int2Factory factory) { print(LoggerLevel.DEBUG, arg1, arg2, factory); } - default void debug(F arg1, S arg2, T arg3, @NonNull N3Factory factory) { + default void debug(A arg1, B arg2, C arg3, @NonNull N3Factory factory) { + print(LoggerLevel.DEBUG, arg1, arg2, arg3, factory); + } + + default void debug(A arg1, int arg2, C arg3, @NonNull N1IntN1Factory factory) { + print(LoggerLevel.DEBUG, arg1, arg2, arg3, factory); + } + + default void debug(A arg1, int arg2, int arg3, @NonNull N1Int2Factory factory) { print(LoggerLevel.DEBUG, arg1, arg2, arg3, factory); } @@ -113,6 +149,30 @@ default void error(@NonNull String message) { print(LoggerLevel.ERROR, message); } + default void error(A arg1, @NonNull N1Factory factory) { + print(LoggerLevel.ERROR, arg1, factory); + } + + default void error(int arg1, @NonNull IntFactory factory) { + print(LoggerLevel.ERROR, arg1, factory); + } + + default void error(int arg1, B arg2, @NonNull IntN1Factory factory) { + print(LoggerLevel.ERROR, arg1, arg2, factory); + } + + default void error(A arg1, B arg2, @NonNull N2Factory factory) { + print(LoggerLevel.ERROR, arg1, arg2, factory); + } + + default void error(int arg1, int arg2, C arg3, @NonNull Int2N1Factory factory) { + print(LoggerLevel.ERROR, arg1, arg2, arg3, factory); + } + + default void error(A arg1, int arg2, int arg3, D arg4, @NonNull N1Int2N1Factory factory) { + print(LoggerLevel.ERROR, arg1, arg2, arg3, arg4, factory); + } + default void error(@NonNull Throwable exception) { print(LoggerLevel.ERROR, exception); } @@ -125,11 +185,11 @@ default void info(T arg1, @NonNull N1Factory factory) { print(LoggerLevel.INFO, arg1, factory); } - default void info(F arg1, S arg2, @NonNull N2Factory factory) { + default void info(A arg1, B arg2, @NonNull N2Factory factory) { print(LoggerLevel.INFO, arg1, arg2, factory); } - default void info(F arg1, S arg2, T arg3, @NonNull N3Factory factory) { + default void info(A arg1, B arg2, C arg3, @NonNull N3Factory factory) { print(LoggerLevel.INFO, arg1, arg2, arg3, factory); } @@ -140,6 +200,20 @@ default boolean enabled(@NonNull LoggerLevel level) { return level.enabled(); } + /** + * Check of enabling the warning level. + */ + default boolean warningEnabled() { + return enabled(LoggerLevel.WARNING); + } + + /** + * Check of enabling the debug level. + */ + default boolean debugEnabled() { + return enabled(LoggerLevel.DEBUG); + } + /** * Override the enabling status of the logger level. */ @@ -162,16 +236,20 @@ default void warning(A arg1, @NonNull N1Factory factory) { print(LoggerLevel.WARNING, arg1, factory); } - default void warning(F arg1, S arg2, @NonNull N2Factory factory) { + default void warning(A arg1, B arg2, @NonNull N2Factory factory) { print(LoggerLevel.WARNING, arg1, arg2, factory); } - default void warning( - F arg1, - S arg2, - T arg3, - FO arg4, - @NonNull N4Factory factory) { + default void warning(A arg1, B arg2, C arg3, @NonNull N3Factory factory) { + print(LoggerLevel.WARNING, arg1, arg2, arg3, factory); + } + + default void warning( + A arg1, + B arg2, + C arg3, + D arg4, + @NonNull N4Factory factory) { print(LoggerLevel.WARNING, arg1, arg2, arg3, arg4, factory); } @@ -179,7 +257,7 @@ default void warning( void print(@NonNull LoggerLevel level, @NonNull Throwable exception); - default void print(@NonNull LoggerLevel level, T arg1, @NonNull N1Factory factory) { + default void print(@NonNull LoggerLevel level, A arg1, @NonNull N1Factory factory) { if (enabled(level)) { print(level, factory.make(arg1)); } @@ -191,31 +269,31 @@ default void print(@NonNull LoggerLevel level, int arg1, @NonNull IntFactory fac } } - default void print( + default void print( @NonNull LoggerLevel level, int arg1, - F arg2, - @NonNull IntN1Factory factory) { + B arg2, + @NonNull IntN1Factory factory) { if (enabled(level)) { print(level, factory.make(arg1, arg2)); } } - default void print( + default void print( @NonNull LoggerLevel level, - F arg1, - S arg2, - @NonNull N2Factory factory) { + A arg1, + B arg2, + @NonNull N2Factory factory) { if (enabled(level)) { print(level, factory.make(arg1, arg2)); } } - default void print( + default void print( @NonNull LoggerLevel level, - F arg1, + A arg1, int arg2, - @NonNull N1IntFactory factory) { + @NonNull N1IntFactory factory) { if (enabled(level)) { print(level, factory.make(arg1, arg2)); } @@ -231,24 +309,69 @@ default void print( } } - default void print( + default void print( + @NonNull LoggerLevel level, + A arg1, + B arg2, + C arg3, + @NonNull N3Factory factory) { + if (enabled(level)) { + print(level, factory.make(arg1, arg2, arg3)); + } + } + + default void print( + @NonNull LoggerLevel level, + A arg1, + int arg2, + C arg3, + @NonNull N1IntN1Factory factory) { + if (enabled(level)) { + print(level, factory.make(arg1, arg2, arg3)); + } + } + + default void print( @NonNull LoggerLevel level, - F arg1, - S arg2, - T arg3, - @NonNull N3Factory factory) { + A arg1, + int arg2, + int arg3, + @NonNull N1Int2Factory factory) { if (enabled(level)) { print(level, factory.make(arg1, arg2, arg3)); } } - default void print( + default void print( + @NonNull LoggerLevel level, + int arg1, + int arg2, + C arg3, + @NonNull Int2N1Factory factory) { + if (enabled(level)) { + print(level, factory.make(arg1, arg2, arg3)); + } + } + + default void print( + @NonNull LoggerLevel level, + A arg1, + int arg2, + int arg3, + D arg4, + @NonNull N1Int2N1Factory factory) { + if (enabled(level)) { + print(level, factory.make(arg1, arg2, arg3, arg4)); + } + } + + default void print( @NonNull LoggerLevel level, - F arg1, - S arg2, - T arg3, - FO arg4, - @NonNull N4Factory factory) { + A arg1, + B arg2, + C arg3, + D arg4, + @NonNull N4Factory factory) { if (enabled(level)) { print(level, factory.make(arg1, arg2, arg3, arg4)); } diff --git a/rlib-logger-impl/build.gradle b/rlib-logger-impl/build.gradle index dd159500..99773b26 100644 --- a/rlib-logger-impl/build.gradle +++ b/rlib-logger-impl/build.gradle @@ -1,3 +1,8 @@ +plugins { + id("configure-java") + id("configure-publishing") +} + dependencies { api projects.rlibCommon api projects.rlibCollections diff --git a/rlib-logger-slf4j/build.gradle b/rlib-logger-slf4j/build.gradle index 1fc24540..08ad7c5e 100644 --- a/rlib-logger-slf4j/build.gradle +++ b/rlib-logger-slf4j/build.gradle @@ -1,3 +1,8 @@ +plugins { + id("configure-java") + id("configure-publishing") +} + dependencies { api projects.rlibLoggerApi api libs.slf4j.api diff --git a/rlib-mail/build.gradle b/rlib-mail/build.gradle index 3f6a80e5..814906a3 100644 --- a/rlib-mail/build.gradle +++ b/rlib-mail/build.gradle @@ -1,3 +1,8 @@ +plugins { + id("configure-java") + id("configure-publishing") +} + dependencies { api projects.rlibCommon api libs.bundles.mail diff --git a/rlib-network/build.gradle b/rlib-network/build.gradle index ef0d1c95..8ddfd5d0 100644 --- a/rlib-network/build.gradle +++ b/rlib-network/build.gradle @@ -1,3 +1,8 @@ +plugins { + id("configure-java") + id("configure-publishing") +} + dependencies { api projects.rlibCommon api projects.rlibClasspath @@ -5,4 +10,5 @@ dependencies { api projects.rlibReusable api libs.project.reactor.core testRuntimeOnly projects.rlibLoggerImpl + loadTestRuntimeOnly projects.rlibLoggerImpl } diff --git a/rlib-network/src/loadTest/java/javasabr/rlib/network/StringNetworkLoadTest.java b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringNetworkLoadTest.java new file mode 100644 index 00000000..5b9922f9 --- /dev/null +++ b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringNetworkLoadTest.java @@ -0,0 +1,206 @@ +package javasabr.rlib.network; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAccumulator; +import javasabr.rlib.common.util.StringUtils; +import javasabr.rlib.common.util.ThreadUtils; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.api.LoggerManager; +import javasabr.rlib.network.ServerNetworkConfig.SimpleServerNetworkConfig; +import javasabr.rlib.network.client.ClientNetwork; +import javasabr.rlib.network.impl.DefaultBufferAllocator; +import javasabr.rlib.network.impl.StringDataConnection; +import javasabr.rlib.network.packet.impl.StringWritableNetworkPacket; +import javasabr.rlib.network.server.ServerNetwork; +import lombok.CustomLog; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.experimental.Accessors; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@CustomLog +public class StringNetworkLoadTest { + + public static final int MAX_ITERATIONS = 10; + public static final int MAX_SEND_DELAY = 20_000; + + private static class TestClient implements AutoCloseable { + + private static final AtomicInteger ID_FACTORY = new AtomicInteger(); + + String clientId = "Client_%s".formatted(ID_FACTORY.incrementAndGet()); + NetworkConfig networkConfig = NetworkConfig.SimpleNetworkConfig.builder() + .groupName(clientId) + .writeBufferSize(256) + .readBufferSize(256) + .pendingBufferSize(512) + .build(); + + BufferAllocator clientAllocator = new DefaultBufferAllocator(networkConfig); + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + ClientNetwork network = NetworkFactory + .stringDataClientNetwork(networkConfig, clientAllocator); + StatisticsCollector statistics; + + private TestClient(StatisticsCollector statistics) { + this.statistics = statistics; + } + + void connectAndSendMessages( + InetSocketAddress serverAddress, + int messages) { + Thread thread = new Thread(() -> { + + ThreadLocalRandom random = ThreadLocalRandom.current(); + + ThreadUtils.sleep(random.nextInt(5000)); + StringDataConnection connection = network.connect(serverAddress); + + connection.onReceive((serverConnection, packet) -> statistics + .receivedServerPackersPerSecond() + .accumulate(1)); + + var tasks = new ArrayList>(messages); + + for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++) { + for (int m = 0; m < messages; m++) { + int delay = random.nextInt(MAX_SEND_DELAY); + ScheduledFuture schedule = executor.schedule( + () -> { + StringWritableNetworkPacket message = newMessage(10, 10240); + connection.send(message); + }, delay, TimeUnit.MILLISECONDS); + tasks.add(schedule); + } + + for (ScheduledFuture task : tasks) { + try { + task.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + tasks.clear(); + } + }); + thread.setName(clientId); + thread.setUncaughtExceptionHandler((t, e) -> log.error(e)); + thread.start(); + } + + @Override + public void close() { + network.shutdown(); + } + } + + @Getter + @Accessors(fluent = true) + private static class StatisticsCollector { + LongAccumulator receivedClientPackersPerSecond = new LongAccumulator(Long::sum, 0); + LongAccumulator sentEchoPackersPerSecond = new LongAccumulator(Long::sum, 0); + LongAccumulator receivedServerPackersPerSecond = new LongAccumulator(Long::sum, 0); + } + + @Test + @SneakyThrows + void testServerWithMultiplyClients() { + LoggerManager.enable(StringNetworkLoadTest.class, LoggerLevel.INFO); + + var serverConfig = SimpleServerNetworkConfig + .builder() + .threadGroupSize(10) + .writeBufferSize(1024) + .readBufferSize(1024) + .pendingBufferSize(2048) + .build(); + + var serverAllocator = new DefaultBufferAllocator(serverConfig); + ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); + + int clientCount = 200; + int messagesPerIteration = 3_000; + int expectedMessages = clientCount * messagesPerIteration * MAX_ITERATIONS; + + var finalWaiter = new CountDownLatch(1); + var statistics = new StatisticsCollector(); + + ServerNetwork serverNetwork = NetworkFactory.stringDataServerNetwork(serverConfig, serverAllocator); + InetSocketAddress serverAddress = serverNetwork.start(); + + serverNetwork.onAccept(accepted -> accepted + .onReceive((connection, packet) -> { + statistics + .receivedClientPackersPerSecond() + .accumulate(1); + connection.send(new StringWritableNetworkPacket("Echo: " + packet.data())); + statistics + .sentEchoPackersPerSecond() + .accumulate(1); + })); + + initReceivedMessagesTracker( + scheduledExecutor, + statistics, + finalWaiter); + + var clients = new ArrayList(); + + for(int c = 0; c < clientCount; c++) { + TestClient testClient = new TestClient(statistics); + testClient.connectAndSendMessages(serverAddress, messagesPerIteration); + } + + Assertions + .assertTrue(finalWaiter.await(300_000, TimeUnit.MILLISECONDS), + "Still not received [%s] -> [%s] messages".formatted(expectedMessages, finalWaiter.getCount())); + + for (TestClient client : clients) { + client.close(); + } + + serverNetwork.shutdown(); + } + + private static void initReceivedMessagesTracker( + ScheduledExecutorService scheduledExecutor, + StatisticsCollector statistics, + CountDownLatch finalWaiter) { + scheduledExecutor.scheduleAtFixedRate(() -> { + long received = statistics + .receivedClientPackersPerSecond() + .getThenReset(); + log.info(received, "Server receiving [%s] messages/sec"::formatted); + if (received == 0) { + finalWaiter.countDown(); + } + }, 1, 1, TimeUnit.SECONDS); + scheduledExecutor.scheduleAtFixedRate(() -> { + long received = statistics + .sentEchoPackersPerSecond() + .getThenReset(); + log.info(received, "Sent echo packets [%s] messages/sec"::formatted); + }, 1, 1, TimeUnit.SECONDS); + scheduledExecutor.scheduleAtFixedRate(() -> { + long received = statistics + .receivedServerPackersPerSecond() + .getThenReset(); + log.info(received, "Clients receiving [%s] echo messages/sec"::formatted); + }, 1, 1, TimeUnit.SECONDS); + } + + private static StringWritableNetworkPacket newMessage(int minMessageLength, int maxMessageLength) { + return new StringWritableNetworkPacket(StringUtils.generate(minMessageLength, maxMessageLength)); + } +} diff --git a/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java new file mode 100644 index 00000000..1338f13d --- /dev/null +++ b/rlib-network/src/loadTest/java/javasabr/rlib/network/StringSslNetworkLoadTest.java @@ -0,0 +1,221 @@ +package javasabr.rlib.network; + +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAccumulator; +import javasabr.rlib.common.util.StringUtils; +import javasabr.rlib.common.util.ThreadUtils; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.api.LoggerManager; +import javasabr.rlib.network.ServerNetworkConfig.SimpleServerNetworkConfig; +import javasabr.rlib.network.client.ClientNetwork; +import javasabr.rlib.network.impl.DefaultBufferAllocator; +import javasabr.rlib.network.impl.StringDataConnection; +import javasabr.rlib.network.impl.StringDataSslConnection; +import javasabr.rlib.network.packet.impl.AbstractSslNetworkPacketReader; +import javasabr.rlib.network.packet.impl.AbstractSslNetworkPacketWriter; +import javasabr.rlib.network.packet.impl.StringWritableNetworkPacket; +import javasabr.rlib.network.server.ServerNetwork; +import javasabr.rlib.network.util.NetworkUtils; +import javax.net.ssl.SSLContext; +import lombok.CustomLog; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.experimental.Accessors; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@CustomLog +public class StringSslNetworkLoadTest { + + public static final int MAX_ITERATIONS = 10; + public static final int MAX_SEND_DELAY = 20_000; + + private static class TestClient implements AutoCloseable { + + private static final AtomicInteger ID_FACTORY = new AtomicInteger(); + + String clientId = "Client_%s".formatted(ID_FACTORY.incrementAndGet()); + NetworkConfig networkConfig = NetworkConfig.SimpleNetworkConfig.builder() + .groupName(clientId) + .writeBufferSize(256) + .readBufferSize(256) + .pendingBufferSize(512) + .build(); + + BufferAllocator clientAllocator = new DefaultBufferAllocator(networkConfig); + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + ClientNetwork network = NetworkFactory + .stringDataSslClientNetwork(networkConfig, clientAllocator, NetworkUtils.createAllTrustedClientSslContext()); + StatisticsCollector statistics; + + private TestClient(StatisticsCollector statistics) { + this.statistics = statistics; + } + + void connectAndSendMessages( + InetSocketAddress serverAddress, + int messages) { + Thread thread = new Thread(() -> { + + ThreadLocalRandom random = ThreadLocalRandom.current(); + + ThreadUtils.sleep(random.nextInt(5000)); + StringDataSslConnection connection = network.connect(serverAddress); + + connection.onReceive((serverConnection, packet) -> statistics + .receivedServerPackersPerSecond() + .accumulate(1)); + + var tasks = new ArrayList>(messages); + + for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++) { + for (int m = 0; m < messages; m++) { + int delay = random.nextInt(MAX_SEND_DELAY); + ScheduledFuture schedule = executor.schedule( + () -> { + StringWritableNetworkPacket message = newMessage(10, 10240); // 10240 + connection.send(message); + }, delay, TimeUnit.MILLISECONDS); + tasks.add(schedule); + } + + for (ScheduledFuture task : tasks) { + try { + task.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + tasks.clear(); + } + }); + thread.setName(clientId); + thread.setUncaughtExceptionHandler((t, e) -> log.error(e)); + thread.start(); + } + + @Override + public void close() { + network.shutdown(); + } + } + + @Getter + @Accessors(fluent = true) + private static class StatisticsCollector { + LongAccumulator receivedClientPackersPerSecond = new LongAccumulator(Long::sum, 0); + LongAccumulator sentEchoPackersPerSecond = new LongAccumulator(Long::sum, 0); + LongAccumulator receivedServerPackersPerSecond = new LongAccumulator(Long::sum, 0); + } + + @Test + @SneakyThrows + void testServerWithMultiplyClients() { + LoggerManager.enable(StringSslNetworkLoadTest.class, LoggerLevel.INFO); + //LoggerManager.enable(AbstractSslNetworkPacketReader.class, LoggerLevel.DEBUG); + //LoggerManager.enable(AbstractSslNetworkPacketWriter.class, LoggerLevel.DEBUG); + + var serverConfig = SimpleServerNetworkConfig + .builder() + .threadGroupSize(10) + .writeBufferSize(1024) + .readBufferSize(1024) + .pendingBufferSize(2048) + .build(); + + var serverAllocator = new DefaultBufferAllocator(serverConfig); + ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); + + int clientCount = 100; + int messagesPerIteration = 2000; + int expectedMessages = clientCount * messagesPerIteration * MAX_ITERATIONS; + + var finalWaiter = new CountDownLatch(2); + var statistics = new StatisticsCollector(); + + InputStream keystoreFile = StringSslNetworkLoadTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); + SSLContext serverSslContext = NetworkUtils.createSslContext(keystoreFile, "test"); + + ServerNetwork serverNetwork = NetworkFactory + .stringDataSslServerNetwork(serverConfig, serverAllocator, serverSslContext); + InetSocketAddress serverAddress = serverNetwork.start(); + + serverNetwork.onAccept(accepted -> accepted + .onReceive((connection, packet) -> { + statistics + .receivedClientPackersPerSecond() + .accumulate(1); + connection.send(new StringWritableNetworkPacket("Echo: " + packet.data())); + statistics + .sentEchoPackersPerSecond() + .accumulate(1); + })); + + initReceivedMessagesTracker( + scheduledExecutor, + statistics, + finalWaiter); + + var clients = new ArrayList(); + + for(int c = 0; c < clientCount; c++) { + TestClient testClient = new TestClient(statistics); + testClient.connectAndSendMessages(serverAddress, messagesPerIteration); + } + + ThreadUtils.sleep(30000); + finalWaiter.countDown(); + + Assertions + .assertTrue(finalWaiter.await(300_000, TimeUnit.MILLISECONDS), + "Still not received [%s] -> [%s] messages".formatted(expectedMessages, finalWaiter.getCount())); + + for (TestClient client : clients) { + client.close(); + } + + serverNetwork.shutdown(); + } + + private static void initReceivedMessagesTracker( + ScheduledExecutorService scheduledExecutor, + StatisticsCollector statistics, + CountDownLatch finalWaiter) { + scheduledExecutor.scheduleAtFixedRate(() -> { + long received = statistics + .receivedClientPackersPerSecond() + .getThenReset(); + log.info(received, "Server receiving [%s] messages/sec"::formatted); + if (received == 0) { + finalWaiter.countDown(); + } + }, 1, 1, TimeUnit.SECONDS); + scheduledExecutor.scheduleAtFixedRate(() -> { + long received = statistics + .sentEchoPackersPerSecond() + .getThenReset(); + log.info(received, "Sent echo packets [%s] messages/sec"::formatted); + }, 1, 1, TimeUnit.SECONDS); + scheduledExecutor.scheduleAtFixedRate(() -> { + long received = statistics + .receivedServerPackersPerSecond() + .getThenReset(); + log.info(received, "Clients receiving [%s] echo messages/sec"::formatted); + }, 1, 1, TimeUnit.SECONDS); + } + + private static StringWritableNetworkPacket newMessage(int minMessageLength, int maxMessageLength) { + return new StringWritableNetworkPacket(StringUtils.generate(minMessageLength, maxMessageLength)); + } +} diff --git a/rlib-network/src/loadTest/java/javasabr/rlib/network/package-info.java b/rlib-network/src/loadTest/java/javasabr/rlib/network/package-info.java new file mode 100644 index 00000000..53222c6a --- /dev/null +++ b/rlib-network/src/loadTest/java/javasabr/rlib/network/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.network; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/rlib-network/src/loadTest/resources/ssl/rlib_test_cert.p12 b/rlib-network/src/loadTest/resources/ssl/rlib_test_cert.p12 new file mode 100644 index 00000000..f8ee7018 Binary files /dev/null and b/rlib-network/src/loadTest/resources/ssl/rlib_test_cert.p12 differ diff --git a/rlib-network/src/loadTest/resources/ssl/rlib_test_cert.pem b/rlib-network/src/loadTest/resources/ssl/rlib_test_cert.pem new file mode 100644 index 00000000..99a41172 --- /dev/null +++ b/rlib-network/src/loadTest/resources/ssl/rlib_test_cert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDzTCCArWgAwIBAgIUFrbfPa58nThm6Q8PT5mkU2yLffAwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVEUxDTALBgNVBAgMBFRlc3QxDTALBgNVBAcMBFRlc3Qx +DTALBgNVBAoMBFRlc3QxDTALBgNVBAsMBFRlc3QxDTALBgNVBAMMBFRlc3QxHDAa +BgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wHhcNMTkxMjA2MDYwMTA5WhcNMjAx +MjA1MDYwMTA5WjB2MQswCQYDVQQGEwJURTENMAsGA1UECAwEVGVzdDENMAsGA1UE +BwwEVGVzdDENMAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVzdDENMAsGA1UEAwwE +VGVzdDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANqnqAnBBYWf5HBTKOqiwWj1/bbVVYooww3BqRd9 +AyJCmJUpJdYTAh+Yswo//mELaSH9vySKYkoJ/HqdVYr40/TqeRhdGc4UYTokOn8u +mGRsW9hdSsJOOo3UTAFUAs4YteYjEqqUKRefQIb1LETHixYyrAu6g5IwM7nfwY9l +9jm6qiTCZTM5V0oU35Yjg/1bYDWd6emM8CV1dbGCDV/QidYqW9KE8MUqCMdDSFe/ +hOt0I09hHZiH1ZVbNebGaSQivR6vcB2UswuqqkberkvjO28QOk6FJEZ8fxn5RtuP +b+C47j1pHOBZxrHaltQEvk8IhStPqgGaGmzULReOEbQA3TECAwEAAaNTMFEwHQYD +VR0OBBYEFNYonPaRTLJGti0H7UiK7MU2/DsUMB8GA1UdIwQYMBaAFNYonPaRTLJG +ti0H7UiK7MU2/DsUMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +AI+zGveb05Mj8y1bJuqyqIxg91Chgqw4nzvMVfpT03rLmV+0qctg2gwAOhQ3qrOI +RoR7T7cwH4ybQXlk9k3hLot0DfpNv5YPtBkmrGI4eRqmqJunAc/18Evop0nxE9tE +MX/oIa7pJJfzPBZbq1elM9mwD+l+Qf/fQ5qEAFgSOMAw6ZwjFf2xKDrt58RVPcHi +CnzG05YZGpBEtIxz9jJlb4dbShZ1Dz86zJW/k5GLr64x+EDM3Vdu0MlUKNsf/+RA +9bE4I+hMP8GwJGSF/TRKaiDl0Q1rdlyovOAUPfZ4uzBa7yAU+++3Atq6ORYwOdGZ +HaYft/lT2/vMy/HuahcbvgM= +-----END CERTIFICATE----- diff --git a/rlib-network/src/loadTest/resources/ssl/rlib_test_key.pem b/rlib-network/src/loadTest/resources/ssl/rlib_test_key.pem new file mode 100644 index 00000000..0dd66b96 --- /dev/null +++ b/rlib-network/src/loadTest/resources/ssl/rlib_test_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDap6gJwQWFn+Rw +UyjqosFo9f221VWKKMMNwakXfQMiQpiVKSXWEwIfmLMKP/5hC2kh/b8kimJKCfx6 +nVWK+NP06nkYXRnOFGE6JDp/LphkbFvYXUrCTjqN1EwBVALOGLXmIxKqlCkXn0CG +9SxEx4sWMqwLuoOSMDO538GPZfY5uqokwmUzOVdKFN+WI4P9W2A1nenpjPAldXWx +gg1f0InWKlvShPDFKgjHQ0hXv4TrdCNPYR2Yh9WVWzXmxmkkIr0er3AdlLMLqqpG +3q5L4ztvEDpOhSRGfH8Z+Ubbj2/guO49aRzgWcax2pbUBL5PCIUrT6oBmhps1C0X +jhG0AN0xAgMBAAECggEAGc9gjoYyWKufE4M9eTTtD6653DMifcSCAcWyaAthq1Gh +ITpSNQrZXVFxEOys4leueUyym2WaZJL5MqAP0O++VVK4KKAUw7a0I2DTZt/hLTl9 +PfUFNhp13vgIYL/B9QIFtry9H1WN5DPwMf4O06+aQ2yH0nqZvU/jSzWWa47lmbqv +JeH/T5FIOhc/pjqhep+6LFccL6TMGtTUlhID7wzwJFgO+kXKYpeZ5CL2NczAX7jR +Qd75u5igxLub0vR8Y7GG8+1Cvgt1PbHAOi9uOeQJfkT3+hSGCaW6qwXdPJ3N1hCO +4ayqGP8f6vcedDIDbclaWsRhD0kYb2AMci3NTwygAQKBgQD1lQfMjaiR3NqZnLu3 +M8KrGoeNu8GeFYPqXt0WqRVLkVo9nf68kGRjPRo5i/BCVZTUdCq9K4koiM2EMlQB +6creo4UfGR6jBf55Jm2AjzUap/y0wNUJR5JdtHR+UI/hiAvYltf7gdCBHH95ZMPw +rvZmVyYSv1F+hVWVUEWNMP/L8QKBgQDj7jOscz75d9Wdr3f1YgtxumqHX0RyG4+c +b0g9XPT7g0i6Uovfvhq+TRxF1glMO47LuQ3AtGU/zN826i2X7vZbyR+BnpjmJ9ti +PMe721CQa4dE/UH4o/xBI9iyORgYaSSU6jjZLHXqcenCyPu4MDGej194baEKSKMA +AsZIyRhlQQKBgBnd+7dRCHtsrt3VQ3R1kECjh7mnGCrFi2KJYXI8lxChm8a3uJg2 +AUzup69+DO1/xDBomAPa7RSG7FbLUkvtS0AOKzxuUerL+9TY9lm/O9G19gk38niK +wGD8W/TeGXmg8dZ7dP552vNuhALOWVElrLB05368BiJ0euJCVUEc8ySRAoGAYjHa +3S6BMORpiRdxLKvilkpjXpKyYB5zjkd67cTAXiK/EFZDoE68IsQBrCx7sPXvnODK +hfyxqfzPJU9Z+Ryf0gchpav33x/IqdfZjJr9aFqK2jPpWf77y+xvjMiAEvQpKGaH +uzgmTKxqstn3Z/CLT+4giTdhq5aDcpu/ZNgNxIECgYAwHeuj1EVS5SRhpkbhgpgO +6SDnDN5lXTZwi0PKjSH6j2o5mf+5+BRAn17aatTTdyOOMaS/V7Fa0EECmrnGLWzT +Fi1VLavmtHg30FF9nDVXXbwAOSdwWle3vpaTvCiQQc0vGEtcwEwK75Vk6GfhAcSv +xYFy5ZN9gVX9/fO00Q7QCA== +-----END PRIVATE KEY----- diff --git a/rlib-network/src/main/java/javasabr/rlib/network/BufferAllocator.java b/rlib-network/src/main/java/javasabr/rlib/network/BufferAllocator.java index 52810c1d..0bcbfc99 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/BufferAllocator.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/BufferAllocator.java @@ -1,6 +1,7 @@ package javasabr.rlib.network; import java.nio.ByteBuffer; +import org.jspecify.annotations.Nullable; /** * The interface to implement a buffer allocator for network things. @@ -11,63 +12,41 @@ public interface BufferAllocator { /** * Get a new read buffer to use. - * - * @return the new buffer. */ ByteBuffer takeReadBuffer(); /** * Get a new pending buffer to use. - * - * @return the new pending buffer. */ ByteBuffer takePendingBuffer(); /** * Get a new write buffer to use. - * - * @return the new buffer. */ ByteBuffer takeWriteBuffer(); /** * Get a new buffer with requested capacity. - * - * @param bufferSize the size of new buffer. - * @return the new buffer. */ ByteBuffer takeBuffer(int bufferSize); /** - * Store an old read buffer if need. - * - * @param buffer the old read buffer. - * @return this allocator. + * Store an already used read buffer. */ BufferAllocator putReadBuffer(ByteBuffer buffer); /** - * Store an old pending buffer if need. - * - * @param buffer the old pending buffer. - * @return this allocator. + * Store an already used pending buffer. */ BufferAllocator putPendingBuffer(ByteBuffer buffer); /** - * Store an old write buffer if need. - * - * @param buffer the old write buffer. - * @return this allocator. + * Store an already used write buffer. */ BufferAllocator putWriteBuffer(ByteBuffer buffer); /** - * Store an old byte buffer if need. - * - * @param buffer the old byte buffer. - * @return this allocator. + * Store an already used byte buffer. */ - BufferAllocator putBuffer(ByteBuffer buffer); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/Connection.java b/rlib-network/src/main/java/javasabr/rlib/network/Connection.java index 62410328..af7eb4bc 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/Connection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/Connection.java @@ -2,9 +2,8 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.packet.WritablePacket; -import lombok.AllArgsConstructor; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; import reactor.core.publisher.Flux; /** @@ -12,27 +11,26 @@ * * @author JavaSaBr */ -public interface Connection { +public interface Connection { - @AllArgsConstructor - class ReceivedPacketEvent, R extends ReadablePacket> { - public final C connection; - public final R packet; + record ReceivedPacketEvent, R extends ReadableNetworkPacket>( + C connection, R packet) { + + @Override + public String toString() { + return "[" + connection + "|" + packet + ']'; + } } /** * Get a remote address of this connection. - * - * @return the remote address. */ - String getRemoteAddress(); + String remoteAddress(); /** * Get a timestamp of last write/read activity. - * - * @return the timestamp of last write/read activity. */ - long getLastActivity(); + long lastActivity(); /** * Close this connection if this connection is still opened. @@ -40,46 +38,34 @@ class ReceivedPacketEvent, R extends ReadablePacket> void close(); /** - * Check a closed state of this connection. - * * @return true if this connection is already closed. */ - boolean isClosed(); + boolean closed(); /** * Send a packet to connection's owner. - * - * @param packet the writable packet. */ void send(W packet); /** * Send a packet to connection's owner with async feedback of this sending. * - * @param packet the writable packet. * @return the async result with true if the packet was sent or false if sending was failed. - * @since 9.5.0 */ CompletableFuture sendWithFeedback(W packet); /** * Register a consumer to handle received packets. - * - * @param consumer the consumer. */ void onReceive(BiConsumer, ? super R> consumer); /** * Get a stream of received packet events. - * - * @return the stream of received packet events. */ Flux, ? extends R>> receivedEvents(); /** * Get a stream of received packets. - * - * @return the stream of received packets. */ Flux receivedPackets(); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/NetworkConfig.java b/rlib-network/src/main/java/javasabr/rlib/network/NetworkConfig.java index 7c2ce76b..7dbbd664 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/NetworkConfig.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/NetworkConfig.java @@ -3,6 +3,7 @@ import java.nio.ByteOrder; import lombok.Builder; import lombok.Getter; +import lombok.experimental.Accessors; /** * The interface to implement a network config. @@ -13,6 +14,7 @@ public interface NetworkConfig { @Builder @Getter + @Accessors(fluent = true, chain = false) class SimpleNetworkConfig implements NetworkConfig { @Builder.Default @@ -31,49 +33,43 @@ class SimpleNetworkConfig implements NetworkConfig { NetworkConfig DEFAULT_CLIENT = new NetworkConfig() { @Override - public String getThreadGroupName() { + public String threadGroupName() { return "ClientNetworkThread"; } }; /** * Get a group name of network threads. - * - * @return the group name. */ - default String getThreadGroupName() { + default String threadGroupName() { return "NetworkThread"; } /** * Get size of buffer with used to collect received data from network. - * - * @return the read buffer's size. */ - default int getReadBufferSize() { + default int readBufferSize() { return 2048; } /** * Get size of buffer with pending data. Pending buffer allows to construct a packet with bigger data than - * {@link #getReadBufferSize()}. It should be at least 2x of {@link #getReadBufferSize()} + * {@link #readBufferSize()}. It should be at least 2x of {@link #readBufferSize()} * * @return the pending buffer's size. */ - default int getPendingBufferSize() { - return getReadBufferSize() * 2; + default int pendingBufferSize() { + return readBufferSize() * 2; } /** * Get size of buffer which used to serialize packets to bytes. - * - * @return the write buffer's size. */ - default int getWriteBufferSize() { + default int writeBufferSize() { return 2048; } - default ByteOrder getByteOrder() { + default ByteOrder byteOrder() { return ByteOrder.BIG_ENDIAN; } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/NetworkCryptor.java b/rlib-network/src/main/java/javasabr/rlib/network/NetworkCryptor.java index b9fb1fe7..bbd5c2c9 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/NetworkCryptor.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/NetworkCryptor.java @@ -15,13 +15,15 @@ public interface NetworkCryptor { */ NetworkCryptor NULL = new NetworkCryptor() { + @Nullable @Override - public @Nullable ByteBuffer decrypt(ByteBuffer data, int length, ByteBuffer toStore) { + public ByteBuffer decrypt(ByteBuffer data, int length, ByteBuffer toStore) { return null; } + @Nullable @Override - public @Nullable ByteBuffer encrypt(ByteBuffer data, int length, ByteBuffer toStore) { + public ByteBuffer encrypt(ByteBuffer data, int length, ByteBuffer toStore) { return null; } }; @@ -34,7 +36,8 @@ public interface NetworkCryptor { * @param toStore the buffer to store decrypted data. * @return the buffer with decrypted data or null if don't need to decrypt anything. */ - @Nullable ByteBuffer decrypt(ByteBuffer data, int length, ByteBuffer toStore); + @Nullable + ByteBuffer decrypt(ByteBuffer data, int length, ByteBuffer toStore); /** * Encrypt data. @@ -44,5 +47,6 @@ public interface NetworkCryptor { * @param toStore the buffer to store encrypted data. * @return the buffer with encrypted data or null if don't need to decrypt encrypt. */ - @Nullable ByteBuffer encrypt(ByteBuffer data, int length, ByteBuffer toStore); + @Nullable + ByteBuffer encrypt(ByteBuffer data, int length, ByteBuffer toStore); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/NetworkFactory.java b/rlib-network/src/main/java/javasabr/rlib/network/NetworkFactory.java index ab1edeee..5d27f4b8 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/NetworkFactory.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/NetworkFactory.java @@ -7,27 +7,29 @@ import javasabr.rlib.network.impl.DefaultBufferAllocator; import javasabr.rlib.network.impl.DefaultConnection; import javasabr.rlib.network.impl.StringDataConnection; -import javasabr.rlib.network.impl.StringDataSSLConnection; -import javasabr.rlib.network.packet.impl.DefaultReadablePacket; -import javasabr.rlib.network.packet.registry.ReadablePacketRegistry; +import javasabr.rlib.network.impl.StringDataSslConnection; +import javasabr.rlib.network.packet.impl.DefaultReadableNetworkPacket; +import javasabr.rlib.network.packet.registry.ReadableNetworkPacketRegistry; import javasabr.rlib.network.server.ServerNetwork; import javasabr.rlib.network.server.impl.DefaultServerNetwork; import javax.net.ssl.SSLContext; +import lombok.experimental.UtilityClass; /** * Class with factory methods to build client/server networks. * * @author JavaSaBr */ +@UtilityClass public final class NetworkFactory { - public static > ClientNetwork newClientNetwork( + public static > ClientNetwork clientNetwork( NetworkConfig networkConfig, BiFunction, AsynchronousSocketChannel, C> channelToConnection) { return new DefaultClientNetwork<>(networkConfig, channelToConnection); } - public static > ServerNetwork newServerNetwork( + public static > ServerNetwork serverNetwork( ServerNetworkConfig networkConfig, BiFunction, AsynchronousSocketChannel, C> channelToConnection) { return new DefaultServerNetwork<>(networkConfig, channelToConnection); @@ -35,48 +37,36 @@ public final class NetworkFactory { /** * Create a string packet based asynchronous client network. - * - * @return the client network. */ - public static ClientNetwork newStringDataClientNetwork() { - return newStringDataClientNetwork(NetworkConfig.DEFAULT_CLIENT); + public static ClientNetwork stringDataClientNetwork() { + return stringDataClientNetwork(NetworkConfig.DEFAULT_CLIENT); } /** * Create a string packet based asynchronous client network. - * - * @param networkConfig the network config. - * @return the client network. */ - public static ClientNetwork newStringDataClientNetwork( + public static ClientNetwork stringDataClientNetwork( NetworkConfig networkConfig) { - return newStringDataClientNetwork(networkConfig, new DefaultBufferAllocator(networkConfig)); + return stringDataClientNetwork(networkConfig, new DefaultBufferAllocator(networkConfig)); } /** * Create a string packet based asynchronous client network. - * - * @param networkConfig the network config. - * @param bufferAllocator the buffer allocator. - * @return the client network. */ - public static ClientNetwork newStringDataClientNetwork( + public static ClientNetwork stringDataClientNetwork( NetworkConfig networkConfig, BufferAllocator bufferAllocator) { - return newClientNetwork( + return clientNetwork( networkConfig, (network, channel) -> new StringDataConnection(network, channel, bufferAllocator)); } /** * Create id based packet default asynchronous client network. - * - * @param packetRegistry the readable packet registry. - * @return the server network. */ - public static ClientNetwork newDefaultClientNetwork( - ReadablePacketRegistry packetRegistry) { - return newDefaultClientNetwork( + public static ClientNetwork defaultClientNetwork( + ReadableNetworkPacketRegistry packetRegistry) { + return defaultClientNetwork( NetworkConfig.DEFAULT_CLIENT, new DefaultBufferAllocator(NetworkConfig.DEFAULT_CLIENT), packetRegistry); @@ -84,17 +74,12 @@ public static ClientNetwork newDefaultClientNetwork( /** * Create id based packet default asynchronous client network. - * - * @param networkConfig the network config. - * @param bufferAllocator the buffer allocator. - * @param packetRegistry the readable packet registry. - * @return the server network. */ - public static ClientNetwork newDefaultClientNetwork( + public static ClientNetwork defaultClientNetwork( NetworkConfig networkConfig, BufferAllocator bufferAllocator, - ReadablePacketRegistry packetRegistry) { - return newClientNetwork( + ReadableNetworkPacketRegistry packetRegistry) { + return clientNetwork( networkConfig, (network, channel) -> new DefaultConnection(network, channel, bufferAllocator, packetRegistry)); } @@ -102,81 +87,60 @@ public static ClientNetwork newDefaultClientNetwork( /** * Create string packet based asynchronous secure client network. * - * @param networkConfig the network config. - * @param bufferAllocator the buffer allocator. - * @param sslContext the ssl context. - * @return the client network. */ - public static ClientNetwork newStringDataSSLClientNetwork( + public static ClientNetwork stringDataSslClientNetwork( NetworkConfig networkConfig, BufferAllocator bufferAllocator, SSLContext sslContext) { - return newClientNetwork( + return clientNetwork( networkConfig, - (network, channel) -> new StringDataSSLConnection(network, channel, bufferAllocator, sslContext, true)); + (network, channel) -> new StringDataSslConnection(network, channel, bufferAllocator, sslContext, true)); } /** * Create string packet based asynchronous server network. - * - * @return the server network. */ - public static ServerNetwork newStringDataServerNetwork() { - return newStringDataServerNetwork(ServerNetworkConfig.DEFAULT_SERVER); + public static ServerNetwork stringDataServerNetwork() { + return stringDataServerNetwork(ServerNetworkConfig.DEFAULT_SERVER); } /** * Create string packet based asynchronous server network. - * - * @param networkConfig the network config. - * @return the server network. */ - public static ServerNetwork newStringDataServerNetwork( + public static ServerNetwork stringDataServerNetwork( ServerNetworkConfig networkConfig) { - return newStringDataServerNetwork(networkConfig, new DefaultBufferAllocator(networkConfig)); + return stringDataServerNetwork(networkConfig, new DefaultBufferAllocator(networkConfig)); } /** * Create string packet based asynchronous server network. - * - * @param networkConfig the network config. - * @param bufferAllocator the buffer allocator. - * @return the server network. */ - public static ServerNetwork newStringDataServerNetwork( + public static ServerNetwork stringDataServerNetwork( ServerNetworkConfig networkConfig, BufferAllocator bufferAllocator) { - return newServerNetwork( + return serverNetwork( networkConfig, (network, channel) -> new StringDataConnection(network, channel, bufferAllocator)); } /** * Create string packet based asynchronous secure server network. - * - * @param networkConfig the network config. - * @param bufferAllocator the buffer allocator. - * @param sslContext the ssl context. - * @return the server network. */ - public static ServerNetwork newStringDataSSLServerNetwork( + public static ServerNetwork stringDataSslServerNetwork( ServerNetworkConfig networkConfig, BufferAllocator bufferAllocator, SSLContext sslContext) { - return newServerNetwork( + return serverNetwork( networkConfig, - (network, channel) -> new StringDataSSLConnection(network, channel, bufferAllocator, sslContext, false)); + (network, channel) -> new StringDataSslConnection(network, channel, bufferAllocator, sslContext, false)); } /** * Create id based packet default asynchronous server network. - * - * @param packetRegistry the readable packet registry. - * @return the server network. */ - public static ServerNetwork newDefaultServerNetwork( - ReadablePacketRegistry packetRegistry) { - return newDefaultServerNetwork( + public static ServerNetwork defaultServerNetwork( + ReadableNetworkPacketRegistry packetRegistry) { + return defaultServerNetwork( ServerNetworkConfig.DEFAULT_SERVER, new DefaultBufferAllocator(ServerNetworkConfig.DEFAULT_SERVER), packetRegistry); @@ -184,22 +148,13 @@ public static ServerNetwork newDefaultServerNetwork( /** * Create id based packet default asynchronous server network. - * - * @param networkConfig the network config. - * @param bufferAllocator the buffer allocator. - * @param packetRegistry the readable packet registry. - * @return the server network. */ - public static ServerNetwork newDefaultServerNetwork( + public static ServerNetwork defaultServerNetwork( ServerNetworkConfig networkConfig, BufferAllocator bufferAllocator, - ReadablePacketRegistry packetRegistry) { - return newServerNetwork( + ReadableNetworkPacketRegistry packetRegistry) { + return serverNetwork( networkConfig, (network, channel) -> new DefaultConnection(network, channel, bufferAllocator, packetRegistry)); } - - private NetworkFactory() throws Exception { - throw new Exception("no permission"); - } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/ServerNetworkConfig.java b/rlib-network/src/main/java/javasabr/rlib/network/ServerNetworkConfig.java index 9288365f..2a3711c0 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/ServerNetworkConfig.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/ServerNetworkConfig.java @@ -1,9 +1,10 @@ package javasabr.rlib.network; import java.nio.ByteOrder; -import javasabr.rlib.common.util.GroupThreadFactory; +import javasabr.rlib.common.util.GroupThreadFactory.ThreadConstructor; import lombok.Builder; import lombok.Getter; +import lombok.experimental.Accessors; /** * The interface to implement a server network config. @@ -14,6 +15,7 @@ public interface ServerNetworkConfig extends NetworkConfig { @Builder @Getter + @Accessors(fluent = true, chain = false) class SimpleServerNetworkConfig implements ServerNetworkConfig { @Builder.Default @@ -21,7 +23,7 @@ class SimpleServerNetworkConfig implements ServerNetworkConfig { @Builder.Default private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN; @Builder.Default - private GroupThreadFactory.ThreadConstructor threadConstructor = Thread::new; + private ThreadConstructor threadConstructor = Thread::new; @Builder.Default private int readBufferSize = 2048; @@ -38,40 +40,34 @@ class SimpleServerNetworkConfig implements ServerNetworkConfig { ServerNetworkConfig DEFAULT_SERVER = new ServerNetworkConfig() { @Override - public int getThreadGroupMinSize() { + public int threadGroupMinSize() { return 1; } @Override - public String getThreadGroupName() { + public String threadGroupName() { return "ServerNetworkThread"; } }; /** * Get a minimal size of network thread executor. - * - * @return the minimal executor size. */ - default int getThreadGroupMinSize() { + default int threadGroupMinSize() { return 1; } /** * Get a maximum size of network thread executor. - * - * @return the maximum executor size. */ - default int getThreadGroupMaxSize() { - return getThreadGroupMinSize(); + default int threadGroupMaxSize() { + return threadGroupMinSize(); } /** * Get a thread constructor which should be used to create network threads. - * - * @return the thread constructor. */ - default GroupThreadFactory.ThreadConstructor getThreadConstructor() { + default ThreadConstructor threadConstructor() { return Thread::new; } @@ -80,7 +76,7 @@ default GroupThreadFactory.ThreadConstructor getThreadConstructor() { * * @return the priority of network threads. */ - default int getThreadPriority() { + default int threadPriority() { return Thread.NORM_PRIORITY; } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/UnsafeConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/UnsafeConnection.java index 3ab99e2b..7447fc97 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/UnsafeConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/UnsafeConnection.java @@ -1,10 +1,10 @@ package javasabr.rlib.network; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.packet.WritablePacket; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; -public interface UnsafeConnection extends Connection { +public interface UnsafeConnection + extends Connection { void onConnected(); - } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/annotation/PacketDescription.java b/rlib-network/src/main/java/javasabr/rlib/network/annotation/NetworkPacketDescription.java similarity index 90% rename from rlib-network/src/main/java/javasabr/rlib/network/annotation/PacketDescription.java rename to rlib-network/src/main/java/javasabr/rlib/network/annotation/NetworkPacketDescription.java index 48a8a37f..7c75856e 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/annotation/PacketDescription.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/annotation/NetworkPacketDescription.java @@ -14,7 +14,6 @@ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) -public @interface PacketDescription { - +public @interface NetworkPacketDescription { int id(); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/client/ClientNetwork.java b/rlib-network/src/main/java/javasabr/rlib/network/client/ClientNetwork.java index 836d28d7..e8a4b7ed 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/client/ClientNetwork.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/client/ClientNetwork.java @@ -1,6 +1,7 @@ package javasabr.rlib.network.client; import java.net.InetSocketAddress; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; @@ -16,25 +17,27 @@ public interface ClientNetwork> extends Network { /** * Connect to a server by the address. - * - * @param serverAddress the sever address. - * @return the future with result connection. */ - CompletableFuture connect(InetSocketAddress serverAddress); + C connect(InetSocketAddress serverAddress); /** * Connect to a server by the address. - * - * @param serverAddress the sever address. - * @return the future with result connection. */ - Mono connected(InetSocketAddress serverAddress); + CompletableFuture connectAsync(InetSocketAddress serverAddress); + + /** + * Connect to a server by the address. + */ + Mono connectReactive(InetSocketAddress serverAddress); /** * Get a current connection to a server or null. - * - * @return the current connection or null. */ @Nullable - C getCurrentConnection(); + C currentConnection(); + + /** + * Get a current connection to a server or empty. + */ + Optional currentConnectionOptional(); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/client/impl/DefaultClientNetwork.java b/rlib-network/src/main/java/javasabr/rlib/network/client/impl/DefaultClientNetwork.java index 81f612a0..b682fed9 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/client/impl/DefaultClientNetwork.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/client/impl/DefaultClientNetwork.java @@ -1,9 +1,7 @@ package javasabr.rlib.network.client.impl; -import static javasabr.rlib.common.util.Utils.unchecked; -import static javasabr.rlib.common.util.Utils.uncheckedGet; - import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.Optional; @@ -12,15 +10,18 @@ import java.util.function.BiFunction; import javasabr.rlib.common.util.AsyncUtils; import javasabr.rlib.common.util.ThreadUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; +import javasabr.rlib.common.util.Utils; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; import javasabr.rlib.network.NetworkConfig; import javasabr.rlib.network.client.ClientNetwork; import javasabr.rlib.network.impl.AbstractNetwork; import javasabr.rlib.network.util.NetworkUtils; +import lombok.AccessLevel; +import lombok.CustomLog; import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; @@ -29,69 +30,69 @@ * * @author JavaSaBr */ +@CustomLog +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) public class DefaultClientNetwork> extends AbstractNetwork implements ClientNetwork { - protected static final Logger LOGGER = LoggerManager.getLogger(DefaultClientNetwork.class); + final AtomicBoolean connecting; - protected final AtomicBoolean connecting; + @Nullable + @Getter(AccessLevel.PROTECTED) + volatile CompletableFuture pendingConnection; - protected volatile @Nullable CompletableFuture pendingConnection; - protected volatile @Getter - @Nullable C currentConnection; + @Getter + @Nullable + volatile C currentConnection; public DefaultClientNetwork( NetworkConfig config, BiFunction, AsynchronousSocketChannel, C> channelToConnection) { super(config, channelToConnection); this.connecting = new AtomicBoolean(false); - - LOGGER.info( - config, - conf -> "Client network configuration: {\n" + " groupName: \"" + conf.getThreadGroupName() + "\",\n" - + " readBufferSize: " + conf.getReadBufferSize() + ",\n" + " pendingBufferSize: " - + conf.getPendingBufferSize() + ",\n" + " writeBufferSize: " + conf.getWriteBufferSize() + "\n" + "}"); + log.info(config, DefaultClientNetwork::buildConfigDescription); } @Override - public CompletableFuture connect(InetSocketAddress serverAddress) { + public C connect(InetSocketAddress serverAddress) { + return connectAsync(serverAddress).join(); + } - C currentConnection = getCurrentConnection(); + @Override + public CompletableFuture connectAsync(InetSocketAddress serverAddress) { + C currentConnection = currentConnection(); if (currentConnection != null) { - unchecked(currentConnection, C::close); + Utils.unchecked(currentConnection, C::close); } // if we are trying connection now if (!connecting.compareAndSet(false, true)) { - - var asyncResult = this.pendingConnection; - - if (asyncResult != null) { - return asyncResult; + CompletableFuture pendingConnection = pendingConnection(); + if (pendingConnection != null) { + return pendingConnection; } - ThreadUtils.sleep(100); - - return connect(serverAddress); + return connectAsync(serverAddress); } var asyncResult = new CompletableFuture(); - var channel = uncheckedGet(AsynchronousSocketChannel::open); - channel.connect( - serverAddress, null, new CompletionHandler() { - - @Override - public void completed(@Nullable Void result, @Nullable Void attachment) { - LOGGER.info(channel, ch -> "Connected to server: " + NetworkUtils.getRemoteAddress(ch)); - asyncResult.complete(channelToConnection.apply(DefaultClientNetwork.this, channel)); - } + @SuppressWarnings("resource") + var channel = Utils.uncheckedGet(AsynchronousSocketChannel::open); + channel.connect(serverAddress, this, new CompletionHandler<>() { + @Override + public void completed(@Nullable Void result, DefaultClientNetwork network) { + SocketAddress remoteAddress = NetworkUtils.getRemoteAddress(channel); + log.info(remoteAddress, "Connected to server:[%s]"::formatted); + asyncResult.complete(channelToConnection.apply(network, channel)); + } - @Override - public void failed(Throwable exc, @Nullable Void attachment) { - asyncResult.completeExceptionally(exc); - } - }); + @Override + public void failed(Throwable exc, DefaultClientNetwork attachment) { + asyncResult.completeExceptionally(exc); + } + }); pendingConnection = asyncResult; @@ -103,20 +104,34 @@ public void failed(Throwable exc, @Nullable Void attachment) { } @Override - public Mono connected(InetSocketAddress serverAddress) { - return Mono.create(monoSink -> connect(serverAddress).whenComplete((connection, ex) -> { - if (ex != null) { - monoSink.error(ex); - } else { - monoSink.success(connection); - } - })); + public Mono connectReactive(InetSocketAddress serverAddress) { + return Mono + .create(monoSink -> connectAsync(serverAddress) + .whenComplete((connection, ex) -> { + if (ex != null) { + monoSink.error(ex); + } else { + monoSink.success(connection); + } + })); + } + + @Override + public Optional currentConnectionOptional() { + return Optional.ofNullable(currentConnection()); } @Override public void shutdown() { - Optional - .ofNullable(getCurrentConnection()) - .ifPresent(connection -> unchecked(connection, C::close)); + C connection = currentConnection(); + if (connection != null) { + Utils.unchecked(connection, C::close); + } + } + + private static String buildConfigDescription(NetworkConfig conf) { + return "Client network configuration: {\n" + " groupName: \"" + conf.threadGroupName() + "\",\n" + + " readBufferSize: " + conf.readBufferSize() + ",\n" + " pendingBufferSize: " + conf.pendingBufferSize() + + ",\n" + " writeBufferSize: " + conf.writeBufferSize() + "\n" + "}"; } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractConnection.java index 6928ea7a..26c78c55 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractConnection.java @@ -12,19 +12,21 @@ import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.collections.deque.DequeFactory; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; import javasabr.rlib.network.UnsafeConnection; -import javasabr.rlib.network.packet.PacketReader; -import javasabr.rlib.network.packet.PacketWriter; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.packet.WritablePacket; +import javasabr.rlib.network.packet.NetworkPacketReader; +import javasabr.rlib.network.packet.NetworkPacketWriter; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; import javasabr.rlib.network.packet.impl.WritablePacketWrapper; import javasabr.rlib.network.util.NetworkUtils; +import lombok.AccessLevel; +import lombok.CustomLog; import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; @@ -34,12 +36,13 @@ * * @author JavaSaBr */ -public abstract class AbstractConnection implements +@CustomLog +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractConnection implements UnsafeConnection { - private static final Logger LOGGER = LoggerManager.getLogger(AbstractConnection.class); - - private static class WritablePacketWithFeedback extends + private static class WritablePacketWithFeedback extends WritablePacketWrapper, W> { public WritablePacketWithFeedback(CompletableFuture attachment, W packet) { @@ -47,23 +50,24 @@ public WritablePacketWithFeedback(CompletableFuture attachment, W packe } } - protected final @Getter - String remoteAddress; + @Getter + final String remoteAddress; - protected final Network> network; - protected final BufferAllocator bufferAllocator; - protected final AsynchronousSocketChannel channel; - protected final Deque pendingPackets; - protected final StampedLock lock; + final Network> network; + final BufferAllocator bufferAllocator; + final AsynchronousSocketChannel channel; + final Deque pendingPackets; + final StampedLock lock; - protected final AtomicBoolean isWriting; - protected final AtomicBoolean closed; + final AtomicBoolean isWriting; + final AtomicBoolean closed; - protected final MutableArray, ? super R>> subscribers; + final MutableArray, ? super R>> subscribers; - protected final int maxPacketsByRead; + final int maxPacketsByRead; - protected volatile @Getter long lastActivity; + @Getter + volatile long lastActivity; public AbstractConnection( Network> network, @@ -74,7 +78,7 @@ public AbstractConnection( this.maxPacketsByRead = maxPacketsByRead; this.lock = new StampedLock(); this.channel = channel; - this.pendingPackets = DequeFactory.linkedListBased(); + this.pendingPackets = DequeFactory.arrayBasedBased(WritableNetworkPacket.class); this.network = network; this.isWriting = new AtomicBoolean(false); this.closed = new AtomicBoolean(false); @@ -83,28 +87,16 @@ public AbstractConnection( } @Override - public void onConnected() { - } - - protected abstract PacketReader getPacketReader(); + public void onConnected() {} - protected abstract PacketWriter getPacketWriter(); + protected abstract NetworkPacketReader packetReader(); - protected void handleReceivedPacket(R packet) { - LOGGER.debug( - channel, - packet, - (ch, pck) -> "Handle received packet: %s from: %s".formatted(pck, NetworkUtils.getRemoteAddress(ch))); - - subscribers - .iterations() - .forEach(this, packet, BiConsumer::accept); - } + protected abstract NetworkPacketWriter packetWriter(); @Override public void onReceive(BiConsumer, ? super R> consumer) { subscribers.add(consumer); - getPacketReader().startRead(); + packetReader().startRead(); } @Override @@ -130,15 +122,13 @@ protected void registerFluxOnReceivedEvents( } protected void registerFluxOnReceivedPackets(FluxSink sink) { - BiConsumer, R> listener = (connection, packet) -> sink.next(packet); - onReceive(listener); - sink.onDispose(() -> subscribers.remove(listener)); } - protected @Nullable WritablePacket nextPacketToWrite() { + @Nullable + protected WritableNetworkPacket nextPacketToWrite() { long stamp = lock.writeLock(); try { return pendingPackets.poll(); @@ -158,15 +148,12 @@ public void close() { * Does the process of closing this connection. */ protected void doClose() { - if (channel.isOpen()) { unchecked(channel, AsynchronousChannel::close); } - clearWaitPackets(); - - getPacketReader().close(); - getPacketWriter().close(); + packetReader().close(); + packetWriter().close(); } /** @@ -177,14 +164,20 @@ protected void updateLastActivity() { } @Override - public boolean isClosed() { + public boolean closed() { return closed.get(); } - protected void onWrittenPacket(WritablePacket packet) { + protected void serializedPacket(WritableNetworkPacket packet) {} + + protected void handleReceivedPacket(R packet) { + log.debug(packet, remoteAddress, "Handle received packet:[%s] from:[%s]"::formatted); + subscribers + .iterations() + .forEach(this, packet, BiConsumer::accept); } - protected void onSentPacket(WritablePacket packet, Boolean result) { + protected void handleSentPacket(WritableNetworkPacket packet, boolean result) { if (packet instanceof WritablePacketWithFeedback) { ((WritablePacketWithFeedback) packet) .getAttachment() @@ -197,23 +190,23 @@ public final void send(W packet) { sendImpl(packet); } - protected void sendImpl(WritablePacket packet) { + protected void sendImpl(WritableNetworkPacket packet) { - if (isClosed()) { + if (closed()) { return; } long stamp = lock.writeLock(); try { - pendingPackets.add(packet); + pendingPackets.addLast(packet); } finally { lock.unlockWrite(stamp); } - getPacketWriter().writeNextPacket(); + packetWriter().tryToSendNextPacket(); } - protected void queueAtFirst(WritablePacket packet) { + protected void queueAtFirst(WritableNetworkPacket packet) { long stamp = lock.writeLock(); try { pendingPackets.addFirst(packet); @@ -229,7 +222,7 @@ public CompletableFuture sendWithFeedback(W packet) { sendImpl(new WritablePacketWithFeedback<>(asyncResult, packet)); - if (isClosed()) { + if (closed()) { return CompletableFuture.completedFuture(Boolean.FALSE); } @@ -251,9 +244,14 @@ protected void clearWaitPackets() { protected void doClearWaitPackets() { for (var pendingPacket : pendingPackets) { - onSentPacket(pendingPacket, Boolean.FALSE); + handleSentPacket(pendingPacket, false); } pendingPackets.clear(); } + + @Override + public String toString() { + return "{" + remoteAddress + '}'; + } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractNetwork.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractNetwork.java index d2b8e76a..592fc997 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractNetwork.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractNetwork.java @@ -2,28 +2,23 @@ import java.nio.channels.AsynchronousSocketChannel; import java.util.function.BiFunction; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; import javasabr.rlib.network.NetworkConfig; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; /** * The base implementation of {@link Network}. * * @author JavaSaBr */ +@CustomLog +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public abstract class AbstractNetwork> implements Network { - - protected static final Logger LOGGER = LoggerManager.getLogger(AbstractNetwork.class); - - protected final NetworkConfig config; - protected final BiFunction, AsynchronousSocketChannel, C> channelToConnection; - - protected AbstractNetwork( - NetworkConfig config, - BiFunction, AsynchronousSocketChannel, C> channelToConnection) { - this.config = config; - this.channelToConnection = channelToConnection; - } + NetworkConfig config; + BiFunction, AsynchronousSocketChannel, C> channelToConnection; } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractSSLConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractSslConnection.java similarity index 62% rename from rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractSSLConnection.java rename to rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractSslConnection.java index 80a4ae16..d88aab8b 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractSSLConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/AbstractSslConnection.java @@ -4,18 +4,21 @@ import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.packet.WritablePacket; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; -public abstract class AbstractSSLConnection extends - AbstractConnection { +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractSslConnection + extends AbstractConnection { - protected final SSLEngine sslEngine; + final SSLEngine sslEngine; - public AbstractSSLConnection( + public AbstractSslConnection( Network> network, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, @@ -33,8 +36,8 @@ public AbstractSSLConnection( } @Override - protected void sendImpl(WritablePacket packet) { + protected void sendImpl(WritableNetworkPacket packet) { super.sendImpl(packet); - getPacketReader().startRead(); + packetReader().startRead(); } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultBufferAllocator.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultBufferAllocator.java index 4ffbc7d6..a23fe361 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultBufferAllocator.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultBufferAllocator.java @@ -1,13 +1,14 @@ package javasabr.rlib.network.impl; import java.nio.ByteBuffer; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.NetworkConfig; import javasabr.rlib.reusable.pool.Pool; import javasabr.rlib.reusable.pool.PoolFactory; +import lombok.AccessLevel; +import lombok.CustomLog; import lombok.ToString; +import lombok.experimental.FieldDefaults; /** * The default byte buffer allocator. @@ -15,15 +16,15 @@ * @author JavaSaBr */ @ToString +@CustomLog +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public class DefaultBufferAllocator implements BufferAllocator { - private static final Logger LOGGER = LoggerManager.getLogger(DefaultBufferAllocator.class); + final Pool readBufferPool; + final Pool pendingBufferPool; + final Pool writeBufferPool; - protected final Pool readBufferPool; - protected final Pool pendingBufferPool; - protected final Pool writeBufferPool; - - protected final NetworkConfig config; + final NetworkConfig config; public DefaultBufferAllocator(NetworkConfig config) { this.config = config; @@ -34,72 +35,72 @@ public DefaultBufferAllocator(NetworkConfig config) { @Override public ByteBuffer takeReadBuffer() { - var bufferSize = config.getReadBufferSize(); - LOGGER.debug(bufferSize, size -> "Allocate a new read buffer with size: " + size); + int bufferSize = config.readBufferSize(); + log.debug(bufferSize, "Allocate new read buffer with size:[%s]"::formatted); return config.isDirectByteBuffer() ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer .allocate(bufferSize) - .order(config.getByteOrder()) + .order(config.byteOrder()) .clear(); } @Override public ByteBuffer takePendingBuffer() { - var bufferSize = config.getPendingBufferSize(); - LOGGER.debug(bufferSize, size -> "Allocate a new pending buffer with size: " + size); + int bufferSize = config.pendingBufferSize(); + log.debug(bufferSize, "Allocate new pending buffer with size:[%s]"::formatted); return config.isDirectByteBuffer() ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer .allocate(bufferSize) - .order(config.getByteOrder()) + .order(config.byteOrder()) .clear(); } @Override public ByteBuffer takeWriteBuffer() { - var bufferSize = config.getWriteBufferSize(); - LOGGER.debug(bufferSize, size -> "Allocate a new write buffer with size: " + size); + int bufferSize = config.writeBufferSize(); + log.debug(bufferSize, "Allocate new write buffer with size:[%s]"::formatted); return config.isDirectByteBuffer() ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer .allocate(bufferSize) - .order(config.getByteOrder()) + .order(config.byteOrder()) .clear(); } @Override public ByteBuffer takeBuffer(int bufferSize) { - LOGGER.debug(bufferSize, size -> "Allocate a new buffer with size: " + size); + log.debug(bufferSize, "Allocate new buffer with size:[%s]"::formatted); return config.isDirectByteBuffer() ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer .allocate(bufferSize) - .order(config.getByteOrder()) + .order(config.byteOrder()) .clear(); } @Override public DefaultBufferAllocator putReadBuffer(ByteBuffer buffer) { - LOGGER.debug("Skip storing a read buffer"); + log.debug("Skip storing read buffer"); return this; } @Override public DefaultBufferAllocator putPendingBuffer(ByteBuffer buffer) { - LOGGER.debug("Skip storing a pending buffer"); + log.debug("Skip storing pending buffer"); return this; } @Override public DefaultBufferAllocator putWriteBuffer(ByteBuffer buffer) { - LOGGER.debug("Skip storing a write buffer"); + log.debug("Skip storing write buffer"); return this; } @Override public BufferAllocator putBuffer(ByteBuffer buffer) { - LOGGER.debug("Skip storing a mapped byte buffer"); + log.debug("Skip storing buffer"); return this; } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultConnection.java index c14df701..26a404ad 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultConnection.java @@ -4,20 +4,21 @@ import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; -import javasabr.rlib.network.packet.impl.DefaultReadablePacket; -import javasabr.rlib.network.packet.impl.DefaultWritablePacket; -import javasabr.rlib.network.packet.registry.ReadablePacketRegistry; +import javasabr.rlib.network.packet.impl.DefaultReadableNetworkPacket; +import javasabr.rlib.network.packet.impl.DefaultWritableNetworkPacket; +import javasabr.rlib.network.packet.registry.ReadableNetworkPacketRegistry; /** * @author JavaSaBr */ -public class DefaultConnection extends IdBasedPacketConnection { +public class DefaultConnection extends + IdBasedPacketConnection { public DefaultConnection( - Network> network, + Network> network, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, - ReadablePacketRegistry packetRegistry) { + ReadableNetworkPacketRegistry packetRegistry) { super(network, channel, bufferAllocator, packetRegistry, 100, 2, 2); } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataConnection.java index c9146330..8d6667cc 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataConnection.java @@ -4,26 +4,30 @@ import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; -import javasabr.rlib.network.packet.PacketReader; -import javasabr.rlib.network.packet.PacketWriter; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.packet.WritablePacket; -import javasabr.rlib.network.packet.impl.DefaultPacketReader; -import javasabr.rlib.network.packet.impl.DefaultPacketWriter; +import javasabr.rlib.network.packet.NetworkPacketReader; +import javasabr.rlib.network.packet.NetworkPacketWriter; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; +import javasabr.rlib.network.packet.impl.DefaultNetworkPacketReader; +import javasabr.rlib.network.packet.impl.DefaultNetworkPacketWriter; import lombok.AccessLevel; import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; /** * @author JavaSaBr */ @Getter(AccessLevel.PROTECTED) -public abstract class DefaultDataConnection extends - AbstractConnection { +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class DefaultDataConnection + extends AbstractConnection { - private final PacketReader packetReader; - private final PacketWriter packetWriter; + final NetworkPacketReader packetReader; + final NetworkPacketWriter packetWriter; - private final int packetLengthHeaderSize; + final int packetLengthHeaderSize; public DefaultDataConnection( Network> network, @@ -37,8 +41,8 @@ public DefaultDataConnection( this.packetWriter = createPacketWriter(); } - protected PacketReader createPacketReader() { - return new DefaultPacketReader<>( + protected NetworkPacketReader createPacketReader() { + return new DefaultNetworkPacketReader<>( this, channel, bufferAllocator, @@ -49,15 +53,15 @@ protected PacketReader createPacketReader() { maxPacketsByRead); } - protected PacketWriter createPacketWriter() { - return new DefaultPacketWriter>( + protected NetworkPacketWriter createPacketWriter() { + return new DefaultNetworkPacketWriter>( this, channel, bufferAllocator, this::updateLastActivity, this::nextPacketToWrite, - this::onWrittenPacket, - this::onSentPacket, + this::serializedPacket, + this::handleSentPacket, packetLengthHeaderSize); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataSSLConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataSslConnection.java similarity index 54% rename from rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataSSLConnection.java rename to rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataSslConnection.java index 283220d2..2708e94a 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataSSLConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/DefaultDataSslConnection.java @@ -4,29 +4,33 @@ import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; -import javasabr.rlib.network.packet.PacketReader; -import javasabr.rlib.network.packet.PacketWriter; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.packet.WritablePacket; -import javasabr.rlib.network.packet.impl.DefaultSSLPacketReader; -import javasabr.rlib.network.packet.impl.DefaultSSLPacketWriter; +import javasabr.rlib.network.packet.NetworkPacketReader; +import javasabr.rlib.network.packet.NetworkPacketWriter; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; +import javasabr.rlib.network.packet.impl.DefaultSslNetworkPacketReader; +import javasabr.rlib.network.packet.impl.DefaultSslNetworkPacketWriter; import javax.net.ssl.SSLContext; import lombok.AccessLevel; import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; /** * @author JavaSaBr */ @Getter(AccessLevel.PROTECTED) -public abstract class DefaultDataSSLConnection extends - AbstractSSLConnection { +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class DefaultDataSslConnection + extends AbstractSslConnection { - private final PacketReader packetReader; - private final PacketWriter packetWriter; + final NetworkPacketReader packetReader; + final NetworkPacketWriter packetWriter; - private final int packetLengthHeaderSize; + final int packetLengthHeaderSize; - public DefaultDataSSLConnection( + public DefaultDataSslConnection( Network> network, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, @@ -40,8 +44,8 @@ public DefaultDataSSLConnection( this.packetWriter = createPacketWriter(); } - protected PacketReader createPacketReader() { - return new DefaultSSLPacketReader<>( + protected NetworkPacketReader createPacketReader() { + return new DefaultSslNetworkPacketReader<>( this, channel, bufferAllocator, @@ -54,17 +58,16 @@ protected PacketReader createPacketReader() { maxPacketsByRead); } - protected PacketWriter createPacketWriter() { - return new DefaultSSLPacketWriter>( + protected NetworkPacketWriter createPacketWriter() { + return new DefaultSslNetworkPacketWriter>( this, channel, bufferAllocator, this::updateLastActivity, this::nextPacketToWrite, - this::onWrittenPacket, - this::onSentPacket, + this::serializedPacket, + this::handleSentPacket, sslEngine, - this::sendImpl, this::queueAtFirst, packetLengthHeaderSize); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/IdBasedPacketConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/IdBasedPacketConnection.java index 3f98fa0b..d71d528a 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/IdBasedPacketConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/IdBasedPacketConnection.java @@ -4,35 +4,39 @@ import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; -import javasabr.rlib.network.packet.IdBasedReadablePacket; -import javasabr.rlib.network.packet.IdBasedWritablePacket; -import javasabr.rlib.network.packet.PacketReader; -import javasabr.rlib.network.packet.PacketWriter; +import javasabr.rlib.network.packet.IdBasedReadableNetworkPacket; +import javasabr.rlib.network.packet.IdBasedWritableNetworkPacket; +import javasabr.rlib.network.packet.NetworkPacketReader; +import javasabr.rlib.network.packet.NetworkPacketWriter; import javasabr.rlib.network.packet.impl.IdBasedPacketReader; -import javasabr.rlib.network.packet.impl.IdBasedPacketWriter; -import javasabr.rlib.network.packet.registry.ReadablePacketRegistry; +import javasabr.rlib.network.packet.impl.IdBasedNetworkPacketWriter; +import javasabr.rlib.network.packet.registry.ReadableNetworkPacketRegistry; import lombok.AccessLevel; import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; /** * @author JavaSaBr */ @Getter(AccessLevel.PROTECTED) -public class IdBasedPacketConnection, W extends IdBasedWritablePacket> extends - AbstractConnection { +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public class IdBasedPacketConnection, W extends IdBasedWritableNetworkPacket> + extends AbstractConnection { - private final PacketReader packetReader; - private final PacketWriter packetWriter; - private final ReadablePacketRegistry packetRegistry; + final NetworkPacketReader packetReader; + final NetworkPacketWriter packetWriter; + final ReadableNetworkPacketRegistry packetRegistry; - private final int packetLengthHeaderSize; - private final int packetIdHeaderSize; + final int packetLengthHeaderSize; + final int packetIdHeaderSize; public IdBasedPacketConnection( Network> network, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, - ReadablePacketRegistry packetRegistry, + ReadableNetworkPacketRegistry packetRegistry, int maxPacketsByRead, int packetLengthHeaderSize, int packetIdHeaderSize) { @@ -44,7 +48,7 @@ public IdBasedPacketConnection( this.packetWriter = createPacketWriter(); } - protected PacketReader createPacketReader() { + protected NetworkPacketReader createPacketReader() { return new IdBasedPacketReader<>( this, channel, @@ -57,15 +61,15 @@ protected PacketReader createPacketReader() { packetRegistry); } - protected PacketWriter createPacketWriter() { - return new IdBasedPacketWriter<>( + protected NetworkPacketWriter createPacketWriter() { + return new IdBasedNetworkPacketWriter<>( this, channel, bufferAllocator, this::updateLastActivity, this::nextPacketToWrite, - this::onWrittenPacket, - this::onSentPacket, + this::serializedPacket, + this::handleSentPacket, packetLengthHeaderSize, packetIdHeaderSize); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/ReuseBufferAllocator.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/ReuseBufferAllocator.java index 8b578ba3..fa771afa 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/ReuseBufferAllocator.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/ReuseBufferAllocator.java @@ -4,22 +4,20 @@ import java.util.function.Function; import javasabr.rlib.collections.array.ArrayFactory; import javasabr.rlib.collections.array.LockableArray; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.NetworkConfig; import javasabr.rlib.reusable.pool.Pool; import javasabr.rlib.reusable.pool.PoolFactory; +import lombok.CustomLog; import lombok.ToString; /** * @author JavaSaBr */ @ToString +@CustomLog public class ReuseBufferAllocator implements BufferAllocator { - protected static final Logger LOGGER = LoggerManager.getLogger(ReuseBufferAllocator.class); - protected final Pool readBufferPool; protected final Pool pendingBufferPool; protected final Pool writeBufferPool; @@ -58,39 +56,39 @@ public ByteBuffer takeWriteBuffer() { protected Function pendingBufferFactory() { return config -> { - var bufferSize = config.getPendingBufferSize(); - LOGGER.debug(bufferSize, size -> "Allocate a new pending buffer with size: " + size); + var bufferSize = config.pendingBufferSize(); + log.debug(bufferSize, "Allocate new pending buffer with size:[%s]"::formatted); return config.isDirectByteBuffer() ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer .allocate(bufferSize) - .order(config.getByteOrder()) + .order(config.byteOrder()) .clear(); }; } protected Function readBufferFactory() { return config -> { - var bufferSize = config.getReadBufferSize(); - LOGGER.debug(bufferSize, size -> "Allocate a new read buffer with size: " + size); + var bufferSize = config.readBufferSize(); + log.debug(bufferSize, "Allocate new read buffer with size:[%s]"::formatted); return config.isDirectByteBuffer() ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer .allocate(bufferSize) - .order(config.getByteOrder()) + .order(config.byteOrder()) .clear(); }; } protected Function writeBufferFactory() { return config -> { - var bufferSize = config.getWriteBufferSize(); - LOGGER.debug(bufferSize, size -> "Allocate a new write buffer with size: " + size); + var bufferSize = config.writeBufferSize(); + log.debug(bufferSize, "Allocate new write buffer with size:[%s]"::formatted); return config.isDirectByteBuffer() ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer .allocate(bufferSize) - .order(config.getByteOrder()) + .order(config.byteOrder()) .clear(); }; } @@ -122,7 +120,7 @@ public ByteBuffer takeBuffer(int bufferSize) { // take it from pool if exist if (exist != null && byteBuffers.remove(exist)) { - LOGGER.debug(exist, buffer -> "Reuse old buffer: " + buffer + " - (" + buffer.hashCode() + ")"); + log.debug(exist, buffer -> "Reuse old buffer: " + buffer + " - (" + buffer.hashCode() + ")"); return exist; } @@ -131,12 +129,12 @@ public ByteBuffer takeBuffer(int bufferSize) { } } - LOGGER.debug(bufferSize, size -> "Allocate a new buffer with size: " + size); + log.debug(bufferSize, "Allocate a new buffer with size:[%s]"::formatted); return config.isDirectByteBuffer() ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer .allocate(bufferSize) - .order(config.getByteOrder()) + .order(config.byteOrder()) .clear(); } @@ -160,7 +158,7 @@ public ReuseBufferAllocator putWriteBuffer(ByteBuffer buffer) { @Override public BufferAllocator putBuffer(ByteBuffer buffer) { - LOGGER.debug(buffer, buf -> "Save used temp buffer: " + buf + " - (" + buf.hashCode() + ")"); + log.debug(buffer, buf -> "Save used temp buffer: " + buf + " - (" + buf.hashCode() + ")"); long stamp = byteBuffers.writeLock(); try { byteBuffers.add(buffer.clear()); diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataConnection.java index cbad855b..52d61ea9 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataConnection.java @@ -5,15 +5,15 @@ import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; import javasabr.rlib.network.packet.impl.StringReadablePacket; -import javasabr.rlib.network.packet.impl.StringWritablePacket; +import javasabr.rlib.network.packet.impl.StringWritableNetworkPacket; /** * @author JavaSaBr */ -public class StringDataConnection extends DefaultDataConnection { +public class StringDataConnection extends DefaultDataConnection { public StringDataConnection( - Network> network, + Network> network, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator) { super(network, channel, bufferAllocator, 100, 2); diff --git a/rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataSSLConnection.java b/rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataSslConnection.java similarity index 73% rename from rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataSSLConnection.java rename to rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataSslConnection.java index e07e5050..c97363da 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataSSLConnection.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataSslConnection.java @@ -5,16 +5,17 @@ import javasabr.rlib.network.Connection; import javasabr.rlib.network.Network; import javasabr.rlib.network.packet.impl.StringReadablePacket; -import javasabr.rlib.network.packet.impl.StringWritablePacket; +import javasabr.rlib.network.packet.impl.StringWritableNetworkPacket; import javax.net.ssl.SSLContext; /** * @author JavaSaBr */ -public class StringDataSSLConnection extends DefaultDataSSLConnection { +public class StringDataSslConnection extends + DefaultDataSslConnection { - public StringDataSSLConnection( - Network> network, + public StringDataSslConnection( + Network> network, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, SSLContext sslContext, diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedNetworkPacket.java new file mode 100644 index 00000000..cf32dfd3 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedNetworkPacket.java @@ -0,0 +1,20 @@ +package javasabr.rlib.network.packet; + +import javasabr.rlib.network.annotation.NetworkPacketDescription; + +/** + * @author JavaSaBr + */ +public interface IdBasedNetworkPacket extends NetworkPacket { + + /** + * Get id of this packet. + * + * @return the packet type's id. + */ + default int packetId() { + return getClass() + .getAnnotation(NetworkPacketDescription.class) + .id(); + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedPacket.java deleted file mode 100644 index 7be950ab..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedPacket.java +++ /dev/null @@ -1,20 +0,0 @@ -package javasabr.rlib.network.packet; - -import javasabr.rlib.network.annotation.PacketDescription; - -/** - * @author JavaSaBr - */ -public interface IdBasedPacket extends Packet { - - /** - * Get id of this packet. - * - * @return the packet type's id. - */ - default int getPacketId() { - return getClass() - .getAnnotation(PacketDescription.class) - .id(); - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedReadableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedReadableNetworkPacket.java new file mode 100644 index 00000000..9f3fa344 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedReadableNetworkPacket.java @@ -0,0 +1,18 @@ +package javasabr.rlib.network.packet; + +import javasabr.rlib.common.util.ClassUtils; +import javasabr.rlib.network.Connection; + +/** + * @author JavaSaBr + */ +public interface IdBasedReadableNetworkPacket> + extends ReadableNetworkPacket, IdBasedNetworkPacket { + + /** + * Create a new instance of this type. + */ + default S newInstance() { + return ClassUtils.newInstance(getClass()); + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedReadablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedReadablePacket.java deleted file mode 100644 index ee45a273..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedReadablePacket.java +++ /dev/null @@ -1,28 +0,0 @@ -package javasabr.rlib.network.packet; - -import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.network.Connection; - -/** - * @author JavaSaBr - */ -public interface IdBasedReadablePacket> extends ReadablePacket, IdBasedPacket { - - /** - * Create a new instance of this type. - * - * @return the new instance of this type. - */ - default S newInstance() { - return ClassUtils.newInstance(getClass()); - } - - /** - * Execute a logic of this packet. - * - * @param connection the owner's connection. - */ - default void execute(Connection connection) { - - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedWritableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedWritableNetworkPacket.java new file mode 100644 index 00000000..d157e6eb --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedWritableNetworkPacket.java @@ -0,0 +1,7 @@ +package javasabr.rlib.network.packet; + +/** + * @author JavaSaBr + */ +public interface IdBasedWritableNetworkPacket extends WritableNetworkPacket, IdBasedNetworkPacket { +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedWritablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedWritablePacket.java deleted file mode 100644 index 2907a2e6..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/IdBasedWritablePacket.java +++ /dev/null @@ -1,8 +0,0 @@ -package javasabr.rlib.network.packet; - -/** - * @author JavaSaBr - */ -public interface IdBasedWritablePacket extends WritablePacket, IdBasedPacket { - -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/MarkerNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/MarkerNetworkPacket.java new file mode 100644 index 00000000..fe98047c --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/MarkerNetworkPacket.java @@ -0,0 +1,6 @@ +package javasabr.rlib.network.packet; + +/** + * Interface to mark that some specific packet doesn't have any data. + */ +public interface MarkerNetworkPacket {} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/Packet.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacket.java similarity index 68% rename from rlib-network/src/main/java/javasabr/rlib/network/packet/Packet.java rename to rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacket.java index 8be12d1c..5ee76d47 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/Packet.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacket.java @@ -5,12 +5,10 @@ * * @author JavaSaBr */ -public interface Packet { +public interface NetworkPacket { /** - * Get packet's name. - * * @return the packet's name. */ - String getName(); + String name(); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/PacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacketReader.java similarity index 84% rename from rlib-network/src/main/java/javasabr/rlib/network/packet/PacketReader.java rename to rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacketReader.java index 1940e916..6111320f 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/PacketReader.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacketReader.java @@ -3,7 +3,7 @@ /** * @author JavaSaBr */ -public interface PacketReader { +public interface NetworkPacketReader { /** * Activate a process of receiving packets. diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacketWriter.java new file mode 100644 index 00000000..a2c1d985 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/NetworkPacketWriter.java @@ -0,0 +1,17 @@ +package javasabr.rlib.network.packet; + +/** + * @author JavaSaBr + */ +public interface NetworkPacketWriter { + + /** + * @return true if the writer starting writing new data to channel. + */ + boolean tryToSendNextPacket(); + + /** + * Close all used resources. + */ + void close(); +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/PacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/PacketWriter.java deleted file mode 100644 index db1d428c..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/PacketWriter.java +++ /dev/null @@ -1,14 +0,0 @@ -package javasabr.rlib.network.packet; - -/** - * @author JavaSaBr - */ -public interface PacketWriter { - - void writeNextPacket(); - - /** - * Close all used resources. - */ - void close(); -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/ReadablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/ReadableNetworkPacket.java similarity index 55% rename from rlib-network/src/main/java/javasabr/rlib/network/packet/ReadablePacket.java rename to rlib-network/src/main/java/javasabr/rlib/network/packet/ReadableNetworkPacket.java index 58eab190..9438e58a 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/ReadablePacket.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/ReadableNetworkPacket.java @@ -1,22 +1,20 @@ package javasabr.rlib.network.packet; import java.nio.ByteBuffer; -import javasabr.rlib.network.Connection; /** * The interface to implement a readable network packet. * * @author JavaSaBr */ -public interface ReadablePacket extends Packet { +public interface ReadableNetworkPacket extends NetworkPacket { /** * Read packet's data from byte buffer. * - * @param connection the network connection. * @param buffer the buffer with received data. - * @param length the data length. + * @param remainingDataLength the expected remaining data length. * @return true if reading was success. */ - boolean read(Connection connection, ByteBuffer buffer, int length); + boolean read(ByteBuffer buffer, int remainingDataLength); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/ReusableWritablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/ReusableWritablePacket.java index b4eb1b06..bfbc96ba 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/ReusableWritablePacket.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/ReusableWritablePacket.java @@ -8,7 +8,7 @@ * * @author JavaSaBr */ -public interface ReusableWritablePacket extends WritablePacket, Reusable { +public interface ReusableWritablePacket extends WritableNetworkPacket, Reusable { /** * Handle completion of packet sending. diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/WritableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/WritableNetworkPacket.java new file mode 100644 index 00000000..91b997b6 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/WritableNetworkPacket.java @@ -0,0 +1,26 @@ +package javasabr.rlib.network.packet; + +import java.nio.ByteBuffer; + + +/** + * Interface to implement a writable packet. + * + * @author JavaSaBr + */ +public interface WritableNetworkPacket extends NetworkPacket { + + /** + * Write this packet to the buffer. + * + * @return true if writing was successful. + */ + boolean write(ByteBuffer buffer); + + /** + * @return expected data length of this packet or -1. + */ + default int expectedLength() { + return -1; + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/WritablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/WritablePacket.java deleted file mode 100644 index cf392115..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/WritablePacket.java +++ /dev/null @@ -1,135 +0,0 @@ -package javasabr.rlib.network.packet; - -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import javasabr.rlib.logger.api.LoggerManager; - -/** - * Interface to implement a writable packet. - * - * @author JavaSaBr - */ -public interface WritablePacket extends Packet { - - /** - * Write this packet to the buffer. - * - * @param buffer the buffer. - * @return true if writing was successful. - */ - boolean write(ByteBuffer buffer); - - /** - * Return an expected data length of this packet or -1. - * - * @return expected data length of this packet or -1. - */ - default int getExpectedLength() { - return -1; - } - - /** - * Write 1 byte to the buffer. - * - * @param buffer the buffer. - * @param value the value. - */ - default void writeByte(ByteBuffer buffer, int value) { - buffer.put((byte) value); - } - - /** - * Write 2 bytes to the buffer. - * - * @param buffer the buffer. - * @param value the value. - */ - default void writeChar(ByteBuffer buffer, char value) { - buffer.putChar(value); - } - - /** - * Write 2 bytes to the buffer. - * - * @param buffer the buffer. - * @param value the value. - */ - default void writeChar(final ByteBuffer buffer, final int value) { - buffer.putChar((char) value); - } - - /** - * Write 4 bytes to the buffer. - * - * @param buffer the buffer. - * @param value the value. - */ - default void writeFloat(ByteBuffer buffer, float value) { - buffer.putFloat(value); - } - - /** - * Write 4 bytes to the buffer. - * - * @param buffer the buffer. - * @param value the value. - */ - default void writeInt(ByteBuffer buffer, int value) { - buffer.putInt(value); - } - - /** - * Write 8 bytes to the buffer. - * - * @param buffer the buffer. - * @param value the value. - */ - default void writeLong(ByteBuffer buffer, long value) { - buffer.putLong(value); - } - - /** - * Writes 2 bytes to the buffer. - * - * @param buffer the buffer. - * @param value the value for writing. - */ - default void writeShort(ByteBuffer buffer, int value) { - buffer.putShort((short) value); - } - - /** - * Writes the string to the buffer. - * - * @param buffer the buffer. - * @param string the string for writing. - */ - default void writeString(ByteBuffer buffer, String string) { - try { - - writeInt(buffer, string.length()); - - for (int i = 0, length = string.length(); i < length; i++) { - buffer.putChar(string.charAt(i)); - } - - } catch (BufferOverflowException ex) { - LoggerManager - .getLogger(WritablePacket.class) - .error( - "Cannot write a string to buffer because the string is too long." + " String length: " + string.length() - + ", buffer: " + buffer); - throw ex; - } - } - - /** - * Write a data buffer to packet buffer. - * - * @param buffer thr packet buffer. - * @param data the data buffer. - */ - default void writeBuffer(ByteBuffer buffer, ByteBuffer data) { - buffer.put(data.array(), data.position(), data.limit()); - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractIdBasedReadableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractIdBasedReadableNetworkPacket.java new file mode 100644 index 00000000..0c57eeef --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractIdBasedReadableNetworkPacket.java @@ -0,0 +1,10 @@ +package javasabr.rlib.network.packet.impl; + +import javasabr.rlib.network.packet.IdBasedReadableNetworkPacket; + +/** + * @author JavaSaBr + */ +public abstract class AbstractIdBasedReadableNetworkPacket> + extends AbstractReadableNetworkPacket implements IdBasedReadableNetworkPacket { +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractIdBasedReadablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractIdBasedReadablePacket.java deleted file mode 100644 index c3202d4e..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractIdBasedReadablePacket.java +++ /dev/null @@ -1,29 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.IdBasedReadablePacket; - -/** - * @author JavaSaBr - */ -public abstract class AbstractIdBasedReadablePacket, - S extends AbstractIdBasedReadablePacket> extends - AbstractReadablePacket implements IdBasedReadablePacket { - - private static final Logger LOGGER = LoggerManager.getLogger(AbstractIdBasedReadablePacket.class); - - @Override - public void execute(Connection connection) { - try { - executeImpl(ClassUtils.unsafeNNCast(connection)); - } catch (Exception e) { - LOGGER.error(e); - } - } - - protected void executeImpl(C connection) { - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacket.java new file mode 100644 index 00000000..d42f0d3c --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacket.java @@ -0,0 +1,48 @@ +package javasabr.rlib.network.packet.impl; + +import static javasabr.rlib.network.util.NetworkUtils.hexDump; + +import java.nio.ByteBuffer; +import javasabr.rlib.network.packet.NetworkPacket; +import lombok.CustomLog; + +/** + * The base implementation of {@link NetworkPacket}. + * + * @author JavaSaBr + */ +@CustomLog +public abstract class AbstractNetworkPacket implements NetworkPacket { + + /** + * Handles packet data exception. + */ + protected void handleException(ByteBuffer buffer, Exception exception) { + log.warning(exception); + if (!log.warningEnabled()) { + return; + } + + String hexDump; + + if (buffer.isDirect()) { + var array = new byte[buffer.remaining()]; + buffer.get(array, buffer.position(), buffer.limit()); + hexDump = hexDump(array, array.length); + } else { + hexDump = hexDump(buffer.array(), buffer.position(), buffer.limit()); + } + + log.warning(name(), buffer, hexDump, "[%s] -> buffer:[%s]\n[%s]"::formatted); + } + + @Override + public String name() { + return getClass().getSimpleName(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + "name='" + name() + '\'' + '}'; + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketReader.java new file mode 100644 index 00000000..567822a8 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketReader.java @@ -0,0 +1,444 @@ +package javasabr.rlib.network.packet.impl; + +import static javasabr.rlib.common.util.ObjectUtils.notNull; + +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import javasabr.rlib.common.util.BufferUtils; +import javasabr.rlib.network.BufferAllocator; +import javasabr.rlib.network.Connection; +import javasabr.rlib.network.packet.NetworkPacketReader; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +/** + * @author JavaSaBr + */ +@CustomLog +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractNetworkPacketReader> + implements NetworkPacketReader { + + final CompletionHandler readChannelHandler = new CompletionHandler<>() { + + @Override + public void completed(Integer receivedBytes, ByteBuffer readingBuffer) { + try { + handleReceivedData(receivedBytes, readingBuffer); + } catch (RuntimeException ex) { + handleFailedReceiving(ex, readingBuffer); + } + } + + @Override + public void failed(Throwable exc, ByteBuffer readingBuffer) { + handleFailedReceiving(exc, readingBuffer); + } + }; + + final AtomicBoolean reading = new AtomicBoolean(false); + + final C connection; + final AsynchronousSocketChannel socketChannel; + final BufferAllocator bufferAllocator; + + final ByteBuffer readBuffer; + final ByteBuffer pendingBuffer; + + final Runnable updateActivityFunction; + final Consumer packetHandler; + + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + @Nullable + volatile ByteBuffer tempBigBuffer; + + final int maxPacketsByRead; + + protected AbstractNetworkPacketReader( + C connection, + AsynchronousSocketChannel socketChannel, + BufferAllocator bufferAllocator, + Runnable updateActivityFunction, + Consumer packetHandler, + int maxPacketsByRead) { + this.connection = connection; + this.socketChannel = socketChannel; + this.bufferAllocator = bufferAllocator; + this.readBuffer = bufferAllocator.takeReadBuffer(); + this.pendingBuffer = bufferAllocator.takePendingBuffer(); + this.updateActivityFunction = updateActivityFunction; + this.packetHandler = packetHandler; + this.maxPacketsByRead = maxPacketsByRead; + } + + protected ByteBuffer bufferToReadFromChannel() { + return readBuffer; + } + + protected String remoteAddress() { + return connection.remoteAddress(); + } + + @Override + public void startRead() { + if (!reading.compareAndSet(false, true)) { + return; + } + log.debug(remoteAddress(), "[%s] Start waiting for new data from channel..."::formatted); + ByteBuffer buffer = bufferToReadFromChannel(); + socketChannel.read(buffer, buffer, readChannelHandler); + } + + /** + * Reads packets from the buffer with received data. + * + * @param receivedBuffer the buffer with received data. + * @return count of read packets. + */ + protected int readPackets(ByteBuffer receivedBuffer) { + return readPackets(receivedBuffer, pendingBuffer); + } + + /** + * Reads packets from the buffer with received data. + * + * @param receivedBuffer the buffer with received data. + * @param pendingBuffer the buffer with pending data from prev. received buffer. + * @return count of read packets. + */ + protected int readPackets(ByteBuffer receivedBuffer, ByteBuffer pendingBuffer) { + String remoteAddress = remoteAddress(); + + log.debug(remoteAddress, receivedBuffer, + "[%s] Start reading packets from received buffer:[%s]"::formatted); + + int waitedBytes = pendingBuffer.position(); + ByteBuffer bufferToRead = receivedBuffer; + ByteBuffer tempBigBuffer = tempBigBuffer(); + + // if we have a temp big buffer it means that we are reading a big packet now + if (tempBigBuffer != null) { + // check and extends temp buffer if need + if (tempBigBuffer.remaining() < receivedBuffer.remaining()) { + reAllocTempBigBuffers(tempBigBuffer.flip(), tempBigBuffer.capacity()); + tempBigBuffer = notNull(tempBigBuffer()); + } + + log.debug( + remoteAddress, receivedBuffer, tempBigBuffer, + "[%s] Put received buffer:[%s] to temp big buffer:[%s]"::formatted); + + bufferToRead = BufferUtils.putToAndFlip(tempBigBuffer, receivedBuffer); + } + // if we have some pending data we need to append the received buffer to the pending buffer + // and start to read pending buffer with result received data + else if (waitedBytes > 0) { + if (pendingBuffer.remaining() < receivedBuffer.remaining()) { + log.debug( + remoteAddress, + pendingBuffer, + receivedBuffer, + "[%s] Pending buffer:[%s] is too small to append received buffer:[%s], allocate new temp bug buffer for this"::formatted); + + allocTempBigBuffers(pendingBuffer.flip(), pendingBuffer.capacity()); + + log.debug(remoteAddress, pendingBuffer, "[%s] Clear pending buffer:[%s]"::formatted); + pendingBuffer.clear(); + + tempBigBuffer = notNull(tempBigBuffer()); + + log.debug( + remoteAddress, receivedBuffer, tempBigBuffer, + "[%s] Put received buffer:[%s] to temp big buffer:[%s]"::formatted); + bufferToRead = BufferUtils.putToAndFlip(tempBigBuffer, receivedBuffer); + } else { + log.debug( + remoteAddress, receivedBuffer, pendingBuffer, + "[%s] Put received buffer:[%s] to pending buffer:[%s]"::formatted); + bufferToRead = BufferUtils.putToAndFlip(pendingBuffer, receivedBuffer); + } + } + + int maxPacketsByRead = maxPacketsByRead(); + int readPackets = 0; + int endPosition = 0; + + while (canStartReadPacket(bufferToRead) && readPackets < maxPacketsByRead) { + // set position to start reading a next packet + bufferToRead.position(endPosition); + + int positionBeforeRead = endPosition; + int packetFullLength = readFullPacketLength(bufferToRead); + int alreadyReadBytes = bufferToRead.position() - endPosition; + int packetDataLength = calculatePacketDataLength(packetFullLength, alreadyReadBytes, bufferToRead); + + log.debug( + remoteAddress, positionBeforeRead, packetFullLength, + "[%s] Found next packet from position:[%s] with length:[%s] "::formatted); + + // calculate position of the end of next packet + endPosition += packetFullLength; + + // if the packet isn't full presented in this buffer + if (packetFullLength == -1 || endPosition > bufferToRead.limit()) { + bufferToRead.position(positionBeforeRead); + + // if we read the received buffer we need to put not yet read data to the pending + // buffer or temp big buffer + if (bufferToRead == receivedBuffer) { + if (packetFullLength <= pendingBuffer.capacity()) { + pendingBuffer.put(receivedBuffer); + log.debug( + remoteAddress, pendingBuffer, + "[%s] Put pending data form received buffer to pending buffer:[%s]"::formatted); + } else { + allocTempBigBuffers(receivedBuffer, packetFullLength); + } + } + // if we already read this pending buffer we need to compact it + else if (bufferToRead == pendingBuffer) { + if (packetFullLength <= pendingBuffer.capacity()) { + pendingBuffer.compact(); + log.debug(remoteAddress, pendingBuffer, "[%s] Compact pending buffer:[%s]"::formatted); + } else { + allocTempBigBuffers(pendingBuffer, packetFullLength); + log.debug(remoteAddress, pendingBuffer, "[%s] Clear pending buffer:[%s]"::formatted); + pendingBuffer.clear(); + } + + } else if (bufferToRead == tempBigBuffer) { + // if not yet read data is less than pending buffer, then we can switch to use the pending buffer + if (Math.max(packetFullLength, tempBigBuffer.remaining()) <= pendingBuffer.capacity()) { + pendingBuffer.clear().put(tempBigBuffer); + log.debug( + remoteAddress, pendingBuffer, + "[%s] Moved pending data from temp big buffer to pending buffer:[%s]"::formatted); + freeTempBigBuffers(); + } + // if a new packet is bigger than current temp big buffer + else if (packetFullLength > tempBigBuffer.capacity()) { + reAllocTempBigBuffers(tempBigBuffer, packetFullLength); + } + // or just compact this current temp big buffer + else { + tempBigBuffer.compact(); + log.debug(remoteAddress, tempBigBuffer, "[%s] Compact temp big buffer:[%s]]"::formatted); + } + } + + log.debug( + remoteAddress, + readPackets, + connection.remoteAddress(), + ("[%s] Read [%s] packets from received buffer of [%s], " + + "but 1 packet is still waiting for receiving additional data.")::formatted); + + receivedBuffer.clear(); + return readPackets; + } + + R readablePacket = createPacketFor( + bufferToRead, + positionBeforeRead, + packetFullLength, + packetDataLength); + + if (readablePacket != null) { + int remainingDataLength = endPosition - bufferToRead.position(); + log.debug(remoteAddress, readablePacket, "[%s] Created instance of packet to read data:[%s]"::formatted); + readAndHandlePacket(bufferToRead, remainingDataLength, readablePacket); + log.debug(remoteAddress, readablePacket, "[%s] Finished reading data for packet:[%s]"::formatted); + readPackets++; + } else { + log.warning(remoteAddress, "[%s] Cannot create any instance of packet to read data"::formatted); + } + } + + bufferToRead.position(endPosition); + + if (bufferToRead.hasRemaining()) { + if (bufferToRead == receivedBuffer) { + pendingBuffer.put(receivedBuffer); + log.debug(remoteAddress, "[%s] Found not yet read data from receive buffer, will put it to pending buffer"::formatted); + } else { + bufferToRead.compact(); + } + } else if (bufferToRead == pendingBuffer) { + pendingBuffer.clear(); + } else if (tempBigBuffer != null) { + freeTempBigBuffers(); + } + + log.debug(remoteAddress, readPackets, "[%s] Read [%s] packets from received buffer"::formatted); + receivedBuffer.clear(); + return readPackets; + } + + protected void readAndHandlePacket(ByteBuffer bufferToRead, int remainingDataLength, R packetInstance) { + if (packetInstance.read(bufferToRead, remainingDataLength)) { + packetHandler.accept(packetInstance); + } else { + log.error(remoteAddress(), packetInstance, + "[%s] Packet:[%s] was read incorrectly"::formatted); + } + } + + /** + * Checks the buffer's data. + * + * @return true if this buffer has enough data to start initial reading. + */ + protected abstract boolean canStartReadPacket(ByteBuffer buffer); + + /** + * Calculates size of packet data. + * + * @param packetLength the full packet length. + * @param alreadyReadBytes the count of already read bytes from buffer to get packet length. + * @param buffer the data buffer. + * @return the length of packet data part. + */ + protected int calculatePacketDataLength(int packetLength, int alreadyReadBytes, ByteBuffer buffer) { + return packetLength - alreadyReadBytes; + } + + /** + * Gets the packet's full length of next packet in the buffer. + * + * @param buffer the buffer with received data. + * @return the packet length or -1 if we have no enough data to read length. + */ + protected abstract int readFullPacketLength(ByteBuffer buffer); + + protected void reAllocTempBigBuffers(ByteBuffer sourceBuffer, int fullPacketLength) { + log.debug(remoteAddress(), sourceBuffer.capacity(), fullPacketLength, + "[%s] Resize temp big buffer from:[%s] to:[%s]"::formatted); + + var newTempBuffer = bufferAllocator.takeBuffer(fullPacketLength + readBuffer.capacity()); + log.debug(remoteAddress(), sourceBuffer, newTempBuffer, + "[%s] Moved data from old temp big buffer:[%s] to new:[%s]"::formatted); + newTempBuffer.put(sourceBuffer); + + freeTempBigBuffers(); + this.tempBigBuffer = newTempBuffer; + } + + protected void allocTempBigBuffers(ByteBuffer sourceBuffer, int fullPacketLength) { + int notConsumeBytes = sourceBuffer.remaining(); + log.debug( + notConsumeBytes, + fullPacketLength, + "Request temp big buffer to store part:[%s] of big packet with length:[%s]"::formatted); + + var tempBigBuffer = bufferAllocator.takeBuffer(fullPacketLength + readBuffer.capacity()); + log.debug(sourceBuffer, tempBigBuffer, "Put data from old temp big buffer:[%s] to new:[%s]"::formatted); + tempBigBuffer.put(sourceBuffer); + + this.tempBigBuffer = tempBigBuffer; + } + + protected void freeTempBigBuffers() { + ByteBuffer tempBuffer = tempBigBuffer(); + if (tempBuffer != null) { + tempBigBuffer(null); + bufferAllocator.putBuffer(tempBuffer); + } + } + + /** + * Handles received data from the channel. + */ + protected void handleReceivedData(int receivedBytes, ByteBuffer readingBuffer) { + updateActivityFunction.run(); + + if (receivedBytes == -1) { + connection.close(); + return; + } + + log.debug(remoteAddress(), receivedBytes, "[%s] Received [%s] bytes from channel"::formatted); + readingBuffer.flip(); + try { + readPackets(readingBuffer); + } catch (Exception e) { + log.error(e); + } + + if (reading.compareAndSet(true, false)) { + startRead(); + } + } + + /** + * Handles the exception during receiving data from the channel. + * + * @param exception the exception. + * @param readingBuffer the currently reading buffer. + */ + protected void handleFailedReceiving(Throwable exception, ByteBuffer readingBuffer) { + if (exception instanceof AsynchronousCloseException) { + log.info(remoteAddress(), "[%s] Connection was closed"::formatted); + } else { + log.error(exception); + connection.close(); + } + } + + /** + * Gets how many packets can be read by the one method call {@link #readPackets(ByteBuffer, ByteBuffer)}}. + */ + protected int maxPacketsByRead() { + return maxPacketsByRead; + } + + protected int readHeader(ByteBuffer buffer, int headerSize) { + switch (headerSize) { + case 1: + return buffer.get() & 0xFF; + case 2: + return buffer.getShort() & 0xFFFF; + case 4: + return buffer.getInt(); + default: + throw new IllegalStateException("Wrong packet's header size: " + headerSize); + } + } + + /** + * Creates a packet to read the received data. + * + * @param buffer the buffer with received data. + * @param startPacketPosition the start position of the packet in the buffer. + * @param packetFullLength the length of packet. + * @param packetDataLength length of packet's data. + * @return the readable packet. + */ + @Nullable + protected abstract R createPacketFor( + ByteBuffer buffer, + int startPacketPosition, + int packetFullLength, + int packetDataLength); + + @Override + public void close() { + + bufferAllocator + .putReadBuffer(readBuffer) + .putPendingBuffer(pendingBuffer); + + freeTempBigBuffers(); + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketWriter.java new file mode 100644 index 00000000..d2660ea7 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractNetworkPacketWriter.java @@ -0,0 +1,417 @@ +package javasabr.rlib.network.packet.impl; + +import static javasabr.rlib.network.util.NetworkUtils.EMPTY_BUFFER; +import static javasabr.rlib.network.util.NetworkUtils.hexDump; + +import java.io.IOException; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javasabr.rlib.functions.ObjBoolConsumer; +import javasabr.rlib.network.BufferAllocator; +import javasabr.rlib.network.Connection; +import javasabr.rlib.network.packet.NetworkPacketWriter; +import javasabr.rlib.network.packet.WritableNetworkPacket; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +/** + * @author JavaSaBr + */ +@CustomLog +@RequiredArgsConstructor +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractNetworkPacketWriter> + implements NetworkPacketWriter { + + final CompletionHandler writeHandler = new CompletionHandler<>() { + + @Override + public void completed(Integer result, @Nullable WritableNetworkPacket packet) { + handleSuccessfulWritingData(result, packet); + } + + @Override + public void failed(Throwable exc, @Nullable WritableNetworkPacket packet) { + handleFailedWritingData(exc, packet); + } + }; + + final AtomicBoolean writing = new AtomicBoolean(); + + final C connection; + final AsynchronousSocketChannel socketChannel; + final BufferAllocator bufferAllocator; + final ByteBuffer firstWriteBuffer; + final ByteBuffer secondWriteBuffer; + + @Nullable + volatile ByteBuffer firstWriteTempBuffer; + @Nullable + volatile ByteBuffer secondWriteTempBuffer; + + @Getter(AccessLevel.PROTECTED) + volatile ByteBuffer writingBuffer = EMPTY_BUFFER; + + final Runnable updateActivityFunction; + final Supplier<@Nullable WritableNetworkPacket> writablePacketProvider; + final ObjBoolConsumer sentPacketHandler; + final Consumer serializedToChannelPacketHandler; + + public AbstractNetworkPacketWriter( + C connection, + AsynchronousSocketChannel socketChannel, + BufferAllocator bufferAllocator, + Runnable updateActivityFunction, + Supplier<@Nullable WritableNetworkPacket> packetProvider, + Consumer serializedToChannelPacketHandler, + ObjBoolConsumer sentPacketHandler) { + this.connection = connection; + this.socketChannel = socketChannel; + this.bufferAllocator = bufferAllocator; + this.firstWriteBuffer = bufferAllocator.takeWriteBuffer(); + this.secondWriteBuffer = bufferAllocator.takeWriteBuffer(); + this.updateActivityFunction = updateActivityFunction; + this.writablePacketProvider = packetProvider; + this.serializedToChannelPacketHandler = serializedToChannelPacketHandler; + this.sentPacketHandler = sentPacketHandler; + } + + protected String remoteAddress() { + return connection.remoteAddress(); + } + + @Override + public boolean tryToSendNextPacket() { + if (connection.closed() || !writing.compareAndSet(false, true)) { + return false; + } + + boolean startedWriting = false; + + try { + startedWriting = tryToSendNextPacketImpl(); + } catch (Exception ex) { + log.error(ex); + } + + if (!startedWriting) { + writing.set(false); + } + + return startedWriting; + } + + protected boolean tryToSendNextPacketImpl() { + for (var nextPacket = writablePacketProvider.get(); + nextPacket != null; nextPacket = writablePacketProvider.get()) { + ByteBuffer resultBuffer = serialize(nextPacket); + boolean startedWriting = writeBuffer(resultBuffer, nextPacket); + serializedToChannelPacketHandler.accept(nextPacket); + if (startedWriting) { + return true; + } + } + return false; + } + + protected boolean writeBuffer(ByteBuffer resultBuffer, @Nullable WritableNetworkPacket nextPacket) { + if (resultBuffer.limit() == 0) { + return false; + } + writingBuffer = resultBuffer; + log.debug(remoteAddress(), resultBuffer, (address, buff) -> "[%s] Write to channel data:\n" + hexDump(buff)); + socketChannel.write(resultBuffer, nextPacket, writeHandler); + return true; + } + + /** + * Serializes the network packet to buffer. + * + * @param packet the network packet. + * @return the final byte buffer with data. + */ + protected ByteBuffer serialize(WritableNetworkPacket packet) { + + if (packet instanceof WritablePacketWrapper) { + packet = ((WritablePacketWrapper) packet).getPacket(); + } + + W resultPacket = (W) packet; + + int expectedLength = packet.expectedLength(); + int totalSize = expectedLength == -1 ? -1 : totalSize(packet, expectedLength); + + // if the packet is too big to use a write buffer + if (expectedLength != -1 && totalSize > firstWriteBuffer.capacity()) { + ByteBuffer first = bufferAllocator.takeBuffer(totalSize); + ByteBuffer second = bufferAllocator.takeBuffer(totalSize); + firstWriteTempBuffer = first; + secondWriteTempBuffer = second; + try { + return serialize(resultPacket, expectedLength, totalSize, first, second); + } catch (BufferOverflowException ex) { + log.error(ex); + bufferAllocator.putBuffer(first); + bufferAllocator.putBuffer(second); + firstWriteTempBuffer = null; + secondWriteTempBuffer = null; + throw new RuntimeException(ex); + } + } else { + try { + return serialize(resultPacket, expectedLength, totalSize, firstWriteBuffer, secondWriteBuffer); + } catch (BufferOverflowException ex) { + log.error(ex); + firstWriteBuffer.clear(); + secondWriteBuffer.clear(); + throw new RuntimeException(ex); + } + } + } + + /** + * Gets total size of the packet if it's possible. + * + * @return the total size or -1. + */ + protected abstract int totalSize(WritableNetworkPacket packet, int expectedLength); + + /** + * Serializes the packet to the buffers. + * + * @param packet the network packet to serialize. + * @param expectedLength the packet's expected size. + * @param totalSize the packet's total size. + * @param firstBuffer the first buffer. + * @param secondBuffer the second buffer. + * @return the final buffer to write to channel. + */ + protected ByteBuffer serialize( + W packet, + int expectedLength, + int totalSize, + ByteBuffer firstBuffer, + ByteBuffer secondBuffer) { + + if (!onBeforeSerialize(packet, expectedLength, totalSize, firstBuffer, secondBuffer)) { + return firstBuffer.clear().limit(0); + } else if (!doSerialize(packet, expectedLength, totalSize, firstBuffer, secondBuffer)) { + return firstBuffer.clear().limit(0); + } else if (!onAfterSerialize(packet, expectedLength, totalSize, firstBuffer, secondBuffer)) { + return firstBuffer.clear().limit(0); + } + + return onSerializeResult(packet, expectedLength, totalSize, firstBuffer, secondBuffer); + } + + /** + * Handles the buffers before serializing packet's data. + * + * @param packet the network packet. + * @param expectedLength the packet's expected size. + * @param totalSize the packet's total size. + * @param firstBuffer the first buffer. + * @param secondBuffer the second buffer. + * @return true if handling was successful. + */ + protected boolean onBeforeSerialize( + W packet, + int expectedLength, + int totalSize, + ByteBuffer firstBuffer, + ByteBuffer secondBuffer) { + firstBuffer.clear(); + return true; + } + + /** + * Serializes the network packet data to the buffers. + * + * @param packet the network packet. + * @param expectedLength the packet's expected size. + * @param totalSize the packet's total size. + * @param firstBuffer the first buffer. + * @param secondBuffer the second buffer. + * @return true if writing was successful. + */ + protected boolean doSerialize( + W packet, + int expectedLength, + int totalSize, + ByteBuffer firstBuffer, + ByteBuffer secondBuffer) { + return packet.write(firstBuffer); + } + + /** + * Handles the buffers after serializing packet's data. + * + * @param packet the network packet. + * @param expectedLength the packet's expected size. + * @param totalSize the packet's total size. + * @param firstBuffer the first buffer. + * @param secondBuffer the second buffer. + * @return true if handling was successful. + */ + protected boolean onAfterSerialize( + W packet, + int expectedLength, + int totalSize, + ByteBuffer firstBuffer, + ByteBuffer secondBuffer) { + firstBuffer.flip(); + return true; + } + + /** + * Handles the final result of serializing packet data and return the buffer which contains the data to send. + * + * @param packet the network packet. + * @param expectedLength the packet's expected size. + * @param totalSize the packet's total size. + * @param firstBuffer the first buffer. + * @param secondBuffer the second buffer. + * @return the result buffer. + */ + protected ByteBuffer onSerializeResult( + W packet, + int expectedLength, + int totalSize, + ByteBuffer firstBuffer, + ByteBuffer secondBuffer) { + return firstBuffer.position(0); + } + + protected ByteBuffer writeHeader(ByteBuffer buffer, int position, int value, int headerSize) { + try { + switch (headerSize) { + case 1: + buffer.put(position, (byte) value); + break; + case 2: + buffer.putShort(position, (short) value); + break; + case 4: + buffer.putInt(position, value); + break; + default: + throw new IllegalStateException("Wrong packet's header size: " + headerSize); + } + return buffer; + } catch (IndexOutOfBoundsException ex) { + log.error(remoteAddress(), position, headerSize, buffer, + "[%s] Cannot write header by position:[%s] with header size:[%s] to buffer:[%s]"::formatted); + throw ex; + } + } + + protected ByteBuffer writeHeader(ByteBuffer buffer, int value, int headerSize) { + switch (headerSize) { + case 1: + buffer.put((byte) value); + break; + case 2: + buffer.putShort((short) value); + break; + case 4: + buffer.putInt(value); + break; + default: + throw new IllegalStateException("Wrong packet's header size: " + headerSize); + } + return buffer; + } + + /** + * Handles successful writing part of network packet data. + * + * @param wroteBytes the count of wrote bytes. + * @param packet the sent packet. + */ + protected void handleSuccessfulWritingData(Integer wroteBytes, @Nullable WritableNetworkPacket packet) { + updateActivityFunction.run(); + + if (wroteBytes == -1) { + if (packet != null) { + sentPacketHandler.accept(packet, false); + } + connection.close(); + return; + } + + ByteBuffer writingBuffer = writingBuffer(); + if (writingBuffer.remaining() > 0) { + log.debug(remoteAddress(), writingBuffer, + "[%s] Buffer was not consumed fully, try to write else [%s] bytes to channel"::formatted); + socketChannel.write(writingBuffer, packet, writeHandler); + return; + } else { + log.debug(remoteAddress(), wroteBytes, "[%s] Finished writing [%s] bytes"::formatted); + } + + if (packet != null) { + sentPacketHandler.accept(packet, true); + } + + if (writing.compareAndSet(true, false)) { + clearTempBuffers(); + tryToSendNextPacket(); + } + } + + /** + * Handles the exception during writing the packet. + * + * @param exception the exception. + * @param packet the packet. + */ + protected void handleFailedWritingData(Throwable exception, @Nullable WritableNetworkPacket packet) { + log.error(new RuntimeException("Failed writing packet:" + packet, exception)); + if (exception instanceof IOException) { + connection.close(); + return; + } + if (!connection.closed()) { + if (writing.compareAndSet(true, false)) { + clearTempBuffers(); + tryToSendNextPacket(); + } + } + } + + @Override + public void close() { + bufferAllocator + .putWriteBuffer(firstWriteBuffer) + .putWriteBuffer(secondWriteBuffer); + clearTempBuffers(); + writingBuffer = EMPTY_BUFFER; + } + + protected void clearTempBuffers() { + this.writingBuffer = EMPTY_BUFFER; + + var firstWriteTempBuffer = this.firstWriteTempBuffer; + if (firstWriteTempBuffer != null) { + this.firstWriteTempBuffer = null; + bufferAllocator.putBuffer(firstWriteTempBuffer); + } + + var secondWriteTempBuffer = this.secondWriteTempBuffer; + if (secondWriteTempBuffer != null) { + this.secondWriteTempBuffer = null; + bufferAllocator.putBuffer(secondWriteTempBuffer); + } + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacket.java deleted file mode 100644 index 739fbb09..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacket.java +++ /dev/null @@ -1,46 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import static javasabr.rlib.network.util.NetworkUtils.hexDump; - -import java.nio.ByteBuffer; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.packet.Packet; - -/** - * The base implementation of {@link Packet}. - * - * @author JavaSaBr - */ -public abstract class AbstractPacket implements Packet { - - protected static final Logger LOGGER = LoggerManager.getLogger(Packet.class); - - /** - * Handle the exception. - * - * @param buffer the data buffer. - * @param exception the exception. - */ - protected void handleException(ByteBuffer buffer, Exception exception) { - LOGGER.warning(exception); - - if (buffer.isDirect()) { - var array = new byte[buffer.limit()]; - buffer.get(array, 0, buffer.limit()); - LOGGER.warning(getName() + " -> buffer: " + buffer + "\n" + hexDump(array, array.length)); - } else { - LOGGER.warning(getName() + " -> buffer: " + buffer + "\n" + hexDump(buffer.array(), buffer.limit())); - } - } - - @Override - public String getName() { - return getClass().getSimpleName(); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{" + "name='" + getName() + '\'' + '}'; - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacketReader.java deleted file mode 100644 index 502545b8..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacketReader.java +++ /dev/null @@ -1,478 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import static javasabr.rlib.common.util.ObjectUtils.notNull; -import static javasabr.rlib.network.util.NetworkUtils.getRemoteAddress; - -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousCloseException; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.CompletionHandler; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; -import javasabr.rlib.common.util.BufferUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.BufferAllocator; -import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.PacketReader; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.util.NetworkUtils; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import org.jspecify.annotations.Nullable; - -/** - * @param the readable packet's type. - * @param the connection's type. - * @author JavaSaBr - */ -public abstract class AbstractPacketReader> implements - PacketReader { - - private static final Logger LOGGER = LoggerManager.getLogger(AbstractPacketReader.class); - - private final CompletionHandler readHandler = new CompletionHandler<>() { - - @Override - public void completed(Integer receivedBytes, ByteBuffer readingBuffer) { - handleReceivedData(receivedBytes, readingBuffer); - } - - @Override - public void failed(Throwable exc, ByteBuffer readingBuffer) { - handleFailedReceiving(exc, readingBuffer); - } - }; - - protected final AtomicBoolean isReading = new AtomicBoolean(false); - - protected final C connection; - protected final AsynchronousSocketChannel channel; - protected final BufferAllocator bufferAllocator; - - protected final ByteBuffer readBuffer; - protected final ByteBuffer pendingBuffer; - - protected final Runnable updateActivityFunction; - protected final Consumer readPacketHandler; - - @Getter(AccessLevel.PROTECTED) - @Setter(AccessLevel.PROTECTED) - protected volatile @Nullable ByteBuffer tempPendingBuffer; - - protected final int maxPacketsByRead; - - protected AbstractPacketReader( - C connection, - AsynchronousSocketChannel channel, - BufferAllocator bufferAllocator, - Runnable updateActivityFunction, - Consumer readPacketHandler, - int maxPacketsByRead) { - this.connection = connection; - this.channel = channel; - this.bufferAllocator = bufferAllocator; - this.readBuffer = bufferAllocator.takeReadBuffer(); - this.pendingBuffer = bufferAllocator.takePendingBuffer(); - this.updateActivityFunction = updateActivityFunction; - this.readPacketHandler = readPacketHandler; - this.maxPacketsByRead = maxPacketsByRead; - } - - protected ByteBuffer getBufferToReadFromChannel() { - return readBuffer; - } - - @Override - public void startRead() { - - if (!isReading.compareAndSet(false, true)) { - return; - } - - LOGGER.debug(channel, ch -> "Start waiting for new data from channel \"" + getRemoteAddress(ch) + "\""); - - var buffer = getBufferToReadFromChannel(); - - channel.read(buffer, buffer, readHandler); - } - - /** - * Read packets from the buffer with received data. - * - * @param receivedBuffer the buffer with received data. - * @return count of read packets. - */ - protected int readPackets(ByteBuffer receivedBuffer) { - return readPackets(receivedBuffer, pendingBuffer); - } - - /** - * Read packets from the buffer with received data. - * - * @param receivedBuffer the buffer with received data. - * @param pendingBuffer the buffer with pending data from prev. received buffer. - * @return count of read packets. - */ - protected int readPackets(ByteBuffer receivedBuffer, ByteBuffer pendingBuffer) { - - LOGGER.debug(receivedBuffer, buf -> "Start reading packets from received buffer " + buf); - - var waitedBytes = pendingBuffer.position(); - var bufferToRead = receivedBuffer; - var tempPendingBuffer = getTempPendingBuffer(); - - // if we have a temp buffer it means that we are reading a really big packet now - if (tempPendingBuffer != null) { - - if (tempPendingBuffer.remaining() < receivedBuffer.remaining()) { - reAllocTempBuffers(tempPendingBuffer.flip(), tempPendingBuffer.capacity()); - tempPendingBuffer = notNull(getTempPendingBuffer()); - } - - LOGGER.debug( - receivedBuffer, - tempPendingBuffer, - (buf, mappedBuf) -> "Put received buffer " + buf + " to read mapped buffer " + mappedBuf); - - bufferToRead = BufferUtils.putToAndFlip(tempPendingBuffer, receivedBuffer); - } - // if we have some pending data we need to append the received buffer to the pending buffer - // and start to read pending buffer with result received data - else if (waitedBytes > 0) { - - if (pendingBuffer.remaining() < receivedBuffer.remaining()) { - - LOGGER.debug( - pendingBuffer, - receivedBuffer, - (penBuf, buf) -> "Pending buffer " + penBuf + " is too small to append received buffer " + buf - + ", will allocate new temp buffer for this"); - - allocTempBuffers(pendingBuffer.flip(), pendingBuffer.capacity()); - - LOGGER.debug(pendingBuffer, buf -> "Clear pending buffer: " + buf); - - pendingBuffer.clear(); - - tempPendingBuffer = notNull(getTempPendingBuffer()); - - LOGGER.debug( - receivedBuffer, - tempPendingBuffer, - (buf, mappedBuf) -> "Put received buffer: " + buf + " to mapped buffer: " + mappedBuf); - - bufferToRead = BufferUtils.putToAndFlip(tempPendingBuffer, receivedBuffer); - - } else { - - LOGGER.debug( - receivedBuffer, - pendingBuffer, - (buf, penBuf) -> "Put received buffer: " + buf + " to pending buffer: " + penBuf); - - bufferToRead = BufferUtils.putToAndFlip(pendingBuffer, receivedBuffer); - } - } - - var maxPacketsByRead = getMaxPacketsByRead(); - - var readPackets = 0; - var endPosition = 0; - - while (canStartReadPacket(bufferToRead) && readPackets < maxPacketsByRead) { - - // set position of start a next packet - bufferToRead.position(endPosition); - - var positionBeforeRead = endPosition; - var packetLength = readPacketLength(bufferToRead); - var dataLength = getDataLength(packetLength, bufferToRead.position() - endPosition, bufferToRead); - - LOGGER.debug( - packetLength, - positionBeforeRead, - (length, pos) -> "Find next packet from position: " + pos + " with length: " + length); - - // calculate position of end the next packet - endPosition += packetLength; - - // if the packet isn't full presented in this buffer - if (packetLength == -1 || endPosition > bufferToRead.limit()) { - - bufferToRead.position(positionBeforeRead); - - // if we read the received buffer we need to put - // not read data to the pending buffer or big mapped byte buffer - if (bufferToRead == receivedBuffer) { - if (packetLength <= pendingBuffer.capacity()) { - pendingBuffer.put(receivedBuffer); - LOGGER.debug(pendingBuffer, buf -> "Put pending data form received buffer to pending buffer: " + buf); - } else { - allocTempBuffers(receivedBuffer, packetLength); - } - } - // if we already read this pending buffer we need to compact it - else if (bufferToRead == pendingBuffer) { - - if (packetLength <= pendingBuffer.capacity()) { - pendingBuffer.compact(); - LOGGER.debug(pendingBuffer, buf -> "Compact pending buffer: " + buf); - } else { - allocTempBuffers(pendingBuffer, packetLength); - LOGGER.debug(pendingBuffer, buf -> "Clear pending buffer: " + buf); - pendingBuffer.clear(); - } - - } else if (bufferToRead == tempPendingBuffer) { - - // if not read data is less than pending buffer then we can switch to use the pending buffer - if (Math.max(packetLength, tempPendingBuffer.remaining()) <= pendingBuffer.capacity()) { - - pendingBuffer - .clear() - .put(tempPendingBuffer); - - LOGGER.debug(pendingBuffer, buf -> "Moved pending data from mapped buffer to pending buffer: " + buf); - - freeTempBuffers(); - } - // if a new packet is bigger than current read mapped buffer - else if (packetLength > tempPendingBuffer.capacity()) { - reAllocTempBuffers(tempPendingBuffer, packetLength); - } - // or just compact this current mapped buffer - else { - tempPendingBuffer.compact(); - LOGGER.debug(tempPendingBuffer, buf -> "Compact mapped buffer: " + buf); - } - } - - LOGGER.debug( - channel, - readPackets, - (ch, count) -> "Read " + count + " packets from received buffer of " + getRemoteAddress(ch) + ", " - + "but 1 packet is still waiting for receiving additional data."); - - receivedBuffer.clear(); - return readPackets; - } - - R packet = createPacketFor(bufferToRead, positionBeforeRead, packetLength, dataLength); - - if (packet != null) { - LOGGER.debug(packet, pck -> "Created instance of packet to read data: " + pck); - readAndHandlePacket(bufferToRead, dataLength, packet); - LOGGER.debug(packet, pck -> "Finished reading data of packet: " + pck); - readPackets++; - } else { - LOGGER.warning("Cannot create any instance of packet to read data"); - } - - bufferToRead.position(endPosition); - } - - if (bufferToRead.hasRemaining()) { - - if (bufferToRead == receivedBuffer) { - pendingBuffer.put(receivedBuffer); - LOGGER.debug("Found not yet read data from receive buffer, will put it to pending buffer."); - } else { - bufferToRead.compact(); - } - - } else if (bufferToRead == pendingBuffer) { - pendingBuffer.clear(); - } else if (tempPendingBuffer != null) { - freeTempBuffers(); - } - - LOGGER.debug( - channel, - readPackets, - (ch, count) -> "Read " + count + " packets from received buffer of " + getRemoteAddress(ch) + "."); - - receivedBuffer.clear(); - return readPackets; - } - - protected void readAndHandlePacket(ByteBuffer bufferToRead, int dataLength, R packet) { - if (packet.read(connection, bufferToRead, dataLength)) { - readPacketHandler.accept(packet); - } else { - LOGGER.error("Packet " + packet + " was read incorrectly"); - } - } - - /** - * Check buffer's data. - * - * @param buffer the buffer to read. - * @return true if this buffer has enough data to start initial reading. - */ - protected abstract boolean canStartReadPacket(ByteBuffer buffer); - - /** - * Calculate size of packet data. - * - * @param packetLength the full packet length. - * @param readBytes the count of already read bytes from buffer to get packet length. - * @param buffer the buffer. - * @return the length of packet data part. - */ - protected int getDataLength(int packetLength, int readBytes, ByteBuffer buffer) { - return packetLength - readBytes; - } - - /** - * Get the packet's data length of next packet in the buffer. - * - * @param buffer the buffer with received data. - * @return the packet length or -1 if we have no enough data to read length. - */ - protected abstract int readPacketLength(ByteBuffer buffer); - - protected void reAllocTempBuffers(ByteBuffer sourceBuffer, int packetLength) { - - LOGGER.debug( - sourceBuffer.capacity(), - packetLength, - (currentSize, newSize) -> "Resize read temp buffer from " + currentSize + " to " + newSize); - - var newReadTempBuffer = bufferAllocator.takeBuffer(packetLength + readBuffer.capacity()); - - LOGGER.debug( - sourceBuffer, - newReadTempBuffer, - (old, buf) -> "Moved pending data from old temp buffer " + old + " to new temp buffer " + buf); - - newReadTempBuffer.put(sourceBuffer); - - freeTempBuffers(); - - this.tempPendingBuffer = newReadTempBuffer; - } - - protected void allocTempBuffers(ByteBuffer sourceBuffer, int packetLength) { - - LOGGER.debug( - packetLength, - sourceBuffer.remaining(), - (length, part) -> "Request temp buffer to store a part: " + part + " of big packet with length: " + length); - - var readTempBuffer = bufferAllocator.takeBuffer(packetLength + readBuffer.capacity()); - - LOGGER.debug( - sourceBuffer, - readTempBuffer, - (recBuf, buf) -> "Put the part of packet: " + recBuf + " to mapped buffer: " + buf); - - readTempBuffer.put(sourceBuffer); - - this.tempPendingBuffer = readTempBuffer; - } - - protected void freeTempBuffers() { - - var readTempBuffer = getTempPendingBuffer(); - - if (readTempBuffer != null) { - setTempPendingBuffer(null); - bufferAllocator.putBuffer(readTempBuffer); - } - } - - /** - * Handle received data. - * - * @param receivedBytes the count of received bytes. - * @param readingBuffer the currently reading buffer. - */ - protected void handleReceivedData(Integer receivedBytes, ByteBuffer readingBuffer) { - updateActivityFunction.run(); - - if (receivedBytes == -1) { - connection.close(); - return; - } - - LOGGER.debug( - receivedBytes, - channel, - (bytes, ch) -> "Received " + bytes + " bytes from channel \"" + NetworkUtils.getRemoteAddress(ch) + "\""); - - readingBuffer.flip(); - try { - readPackets(readingBuffer); - } catch (Exception e) { - LOGGER.error(e); - } - - if (isReading.compareAndSet(true, false)) { - startRead(); - } - } - - /** - * Handle the exception during receiving data. - * - * @param exception the exception. - * @param readingBuffer the currently reading buffer. - */ - protected void handleFailedReceiving(Throwable exception, ByteBuffer readingBuffer) { - if (exception instanceof AsynchronousCloseException) { - LOGGER.info(connection, cn -> "Connection " + cn.getRemoteAddress() + " was closed."); - } else { - LOGGER.error(exception); - connection.close(); - } - } - - /** - * Get the how many packets can be read by the one method call {@link #readPackets(ByteBuffer, ByteBuffer)}}. - * - * @return the how many packets can be read. - */ - protected int getMaxPacketsByRead() { - return maxPacketsByRead; - } - - protected int readHeader(ByteBuffer buffer, int headerSize) { - switch (headerSize) { - case 1: - return buffer.get() & 0xFF; - case 2: - return buffer.getShort() & 0xFFFF; - case 4: - return buffer.getInt(); - default: - throw new IllegalStateException("Wrong packet's header size: " + headerSize); - } - } - - /** - * Create a packet to read received data. - * - * @param buffer the buffer with received data. - * @param startPacketPosition the start position of the packet in the buffer. - * @param packetLength the length of packet. - * @param dataLength length of packet's data. - * @return the readable packet. - */ - protected abstract @Nullable R createPacketFor( - ByteBuffer buffer, - int startPacketPosition, - int packetLength, - int dataLength); - - @Override - public void close() { - - bufferAllocator - .putReadBuffer(readBuffer) - .putPendingBuffer(pendingBuffer); - - freeTempBuffers(); - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacketWriter.java deleted file mode 100644 index 650b927c..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractPacketWriter.java +++ /dev/null @@ -1,389 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import static javasabr.rlib.network.util.NetworkUtils.EMPTY_BUFFER; -import static javasabr.rlib.network.util.NetworkUtils.getRemoteAddress; -import static javasabr.rlib.network.util.NetworkUtils.hexDump; - -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.CompletionHandler; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.BufferAllocator; -import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.PacketWriter; -import javasabr.rlib.network.packet.WritablePacket; -import javasabr.rlib.network.util.NetworkUtils; -import lombok.RequiredArgsConstructor; -import org.jspecify.annotations.Nullable; - -/** - * @author JavaSaBr - */ -@RequiredArgsConstructor -public abstract class AbstractPacketWriter> implements - PacketWriter { - - private static final Logger LOGGER = LoggerManager.getLogger(AbstractPacketWriter.class); - - private final CompletionHandler writeHandler = new CompletionHandler<>() { - - @Override - public void completed(Integer result, WritablePacket packet) { - handleSuccessfulWriting(result, packet); - } - - @Override - public void failed(Throwable exc, WritablePacket packet) { - handleFailedWriting(exc, packet); - } - }; - - protected final AtomicBoolean isWriting = new AtomicBoolean(); - - protected final C connection; - protected final AsynchronousSocketChannel channel; - protected final BufferAllocator bufferAllocator; - protected final ByteBuffer firstWriteBuffer; - protected final ByteBuffer secondWriteBuffer; - - protected volatile @Nullable ByteBuffer firstWriteTempBuffer; - protected volatile @Nullable ByteBuffer secondWriteTempBuffer; - - protected volatile ByteBuffer writingBuffer = EMPTY_BUFFER; - - protected final Runnable updateActivityFunction; - protected final Supplier<@Nullable WritablePacket> nextWritePacketSupplier; - protected final Consumer writtenPacketHandler; - protected final BiConsumer sentPacketHandler; - - public AbstractPacketWriter( - C connection, - AsynchronousSocketChannel channel, - BufferAllocator bufferAllocator, - Runnable updateActivityFunction, - Supplier<@Nullable WritablePacket> packetProvider, - Consumer writtenPacketHandler, - BiConsumer sentPacketHandler) { - this.connection = connection; - this.channel = channel; - this.bufferAllocator = bufferAllocator; - this.firstWriteBuffer = bufferAllocator.takeWriteBuffer(); - this.secondWriteBuffer = bufferAllocator.takeWriteBuffer(); - this.updateActivityFunction = updateActivityFunction; - this.nextWritePacketSupplier = packetProvider; - this.writtenPacketHandler = writtenPacketHandler; - this.sentPacketHandler = sentPacketHandler; - } - - @Override - public void writeNextPacket() { - - if (connection.isClosed() || !isWriting.compareAndSet(false, true)) { - return; - } - - var waitPacket = nextWritePacketSupplier.get(); - - if (waitPacket == null) { - isWriting.set(false); - return; - } - - var resultBuffer = serialize(waitPacket); - - if (resultBuffer.limit() != 0) { - writingBuffer = resultBuffer; - - LOGGER.debug( - channel, - resultBuffer, - (ch, buf) -> "Write to channel \"" + getRemoteAddress(ch) + "\" data:\n" + hexDump(buf)); - - channel.write(resultBuffer, waitPacket, writeHandler); - } else { - isWriting.set(false); - } - - writtenPacketHandler.accept(waitPacket); - } - - protected ByteBuffer serialize(WritablePacket packet) { - - if (packet instanceof WritablePacketWrapper) { - packet = ((WritablePacketWrapper) packet).getPacket(); - } - - W resultPacket = (W) packet; - - var expectedLength = packet.getExpectedLength(); - var totalSize = expectedLength == -1 ? -1 : getTotalSize(packet, expectedLength); - - // if the packet is too big to use a write buffer - if (expectedLength != -1 && totalSize > firstWriteBuffer.capacity()) { - var first = bufferAllocator.takeBuffer(totalSize); - var second = bufferAllocator.takeBuffer(totalSize); - firstWriteTempBuffer = first; - secondWriteTempBuffer = second; - return serialize(resultPacket, expectedLength, totalSize, first, second); - } else { - return serialize(resultPacket, expectedLength, totalSize, firstWriteBuffer, secondWriteBuffer); - } - } - - /** - * Get a total size of packet if it possible. - * - * @param packet the packet. - * @param expectedLength the expected size. - * @return the total size or -1. - */ - protected abstract int getTotalSize(WritablePacket packet, int expectedLength); - - /** - * Serialize packet to byte buffer. - * - * @param packet the packet to serialize. - * @param expectedLength the packet's expected size. - * @param totalSize the packet's total size. - * @param firstBuffer the first byte buffer. - * @param secondBuffer the second byte buffer. - * @return the buffer to write to channel. - */ - protected ByteBuffer serialize( - W packet, - int expectedLength, - int totalSize, - ByteBuffer firstBuffer, - ByteBuffer secondBuffer) { - - if (!onBeforeWrite(packet, expectedLength, totalSize, firstBuffer, secondBuffer)) { - return firstBuffer - .clear() - .limit(0); - } else if (!onWrite(packet, expectedLength, totalSize, firstBuffer, secondBuffer)) { - return firstBuffer - .clear() - .limit(0); - } else if (!onAfterWrite(packet, expectedLength, totalSize, firstBuffer, secondBuffer)) { - return firstBuffer - .clear() - .limit(0); - } - - return onResult(packet, expectedLength, totalSize, firstBuffer, secondBuffer); - } - - /** - * Handle a byte buffer before writing packet's data. - * - * @param packet the packet. - * @param expectedLength the packet's expected size. - * @param totalSize the packet's total size. - * @param firstBuffer the first byte buffer. - * @param secondBuffer the second byte buffer. - * @return true if handling was successful. - */ - protected boolean onBeforeWrite( - W packet, - int expectedLength, - int totalSize, - ByteBuffer firstBuffer, - ByteBuffer secondBuffer) { - firstBuffer.clear(); - return true; - } - - /** - * Write a packet to byte buffer. - * - * @param packet the packet. - * @param expectedLength the packet's expected size. - * @param totalSize the packet's total size. - * @param firstBuffer the first byte buffer. - * @param secondBuffer the second byte buffer. - * @return true if writing was successful. - */ - protected boolean onWrite( - W packet, - int expectedLength, - int totalSize, - ByteBuffer firstBuffer, - ByteBuffer secondBuffer) { - return packet.write(firstBuffer); - } - - /** - * Handle a byte buffer after writing packet's data. - * - * @param packet the packet. - * @param expectedLength the packet's expected size. - * @param totalSize the packet's total size. - * @param firstBuffer the first byte buffer. - * @param secondBuffer the second byte buffer. - * @return true if handling was successful. - */ - protected boolean onAfterWrite( - W packet, - int expectedLength, - int totalSize, - ByteBuffer firstBuffer, - ByteBuffer secondBuffer) { - firstBuffer.flip(); - return true; - } - - /** - * Handle a final result byte buffer. - * - * @param packet the packet. - * @param expectedLength the packet's expected size. - * @param totalSize the packet's total size. - * @param firstBuffer the first byte buffer. - * @param secondBuffer the second byte buffer. - * @return the same byte buffer. - */ - protected ByteBuffer onResult( - W packet, - int expectedLength, - int totalSize, - ByteBuffer firstBuffer, - ByteBuffer secondBuffer) { - return firstBuffer.position(0); - } - - protected ByteBuffer writeHeader(ByteBuffer buffer, int position, int value, int headerSize) { - try { - - switch (headerSize) { - case 1: - buffer.put(position, (byte) value); - break; - case 2: - buffer.putShort(position, (short) value); - break; - case 4: - buffer.putInt(position, value); - break; - default: - throw new IllegalStateException("Wrong packet's header size: " + headerSize); - } - - return buffer; - - } catch (IndexOutOfBoundsException ex) { - LOGGER.error( - "Cannot write header by position " + position + " with header size " + headerSize + " to buffer " + buffer); - throw ex; - } - } - - protected ByteBuffer writeHeader(ByteBuffer buffer, int value, int headerSize) { - - switch (headerSize) { - case 1: - buffer.put((byte) value); - break; - case 2: - buffer.putShort((short) value); - break; - case 4: - buffer.putInt(value); - break; - default: - throw new IllegalStateException("Wrong packet's header size: " + headerSize); - } - - return buffer; - } - - /** - * Handle successful wrote data. - * - * @param result the count of wrote bytes. - * @param packet the sent packet. - */ - protected void handleSuccessfulWriting(Integer result, WritablePacket packet) { - updateActivityFunction.run(); - - if (result == -1) { - sentPacketHandler.accept(packet, Boolean.FALSE); - connection.close(); - return; - } - - var writingBuffer = this.writingBuffer; - - if (writingBuffer.remaining() > 0) { - LOGGER.debug( - writingBuffer, - channel, - (buf, ch) -> "Buffer was not consumed fully, " + "try to write else " + buf.remaining() + " bytes to channel " - + NetworkUtils.getRemoteAddress(ch)); - channel.write(writingBuffer, packet, writeHandler); - return; - } else { - LOGGER.debug(result, bytes -> "Done writing " + bytes + " bytes"); - } - - sentPacketHandler.accept(packet, Boolean.TRUE); - - if (isWriting.compareAndSet(true, false)) { - - // if we have temp buffers, we can remove it after finishing writing a packet - if (firstWriteTempBuffer != null) { - clearTempBuffers(); - } - - writeNextPacket(); - } - } - - /** - * Handle the exception during writing the packet. - * - * @param exception the exception. - * @param packet the packet. - */ - protected void handleFailedWriting(Throwable exception, WritablePacket packet) { - LOGGER.error(new RuntimeException("Failed writing packet: " + packet, exception)); - - if (!connection.isClosed()) { - if (isWriting.compareAndSet(true, false)) { - writeNextPacket(); - } - } - } - - @Override - public void close() { - - bufferAllocator - .putWriteBuffer(firstWriteBuffer) - .putWriteBuffer(secondWriteBuffer); - - clearTempBuffers(); - - writingBuffer = EMPTY_BUFFER; - } - - protected void clearTempBuffers() { - - var secondWriteTempBuffer = this.secondWriteTempBuffer; - var firstWriteTempBuffer = this.firstWriteTempBuffer; - - if (secondWriteTempBuffer != null) { - this.secondWriteTempBuffer = null; - bufferAllocator.putBuffer(secondWriteTempBuffer); - } - - if (firstWriteTempBuffer != null) { - this.firstWriteTempBuffer = null; - bufferAllocator.putBuffer(firstWriteTempBuffer); - } - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReadableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReadableNetworkPacket.java new file mode 100644 index 00000000..2b3b720c --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReadableNetworkPacket.java @@ -0,0 +1,128 @@ +package javasabr.rlib.network.packet.impl; + +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import javasabr.rlib.common.util.ClassUtils; +import javasabr.rlib.network.Connection; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.NoArgsConstructor; + +/** + * The base implementation of {@link ReadableNetworkPacket}. + * + * @author JavaSaBr + */ +@CustomLog +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public abstract class AbstractReadableNetworkPacket + extends AbstractNetworkPacket implements ReadableNetworkPacket { + + @Override + public boolean read(ByteBuffer buffer, int remainingDataLength) { + int oldLimit = buffer.limit(); + int oldPosition = buffer.position(); + try { + buffer.limit(oldPosition + remainingDataLength); + readImpl(buffer); + return true; + } catch (Exception e) { + buffer.position(oldPosition); + handleException(buffer, e); + return false; + } finally { + buffer.limit(oldLimit); + } + } + + protected void readImpl(ByteBuffer buffer) {} + + /** + * Reads 1 byte from the buffer. + */ + protected int readByte(ByteBuffer buffer) { + return buffer.get(); + } + + /** + * Fills byte array with data from the buffer. + */ + protected void readBytes(ByteBuffer buffer, byte[] array) { + buffer.get(array); + } + + /** + * Fills byte array with data from the buffer. + */ + protected void readBytes(ByteBuffer buffer, byte[] array, int offset, int length) { + buffer.get(array, offset, length); + } + + /** + * Reads 4 bytes from buffer. + */ + protected float readFloat(ByteBuffer buffer) { + return buffer.getFloat(); + } + + /** + * Reads 8 bytes from buffer. + */ + protected double readDouble(ByteBuffer buffer) { + return buffer.getDouble(); + } + + /** + * Reads 4 bytes from buffer. + */ + protected int readInt(ByteBuffer buffer) { + return buffer.getInt(); + } + + /** + * Reads 8 bytes from buffer. + */ + protected long readLong(ByteBuffer buffer) { + return buffer.getLong(); + } + + /** + * Reads 2 bytes from buffer. + */ + protected int readShort(ByteBuffer buffer) { + return buffer.getShort(); + } + + /** + * Read a string from buffer. + */ + protected String readString(ByteBuffer buffer, int maxLength) { + int length = readInt(buffer); + if (maxLength < length) { + throw new IllegalArgumentException("Buffer contains too long string:[%s] with limit:[%s]".formatted( + length, + maxLength)); + } + try { + int requiredRemainingBytes = length * 2; + if (requiredRemainingBytes > buffer.remaining()) { + throw new IllegalArgumentException("Buffer:[%s] has no enough data for string:[%s]".formatted(buffer, length)); + } + + var chars = new char[length]; + + for (int i = 0; i < length; i++) { + chars[i] = buffer.getChar(); + } + + return new String(chars); + } catch (OutOfMemoryError ex) { + log.error(length, "Cannot read too long string:[%s] by memory reason"::formatted); + throw ex; + } catch (BufferUnderflowException ex) { + log.error(length, buffer, "Cannot read too long string:[%s] by not enough data in buffer:[%s]"::formatted); + throw ex; + } + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReadablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReadablePacket.java deleted file mode 100644 index 86a24316..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReadablePacket.java +++ /dev/null @@ -1,161 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.ReadablePacket; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -/** - * The base implementation of {@link ReadablePacket}. - * - * @author JavaSaBr - */ -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public abstract class AbstractReadablePacket> extends AbstractPacket implements - ReadablePacket { - - @Override - public boolean read(Connection connection, ByteBuffer buffer, int length) { - var oldLimit = buffer.limit(); - try { - buffer.limit(buffer.position() + length); - readImpl(ClassUtils.unsafeNNCast(connection), buffer); - return true; - } catch (Exception e) { - handleException(buffer, e); - return false; - } finally { - buffer.limit(oldLimit); - } - } - - /** - * Read packet's data from byte buffer. - * - * @param connection the network connection. - * @param buffer the buffer with received data. - */ - protected void readImpl(C connection, ByteBuffer buffer) { - } - - /** - * Read 1 byte from buffer. - * - * @param buffer the buffer to read. - * @return 1 byte from the buffer. - */ - protected int readByte(ByteBuffer buffer) { - return buffer.get(); - } - - /** - * Fill byte array with data from received buffer. - * - * @param buffer the buffer to read. - * @param array the filled byte array. - */ - protected void readBytes(ByteBuffer buffer, byte[] array) { - buffer.get(array); - } - - /** - * Fill byte array with data from received buffer. - * - * @param buffer the buffer to read. - * @param array the byte array. - * @param offset the offset to fill the byte array. - * @param length the length to fill the byte array. - */ - protected void readBytes(ByteBuffer buffer, byte[] array, int offset, int length) { - buffer.get(array, offset, length); - } - - /** - * Read 4 bytes from buffer. - * - * @param buffer the buffer to read. - * @return 4 bytes as float from the buffer. - */ - protected float readFloat(ByteBuffer buffer) { - return buffer.getFloat(); - } - - /** - * Read 8 bytes from buffer. - * - * @param buffer the buffer to read. - * @return 4 bytes as double from the buffer. - */ - protected double readDouble(ByteBuffer buffer) { - return buffer.getDouble(); - } - - /** - * Read 4 bytes from buffer. - * - * @param buffer the buffer to read. - * @return 4 bytes as int from the buffer. - */ - protected int readInt(ByteBuffer buffer) { - return buffer.getInt(); - } - - /** - * Read 8 bytes from buffer. - * - * @param buffer the buffer to read. - * @return 8 bytes as long from buffer. - */ - protected long readLong(ByteBuffer buffer) { - return buffer.getLong(); - } - - /** - * Read 2 bytes from buffer. - * - * @param buffer the buffer to read. - * @return 2 bytes as short from buffer. - */ - protected int readShort(ByteBuffer buffer) { - return buffer.getShort(); - } - - /** - * Read a string from buffer. - * - * @param buffer the buffer to read. - * @return the read string from the buffer. - */ - protected String readString(ByteBuffer buffer) { - - var length = readInt(buffer); - try { - - var requiredRemainingBytes = length * 2; - - if (requiredRemainingBytes > buffer.remaining()) { - throw new IllegalStateException("Found too long string " + length + " from buffer " + buffer); - } - - var array = new char[length]; - - for (int i = 0; i < length; i++) { - array[i] = buffer.getChar(); - } - - return new String(array); - - } catch (OutOfMemoryError ex) { - LOGGER.error("Cannot read too long \"" + length + "\" string by memory reason"); - throw ex; - } catch (BufferUnderflowException ex) { - LOGGER.error( - "Cannot read string because buffer doesn't contains enough data. " + "Expected string length " + length - + ", buffer " + buffer); - throw ex; - } - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritableNetworkPacket.java similarity index 76% rename from rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritablePacket.java rename to rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritableNetworkPacket.java index 6f004c3d..30c057a2 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritablePacket.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractReusableWritableNetworkPacket.java @@ -10,30 +10,37 @@ import javasabr.rlib.network.packet.ReusableWritablePacket; import javasabr.rlib.reusable.pool.Pool; import javasabr.rlib.reusable.pool.PoolFactory; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; /** - * The reusable implementation of {@link AbstractWritablePacket} using the counter to control the life cycle of this + * The reusable implementation of {@link AbstractWritableNetworkPacket} using the counter to control the life cycle of this * packet. * * @author JavaSaBr */ -public abstract class AbstractReusableWritablePacket extends AbstractWritablePacket implements ReusableWritablePacket { +@CustomLog +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractReusableWritableNetworkPacket extends AbstractWritableNetworkPacket + implements ReusableWritablePacket { - protected static final ThreadLocal, Pool>> LOCAL_POOLS = ThreadLocal.withInitial( - HashMap::new); + protected static final ThreadLocal, Pool>> + LOCAL_POOLS = ThreadLocal.withInitial(HashMap::new); - protected final AtomicInteger counter; + final AtomicInteger counter; /** * The pool to store this packet after using. */ - protected volatile @Nullable Pool pool; - protected volatile int barrier; + @Nullable + volatile Pool pool; + volatile int barrier; - protected int barrierSink; + int barrierSink; - public AbstractReusableWritablePacket() { + public AbstractReusableWritableNetworkPacket() { this.counter = new AtomicInteger(); } @@ -41,11 +48,8 @@ public AbstractReusableWritablePacket() { public boolean write(ByteBuffer buffer) { if (counter.get() < 1) { - LOGGER.warning( - this, - arg -> "Attempt to write is already finished packet " + arg + " on thread " + Thread - .currentThread() - .getName()); + log.warning(this, arg -> + "Attempt to write is already finished packet:[%s] on thread:[%s]".formatted(arg, Thread.currentThread().getName())); return false; } @@ -115,11 +119,11 @@ public void forceComplete() { } /** - * Get thread local pool. + * Gets thread local pool. * * @return thread local pool. */ - protected Pool getThreadLocalPool() { + protected Pool threadLocalPool() { Class packetClass = ClassUtils.unsafeNNCast(getClass()); return LOCAL_POOLS .get() @@ -127,22 +131,20 @@ protected Pool getThreadLocalPool() { } /** - * Get the pool to store used packet. + * Gets the pool to store used packet. * * @return the pool to store used packet. */ protected Pool getPool() { Pool local = this.pool; - if (local != null) { return local; } - this.pool = getThreadLocalPool(); + this.pool = threadLocalPool(); local = this.pool; - return notNull(local); } @@ -200,6 +202,6 @@ public void increaseSends(int count) { @Override public String toString() { - return "AbstractReusableSendablePacket{" + "counter=" + counter + "} " + super.toString(); + return "AbstractReusableWritableNetworkPacket{" + "counter=" + counter + "} " + super.toString(); } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSSLPacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSSLPacketReader.java deleted file mode 100644 index 3ec1d274..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSSLPacketReader.java +++ /dev/null @@ -1,263 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import static javasabr.rlib.network.util.NetworkUtils.hexDump; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousSocketChannel; -import javasabr.rlib.common.function.NotNullConsumer; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.BufferAllocator; -import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.packet.WritablePacket; -import javasabr.rlib.network.util.NetworkUtils; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; -import javax.net.ssl.SSLException; - -/** - * @param the readable packet's type. - * @param the connection's type. - */ -public abstract class AbstractSSLPacketReader> extends - AbstractPacketReader { - - private static final Logger LOGGER = LoggerManager.getLogger(AbstractSSLPacketReader.class); - - private static final ByteBuffer[] EMPTY_BUFFERS = { - NetworkUtils.EMPTY_BUFFER - }; - - private static final int SKIP_READ_PACKETS = -1; - - protected final SSLEngine sslEngine; - protected final NotNullConsumer packetWriter; - - protected volatile ByteBuffer sslNetworkBuffer; - protected volatile ByteBuffer sslDataBuffer; - - protected AbstractSSLPacketReader( - C connection, - AsynchronousSocketChannel channel, - BufferAllocator bufferAllocator, - Runnable updateActivityFunction, - NotNullConsumer readPacketHandler, - SSLEngine sslEngine, - NotNullConsumer packetWriter, - int maxPacketsByRead) { - super(connection, channel, bufferAllocator, updateActivityFunction, readPacketHandler, maxPacketsByRead); - this.sslEngine = sslEngine; - this.sslDataBuffer = bufferAllocator.takeBuffer(sslEngine - .getSession() - .getApplicationBufferSize()); - this.sslNetworkBuffer = bufferAllocator.takeBuffer(sslEngine - .getSession() - .getPacketBufferSize()); - this.packetWriter = packetWriter; - } - - @Override - protected ByteBuffer getBufferToReadFromChannel() { - return sslNetworkBuffer; - } - - @Override - protected void handleReceivedData(Integer receivedBytes, ByteBuffer readingBuffer) { - - if (receivedBytes == -1) { - doHandshake(readingBuffer, -1); - return; - } - - super.handleReceivedData(receivedBytes, readingBuffer); - } - - @Override - protected int readPackets(ByteBuffer receivedBuffer) { - - var handshakeStatus = sslEngine.getHandshakeStatus(); - - // ssl engine is ready to decrypt - if (handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING) { - return decryptAndRead(receivedBuffer); - } else { - return doHandshake(receivedBuffer, receivedBuffer.limit()); - } - } - - protected int doHandshake(ByteBuffer receivedBuffer, int receivedBytes) { - - var handshakeStatus = sslEngine.getHandshakeStatus(); - - while (handshakeStatus != HandshakeStatus.FINISHED && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) { - LOGGER.debug(handshakeStatus, status -> "Do handshake with status: " + status); - - SSLEngineResult result; - - switch (handshakeStatus) { - case NEED_UNWRAP: { - - if (receivedBytes == -1) { - - if (sslEngine.isInboundDone() && sslEngine.isOutboundDone()) { - return SKIP_READ_PACKETS; - } - - try { - sslEngine.closeInbound(); - } catch (SSLException e) { - LOGGER.error("This engine was forced to close inbound, without having received the " - + "proper SSL/TLS close notification message from the peer, due to end of stream."); - } - - sslEngine.closeOutbound(); - handshakeStatus = sslEngine.getHandshakeStatus(); - break; - - } else if (!receivedBuffer.hasRemaining()) { - receivedBuffer.clear(); - return SKIP_READ_PACKETS; - } - - try { - LOGGER.debug(receivedBuffer, buff -> "Try to unwrap data:\n" + hexDump(buff)); - result = sslEngine.unwrap(receivedBuffer, EMPTY_BUFFERS); - handshakeStatus = result.getHandshakeStatus(); - LOGGER.debug(handshakeStatus, status -> "Handshake status: " + status + " after unwrapping"); - } catch (SSLException sslException) { - LOGGER.error("A problem was encountered while processing the data that caused the " - + "SSLEngine to abort. Will try to properly close connection..."); - sslEngine.closeOutbound(); - handshakeStatus = sslEngine.getHandshakeStatus(); - break; - } - - switch (result.getStatus()) { - case OK: - break; - case BUFFER_OVERFLOW: - throw new IllegalStateException("Unexpected ssl engine result"); - case BUFFER_UNDERFLOW: - LOGGER.debug("Increase ssl network buffer"); - increaseNetworkBuffer(); - break; - case CLOSED: - if (sslEngine.isOutboundDone()) { - return SKIP_READ_PACKETS; - } else { - sslEngine.closeOutbound(); - handshakeStatus = sslEngine.getHandshakeStatus(); - break; - } - default: - throw new IllegalStateException("Invalid SSL status: " + result.getStatus()); - } - break; - } - case NEED_WRAP: - LOGGER.debug("Send command to wrap data"); - packetWriter.accept(SSLWritablePacket.getInstance()); - sslNetworkBuffer.clear(); - return SKIP_READ_PACKETS; - case NEED_TASK: - - Runnable task; - - while ((task = sslEngine.getDelegatedTask()) != null) { - LOGGER.debug(task, t -> "Execute SSL Engine's task: " + t.getClass()); - task.run(); - } - - handshakeStatus = sslEngine.getHandshakeStatus(); - - LOGGER.debug(handshakeStatus, status -> "Handshake status: " + status + " after engine tasks"); - - if (handshakeStatus == HandshakeStatus.NEED_UNWRAP && !receivedBuffer.hasRemaining()) { - sslNetworkBuffer.clear(); - return SKIP_READ_PACKETS; - } - - break; - default: - throw new IllegalStateException("Invalid SSL status: " + handshakeStatus); - } - } - - if (!receivedBuffer.hasRemaining()) { - - // if buffer is empty and status is FINISHED then we can notify writer - if (handshakeStatus == HandshakeStatus.FINISHED) { - packetWriter.accept(SSLWritablePacket.getInstance()); - } - - receivedBuffer.clear(); - - return SKIP_READ_PACKETS; - } - - return decryptAndRead(receivedBuffer); - } - - protected int decryptAndRead(ByteBuffer receivedBuffer) { - - int total = 0; - - while (receivedBuffer.hasRemaining()) { - - SSLEngineResult result; - try { - LOGGER.debug(receivedBuffer, buf -> "Try to decrypt data:\n" + hexDump(buf)); - result = sslEngine.unwrap(receivedBuffer, sslDataBuffer.clear()); - } catch (SSLException e) { - var handshakeStatus = sslEngine.getHandshakeStatus(); - throw new IllegalStateException(e); - } - - switch (result.getStatus()) { - case OK: - sslDataBuffer.flip(); - LOGGER.debug(sslDataBuffer, buf -> "Decrypted data:\n" + hexDump(buf)); - total += readPackets(sslDataBuffer, pendingBuffer); - break; - case BUFFER_OVERFLOW: - increaseDataBuffer(); - return decryptAndRead(receivedBuffer); - case CLOSED: - closeConnection(); - return SKIP_READ_PACKETS; - default: - - if (receivedBuffer.position() > 0) { - receivedBuffer.compact(); - return total; - } - - throw new IllegalStateException("Invalid SSL status: " + result.getStatus()); - } - } - - receivedBuffer.clear(); - - return total; - } - - private void increaseNetworkBuffer() { - sslNetworkBuffer = NetworkUtils.increasePacketBuffer(sslNetworkBuffer, bufferAllocator, sslEngine); - } - - private void increaseDataBuffer() { - sslDataBuffer = NetworkUtils.increaseApplicationBuffer(sslDataBuffer, bufferAllocator, sslEngine); - } - - protected void closeConnection() { - try { - sslEngine.closeOutbound(); - channel.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSSLPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSSLPacketWriter.java deleted file mode 100644 index 3b96e8be..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSSLPacketWriter.java +++ /dev/null @@ -1,211 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import static javasabr.rlib.network.util.NetworkUtils.EMPTY_BUFFER; -import static javasabr.rlib.network.util.NetworkUtils.hexDump; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousSocketChannel; -import javasabr.rlib.common.function.NotNullBiConsumer; -import javasabr.rlib.common.function.NotNullConsumer; -import javasabr.rlib.common.function.NullableSupplier; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.BufferAllocator; -import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.WritablePacket; -import javasabr.rlib.network.util.NetworkUtils; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; -import javax.net.ssl.SSLException; -import org.jspecify.annotations.Nullable; - -public abstract class AbstractSSLPacketWriter> extends - AbstractPacketWriter { - - private static final Logger LOGGER = LoggerManager.getLogger(AbstractSSLPacketWriter.class); - - private static final ByteBuffer[] EMPTY_BUFFERS = { - NetworkUtils.EMPTY_BUFFER - }; - - protected final SSLEngine sslEngine; - protected final NotNullConsumer packetWriter; - protected final NotNullConsumer queueAtFirst; - - protected volatile ByteBuffer sslNetworkBuffer; - - public AbstractSSLPacketWriter( - C connection, - AsynchronousSocketChannel channel, - BufferAllocator bufferAllocator, - Runnable updateActivityFunction, - NullableSupplier packetProvider, - NotNullConsumer writtenPacketHandler, - NotNullBiConsumer sentPacketHandler, - SSLEngine sslEngine, - NotNullConsumer packetWriter, - NotNullConsumer queueAtFirst) { - super( - connection, - channel, - bufferAllocator, - updateActivityFunction, - packetProvider, - writtenPacketHandler, - sentPacketHandler); - this.sslEngine = sslEngine; - this.packetWriter = packetWriter; - this.queueAtFirst = queueAtFirst; - this.sslNetworkBuffer = bufferAllocator.takeBuffer(sslEngine - .getSession() - .getPacketBufferSize()); - } - - @Override - public void writeNextPacket() { - - var status = sslEngine.getHandshakeStatus(); - - switch (status) { - case NEED_UNWRAP: - return; - } - - super.writeNextPacket(); - } - - @Override - protected ByteBuffer serialize(WritablePacket packet) { - - var status = sslEngine.getHandshakeStatus(); - - if (status == HandshakeStatus.FINISHED || status == HandshakeStatus.NOT_HANDSHAKING) { - - if (packet instanceof SSLWritablePacket) { - return EMPTY_BUFFER; - } - - var dataBuffer = super.serialize(packet); - - LOGGER.debug(dataBuffer, buff -> "Try to encrypt data:\n" + hexDump(buff)); - - SSLEngineResult result; - try { - result = sslEngine.wrap(dataBuffer, sslNetworkBuffer.clear()); - } catch (SSLException e) { - throw new RuntimeException(e); - } - - switch (result.getStatus()) { - case BUFFER_UNDERFLOW: - increaseNetworkBuffer(); - break; - case BUFFER_OVERFLOW: - throw new IllegalStateException("Unexpected ssl engine result"); - case OK: - return sslNetworkBuffer.flip(); - case CLOSED: - closeConnection(); - return EMPTY_BUFFER; - } - } - - var bufferToWrite = doHandshake(packet); - - if (bufferToWrite != null) { - return bufferToWrite; - } - - throw new IllegalStateException(); - } - - protected @Nullable ByteBuffer doHandshake(WritablePacket packet) { - - if (!(packet instanceof SSLWritablePacket)) { - LOGGER.debug(packet, pck -> "Return packet " + pck + " to queue as first"); - queueAtFirst.accept(packet); - } - - var handshakeStatus = sslEngine.getHandshakeStatus(); - - while (handshakeStatus != HandshakeStatus.FINISHED && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) { - - SSLEngineResult result; - - switch (handshakeStatus) { - - case NEED_WRAP: - try { - // check result - result = sslEngine.wrap(EMPTY_BUFFERS, sslNetworkBuffer.clear()); - handshakeStatus = result.getHandshakeStatus(); - } catch (SSLException sslException) { - LOGGER.error("A problem was encountered while processing the data that caused the SSLEngine " - + "to abort. Will try to properly close connection..."); - sslEngine.closeOutbound(); - handshakeStatus = sslEngine.getHandshakeStatus(); - break; - } - switch (result.getStatus()) { - case OK: - - sslNetworkBuffer.flip(); - - if (handshakeStatus == HandshakeStatus.NEED_WRAP) { - LOGGER.debug("Send command to wrap data again"); - queueAtFirst.accept(SSLWritablePacket.getInstance()); - } - - LOGGER.debug(sslNetworkBuffer, result, (buf, res) -> "Send wrapped data:\n" + hexDump(buf, res)); - - return sslNetworkBuffer; - case BUFFER_OVERFLOW: - sslNetworkBuffer = NetworkUtils.enlargePacketBuffer(bufferAllocator, sslEngine); - break; - case BUFFER_UNDERFLOW: - throw new IllegalStateException("Unexpected ssl engine result"); - case CLOSED: - try { - return EMPTY_BUFFER; - } catch (Exception e) { - LOGGER.error("Failed to send server's CLOSE message due to socket channel's failure."); - handshakeStatus = sslEngine.getHandshakeStatus(); - } - break; - default: - throw new IllegalStateException("Invalid SSL status: " + result.getStatus()); - } - break; - case NEED_TASK: - Runnable task; - while ((task = sslEngine.getDelegatedTask()) != null) { - LOGGER.debug(task, t -> "Execute SSL Engine's task: " + t.getClass()); - task.run(); - } - handshakeStatus = sslEngine.getHandshakeStatus(); - break; - case NEED_UNWRAP: - break; - default: - throw new IllegalStateException("Invalid SSL status: " + handshakeStatus); - } - } - - return EMPTY_BUFFER; - } - - private void increaseNetworkBuffer() { - sslNetworkBuffer = NetworkUtils.increasePacketBuffer(sslNetworkBuffer, bufferAllocator, sslEngine); - } - - protected void closeConnection() { - try { - sslEngine.closeOutbound(); - channel.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSslNetworkPacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSslNetworkPacketReader.java new file mode 100644 index 00000000..fdaeb2db --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSslNetworkPacketReader.java @@ -0,0 +1,307 @@ +package javasabr.rlib.network.packet.impl; + +import static javasabr.rlib.network.util.NetworkUtils.hexDump; + +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.function.Consumer; +import javasabr.rlib.common.util.BufferUtils; +import javasabr.rlib.network.BufferAllocator; +import javasabr.rlib.network.Connection; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; +import javasabr.rlib.network.util.NetworkUtils; +import javasabr.rlib.network.util.SslUtils; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLException; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; + +/** + * @author JavaSaBr + */ +@CustomLog +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractSslNetworkPacketReader> + extends AbstractNetworkPacketReader { + + private static final ByteBuffer[] EMPTY_BUFFERS = { + NetworkUtils.EMPTY_BUFFER + }; + + private static final int SKIP_READ_PACKETS = -1; + + final SSLEngine sslEngine; + final Consumer packetWriter; + + @Getter(value = AccessLevel.PROTECTED) + volatile ByteBuffer sslNetworkBuffer; + @Getter(value = AccessLevel.PROTECTED) + volatile ByteBuffer sslDataBuffer; + @Getter(value = AccessLevel.PROTECTED) + volatile ByteBuffer sslDataPendingBuffer; + + protected AbstractSslNetworkPacketReader( + C connection, + AsynchronousSocketChannel channel, + BufferAllocator bufferAllocator, + Runnable updateActivityFunction, + Consumer readPacketHandler, + SSLEngine sslEngine, + Consumer packetWriter, + int maxPacketsByRead) { + super(connection, channel, bufferAllocator, updateActivityFunction, readPacketHandler, maxPacketsByRead); + this.sslEngine = sslEngine; + this.sslDataBuffer = bufferAllocator.takeBuffer(sslEngine + .getSession() + .getApplicationBufferSize()); + this.sslDataPendingBuffer = bufferAllocator.takeBuffer(sslEngine + .getSession() + .getApplicationBufferSize() * 2); + this.sslNetworkBuffer = bufferAllocator.takeBuffer(sslEngine + .getSession() + .getPacketBufferSize()) + .limit(0); + this.packetWriter = packetWriter; + } + + @Override + protected void handleReceivedData(int receivedBytes, ByteBuffer readingBuffer) { + if (receivedBytes == -1) { + doHandshake(sslNetworkBuffer(), -1); + return; + } + super.handleReceivedData(receivedBytes, readingBuffer); + } + + @Override + protected int readPackets(ByteBuffer readingBuffer) { + ByteBuffer networkBuffer = moveDataToNetworkBuffer(readingBuffer); + HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus(); + if (SslUtils.isReadyToCrypt(handshakeStatus)) { + return decryptAndRead(networkBuffer); + } else { + return doHandshake(networkBuffer, networkBuffer.limit()); + } + } + + protected int doHandshake(ByteBuffer networkBuffer, int receivedBytes) { + HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus(); + String remoteAddress = remoteAddress(); + while (SslUtils.needToProcess(handshakeStatus)) { + log.debug(remoteAddress, handshakeStatus, "[%s] Do handshake with status:[%s] "::formatted); + switch (handshakeStatus) { + case NEED_UNWRAP: { + SSLEngineResult result; + if (receivedBytes == -1) { + if (sslEngine.isInboundDone() && sslEngine.isOutboundDone()) { + return SKIP_READ_PACKETS; + } + try { + sslEngine.closeInbound(); + } catch (SSLException e) { + log.error("This engine was forced to close inbound, without having received the " + + "proper SSL/TLS close notification message from the peer, due to end of stream."); + } + sslEngine.closeOutbound(); + handshakeStatus = sslEngine.getHandshakeStatus(); + break; + } else if (!networkBuffer.hasRemaining()) { + NetworkUtils.cleanNetworkBuffer(networkBuffer); + return SKIP_READ_PACKETS; + } + try { + logDataBeforeUnwrap(remoteAddress, networkBuffer); + result = sslEngine.unwrap(networkBuffer, EMPTY_BUFFERS); + handshakeStatus = result.getHandshakeStatus(); + log.debug(remoteAddress, handshakeStatus, "[%s] Handshake status:[%s] after unwrapping"::formatted); + } catch (SSLException sslException) { + log.error("A problem was encountered while processing the data that caused the " + + "SSLEngine to abort. Will try to properly close connection..."); + sslEngine.closeOutbound(); + handshakeStatus = sslEngine.getHandshakeStatus(); + break; + } + switch (result.getStatus()) { + case OK: { + break; + } + case BUFFER_OVERFLOW: { + throw new IllegalStateException("Unexpected SSL Engine result:" + result.getStatus()); + } + case BUFFER_UNDERFLOW: { + log.debug(remoteAddress, "[%s] Wait for more received data..."::formatted); + NetworkUtils.compactNetworkBufferIfNeed(networkBuffer); + return SKIP_READ_PACKETS; + } + case CLOSED: { + if (sslEngine.isOutboundDone()) { + return SKIP_READ_PACKETS; + } else { + sslEngine.closeOutbound(); + handshakeStatus = sslEngine.getHandshakeStatus(); + break; + } + } + default: { + throw new IllegalStateException("Invalid SSL Engine result:" + result.getStatus()); + } + } + break; + } + case NEED_WRAP: { + log.debug(remoteAddress, "[%s] Send command to wrap data"::formatted); + packetWriter.accept(SslWrapRequestPacket.getInstance()); + NetworkUtils.cleanNetworkBuffer(networkBuffer); + return SKIP_READ_PACKETS; + } + case NEED_TASK: { + handshakeStatus = SslUtils.executeSslTasks(sslEngine); + log.debug(remoteAddress, handshakeStatus, "[%s] Handshake status:[%s] after engine tasks"::formatted); + if (handshakeStatus == HandshakeStatus.NEED_UNWRAP && !networkBuffer.hasRemaining()) { + NetworkUtils.cleanNetworkBuffer(networkBuffer); + return SKIP_READ_PACKETS; + } + break; + } + default: { + throw new IllegalStateException("Invalid SSL status:" + handshakeStatus); + } + } + } + + if (!networkBuffer.hasRemaining()) { + // if buffer is empty and status is FINISHED then we can notify writer + if (handshakeStatus == HandshakeStatus.FINISHED) { + packetWriter.accept(SslWrapRequestPacket.getInstance()); + } + NetworkUtils.cleanNetworkBuffer(networkBuffer); + return SKIP_READ_PACKETS; + } + + return decryptAndRead(networkBuffer); + } + + protected int decryptAndRead(ByteBuffer receivedBuffer) { + String remoteAddress = remoteAddress(); + int total = 0; + while (receivedBuffer.hasRemaining()) { + ByteBuffer sslDataBuffer = sslDataBuffer(); + SSLEngineResult result; + try { + logDataBeforeDecrypt(remoteAddress, receivedBuffer); + result = sslEngine.unwrap(receivedBuffer, sslDataBuffer.clear()); + } catch (SSLException e) { + throw new IllegalStateException(e); + } + switch (result.getStatus()) { + case OK: { + sslDataBuffer.flip(); + logDataAfterDecrypt(remoteAddress, sslDataBuffer); + total += readPackets(sslDataBuffer, sslDataPendingBuffer); + break; + } + case BUFFER_OVERFLOW: { + log.debug(remoteAddress, "[%s] Increase SSL data buffer and try again..."::formatted); + increaseDataBuffer(); + return decryptAndRead(receivedBuffer); + } + case BUFFER_UNDERFLOW: { + log.debug(remoteAddress, "[%s] Wait for more received data..."::formatted); + NetworkUtils.compactNetworkBufferIfNeed(receivedBuffer); + return SKIP_READ_PACKETS; + } + case CLOSED: { + connection.close(); + return SKIP_READ_PACKETS; + } + default: { + throw new IllegalStateException("Invalid SSL status:" + result.getStatus()); + } + } + } + + log.debug(remoteAddress, "[%s] Clear SSL network buffer"::formatted); + NetworkUtils.cleanNetworkBuffer(receivedBuffer); + return total; + } + + protected ByteBuffer moveDataToNetworkBuffer(ByteBuffer readingBuffer) { + String remoteAddress = remoteAddress(); + logAcceptedNewDataPart(remoteAddress, readingBuffer); + + ByteBuffer sslNetworkBuffer = sslNetworkBuffer(); + int availableSpace = sslNetworkBuffer.capacity() - sslNetworkBuffer.limit(); + if (availableSpace >= readingBuffer.limit()) { + BufferUtils.appendAndClear(sslNetworkBuffer, readingBuffer); + } else { + sslNetworkBuffer = increaseNetworkBuffer(readingBuffer.limit()); + BufferUtils.appendAndClear(sslNetworkBuffer, readingBuffer); + } + + logPendingNetworkData(remoteAddress, sslNetworkBuffer); + return sslNetworkBuffer; + } + + protected synchronized ByteBuffer increaseNetworkBuffer(int extra) { + ByteBuffer current = sslNetworkBuffer(); + int newSize = (int) Math.max(current.capacity() * 1.3, current.capacity() + extra); + sslNetworkBuffer = NetworkUtils + .increaseBuffer(current, bufferAllocator, newSize) + .flip(); + return sslNetworkBuffer; + } + + protected synchronized void increaseDataBuffer() { + int newSize = sslEngine + .getSession() + .getApplicationBufferSize(); + sslDataBuffer = NetworkUtils + .increaseBuffer(sslDataBuffer, bufferAllocator, newSize) + .flip(); + sslDataPendingBuffer = NetworkUtils + .increaseBuffer(sslDataPendingBuffer, bufferAllocator, newSize * 2) + .flip(); + } + + @Override + public void close() { + sslEngine.closeOutbound(); + bufferAllocator + .putBuffer(sslDataBuffer) + .putBuffer(sslDataPendingBuffer) + .putBuffer(sslNetworkBuffer); + super.close(); + } + + private static void logDataBeforeUnwrap(String remoteAddress, ByteBuffer networkBuffer) { + log.debug(remoteAddress, networkBuffer, + (address, buff) -> "[%s] Try to unwrap data:\n%s".formatted(address, hexDump(buff))); + } + + private static void logDataBeforeDecrypt(String remoteAddress, ByteBuffer receivedBuffer) { + log.debug(remoteAddress, receivedBuffer, + (address, buf) -> "[%s] Try to decrypt data:\n%s".formatted(address, hexDump(buf))); + } + + private static void logDataAfterDecrypt(String remoteAddress, ByteBuffer sslDataBuffer) { + log.debug(remoteAddress, sslDataBuffer, + (address, buf) -> "[%s] Decrypted data:\n%s".formatted(address, hexDump(buf))); + } + + private static void logAcceptedNewDataPart(String remoteAddress, ByteBuffer readingBuffer) { + log.debug(remoteAddress, readingBuffer, + (address, buf) -> "[%s] Append new part of received data:\n%s".formatted(address, hexDump(buf))); + } + + private static void logPendingNetworkData(String remoteAddress, ByteBuffer sslNetworkBuffer) { + log.debug(remoteAddress, sslNetworkBuffer, + (address, buf) -> "[%s] Result pending received network data:\n%s".formatted(address, hexDump(buf))); + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSslNetworkPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSslNetworkPacketWriter.java new file mode 100644 index 00000000..bbd649e3 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractSslNetworkPacketWriter.java @@ -0,0 +1,243 @@ +package javasabr.rlib.network.packet.impl; + +import static javasabr.rlib.network.util.NetworkUtils.EMPTY_BUFFER; +import static javasabr.rlib.network.util.NetworkUtils.hexDump; + +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javasabr.rlib.functions.ObjBoolConsumer; +import javasabr.rlib.network.BufferAllocator; +import javasabr.rlib.network.Connection; +import javasabr.rlib.network.packet.WritableNetworkPacket; +import javasabr.rlib.network.util.NetworkUtils; +import javasabr.rlib.network.util.SslUtils; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLException; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +@CustomLog +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public abstract class AbstractSslNetworkPacketWriter> + extends AbstractNetworkPacketWriter { + + private static final ByteBuffer[] EMPTY_BUFFERS = { + NetworkUtils.EMPTY_BUFFER + }; + + final SSLEngine sslEngine; + final Consumer queueAtFirst; + + @Getter(AccessLevel.PROTECTED) + @Nullable + volatile ByteBuffer sslTempBuffer; + + @Getter(AccessLevel.PROTECTED) + volatile ByteBuffer sslNetworkBuffer; + + public AbstractSslNetworkPacketWriter( + C connection, + AsynchronousSocketChannel channel, + BufferAllocator bufferAllocator, + Runnable updateActivityFunction, + Supplier packetProvider, + Consumer serializedToChannelPacketHandler, + ObjBoolConsumer sentPacketHandler, + SSLEngine sslEngine, + Consumer queueAtFirst) { + super( + connection, + channel, + bufferAllocator, + updateActivityFunction, + packetProvider, + serializedToChannelPacketHandler, + sentPacketHandler); + this.sslEngine = sslEngine; + this.queueAtFirst = queueAtFirst; + this.sslNetworkBuffer = bufferAllocator.takeBuffer(sslEngine + .getSession() + .getPacketBufferSize()); + } + + @Override + public boolean tryToSendNextPacket() { + HandshakeStatus status = sslEngine.getHandshakeStatus(); + if (status == HandshakeStatus.NEED_UNWRAP) { + return false; + } + return super.tryToSendNextPacket(); + } + + @Override + protected boolean tryToSendNextPacketImpl() { + HandshakeStatus status = sslEngine.getHandshakeStatus(); + if (SslUtils.isReadyToCrypt(status)) { + return super.tryToSendNextPacketImpl(); + } + + ByteBuffer dataToSend = doHandshake(status); + if (dataToSend != null) { + return writeBuffer(dataToSend, null); + } + + throw new IllegalStateException("Unexpected SSL state"); + } + + @Override + protected ByteBuffer serialize(WritableNetworkPacket packet) { + if (packet instanceof SslWrapRequestPacket) { + return EMPTY_BUFFER; + } + + String remoteAddress = remoteAddress(); + ByteBuffer serialized = super.serialize(packet); + logDataBeforeEncrypt(remoteAddress, serialized); + + ByteBuffer bufferToSend = sslNetworkBuffer(); + SSLEngineResult result = tryEncrypt(serialized, bufferToSend); + + if (result.getStatus() == SSLEngineResult.Status.OK && serialized.hasRemaining()) { + log.debug(remoteAddress, serialized.remaining(), + "[%s] Has remaining [%s] bytes after encrypting, will create temp big buffer"::formatted); + + int tempBufferSize = (int) ((bufferToSend.limit() + serialized.remaining()) * 1.2); + ByteBuffer tempBuffer = bufferAllocator.takeBuffer(tempBufferSize); + tempBuffer.put(bufferToSend.flip()); + + while (serialized.hasRemaining()) { + result = tryEncrypt(serialized, bufferToSend); + if (result.getStatus() != SSLEngineResult.Status.OK) { + break; + } + tempBuffer.put(bufferToSend.flip()); + } + + bufferToSend = tempBuffer; + this.sslTempBuffer = tempBuffer; + } + + return switch (result.getStatus()) { + case BUFFER_UNDERFLOW, BUFFER_OVERFLOW -> + throw new IllegalStateException("Unexpected ssl engine result"); + case OK -> { + bufferToSend.flip(); + logEncryptedData(remoteAddress, bufferToSend); + yield bufferToSend; + } + case CLOSED -> closeAndReturn(); + }; + } + + protected ByteBuffer closeAndReturn() { + connection.close(); + return EMPTY_BUFFER; + } + + protected SSLEngineResult tryEncrypt(ByteBuffer source, ByteBuffer destination) { + try { + return sslEngine.wrap(source, destination.clear()); + } catch (SSLException ex) { + log.error(ex); + throw new RuntimeException(ex); + } + } + + @Nullable + protected ByteBuffer doHandshake(HandshakeStatus handshakeStatus) { + String remoteAddress = remoteAddress(); + while (SslUtils.needToProcess(handshakeStatus)) { + log.debug(remoteAddress, handshakeStatus, "[%s] Do handshake with status:[%s] "::formatted); + ByteBuffer sslNetworkBuffer = sslNetworkBuffer(); + switch (handshakeStatus) { + case NEED_WRAP: { + SSLEngineResult result; + try { + result = sslEngine.wrap(EMPTY_BUFFERS, sslNetworkBuffer.clear()); + } catch (SSLException sslException) { + log.error("A problem was encountered while processing the data that caused the SSLEngine " + + "to abort. Will try to properly close connection..."); + sslEngine.closeOutbound(); + handshakeStatus = sslEngine.getHandshakeStatus(); + break; + } + switch (result.getStatus()) { + case OK: + sslNetworkBuffer.flip(); + logWrappedData(remoteAddress, sslNetworkBuffer, result); + return sslNetworkBuffer; + case BUFFER_OVERFLOW, BUFFER_UNDERFLOW: { + throw new RuntimeException("Unexpected SSL result:" + result); + } + case CLOSED: { + try { + return EMPTY_BUFFER; + } catch (Exception e) { + log.error("Failed to send server's CLOSE message due to socket channel's failure."); + handshakeStatus = sslEngine.getHandshakeStatus(); + } + break; + } + default: { + throw new IllegalStateException("Invalid SSL result:" + result); + } + } + break; + } + case NEED_TASK: { + handshakeStatus = SslUtils.executeSslTasks(sslEngine); + break; + } + case NEED_UNWRAP: { + break; + } + default: { + throw new IllegalStateException("Invalid SSL status:" + handshakeStatus); + } + } + } + + return EMPTY_BUFFER; + } + + @Override + protected void clearTempBuffers() { + super.clearTempBuffers(); + ByteBuffer sslTempBuffer = this.sslTempBuffer; + if (sslTempBuffer != null) { + bufferAllocator.putBuffer(sslTempBuffer); + this.sslTempBuffer = null; + } + } + + @Override + public void close() { + sslEngine.closeOutbound(); + bufferAllocator.putBuffer(sslNetworkBuffer); + super.close(); + } + + private static void logDataBeforeEncrypt(String remoteAddress, ByteBuffer serialized) { + log.debug(remoteAddress, serialized, + (address, buff) -> "[%s] Try to encrypt data:\n%s".formatted(address, hexDump(buff))); + } + + private static void logEncryptedData(String remoteAddress, ByteBuffer bufferToSend) { + log.debug(remoteAddress, bufferToSend, + (address, buff) -> "[%s] Result encrypted data:\n%s".formatted(address, hexDump(buff))); + } + + private static void logWrappedData(String remoteAddress, ByteBuffer sslNetworkBuffer, SSLEngineResult result) { + log.debug(remoteAddress, sslNetworkBuffer, result, + (address, buf, res) -> "[%s] Send wrapped data:\n%s".formatted(address, hexDump(buf, res))); + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractWritableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractWritableNetworkPacket.java new file mode 100644 index 00000000..7bf754f6 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractWritableNetworkPacket.java @@ -0,0 +1,106 @@ +package javasabr.rlib.network.packet.impl; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import javasabr.rlib.network.packet.WritableNetworkPacket; +import lombok.CustomLog; + +/** + * The base implementation of the {@link WritableNetworkPacket}. + * + * @author JavaSaBr + */ +@CustomLog +public abstract class AbstractWritableNetworkPacket extends AbstractNetworkPacket + implements WritableNetworkPacket { + + @Override + public boolean write(ByteBuffer buffer) { + try { + writeImpl(buffer); + return true; + } catch (Exception e) { + handleException(buffer, e); + return false; + } + } + + /** + * The process of writing this packet to the buffer. + */ + protected void writeImpl(ByteBuffer buffer) {} + + /** + * Write 1 byte to the buffer. + */ + protected void writeByte(ByteBuffer buffer, int value) { + buffer.put((byte) value); + } + + /** + * Write 2 bytes to the buffer. + */ + protected void writeChar(ByteBuffer buffer, char value) { + buffer.putChar(value); + } + + /** + * Write 2 bytes to the buffer. + */ + protected void writeChar(ByteBuffer buffer, int value) { + buffer.putChar((char) value); + } + + /** + * Write 4 bytes to the buffer. + */ + protected void writeFloat(ByteBuffer buffer, float value) { + buffer.putFloat(value); + } + + /** + * Write 4 bytes to the buffer. + */ + protected void writeInt(ByteBuffer buffer, int value) { + buffer.putInt(value); + } + + /** + * Write 8 bytes to the buffer. + */ + protected void writeLong(ByteBuffer buffer, long value) { + buffer.putLong(value); + } + + /** + * Writes 2 bytes to the buffer. + */ + protected void writeShort(ByteBuffer buffer, int value) { + buffer.putShort((short) value); + } + + /** + * Writes the string to the buffer. + */ + protected void writeString(ByteBuffer buffer, String string) { + try { + writeInt(buffer, string.length()); + for (int i = 0, length = string.length(); i < length; i++) { + buffer.putChar(string.charAt(i)); + } + } catch (BufferOverflowException ex) { + log.error( + string.length(), + buffer, + "Cannot write string to buffer because the string is too long. String length:[%s], buffer:[%s]"::formatted); + throw ex; + } + } + + /** + * Write a data buffer to packet buffer. + */ + protected void writeBuffer(ByteBuffer buffer, ByteBuffer data) { + buffer.put(data.array(), data.position(), data.limit()); + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractWritablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractWritablePacket.java deleted file mode 100644 index d2c9444d..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/AbstractWritablePacket.java +++ /dev/null @@ -1,30 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import java.nio.ByteBuffer; -import javasabr.rlib.network.packet.WritablePacket; - -/** - * The base implementation of the {@link WritablePacket}. - * - * @author JavaSaBr - */ -public abstract class AbstractWritablePacket extends AbstractPacket implements WritablePacket { - - @Override - public boolean write(ByteBuffer buffer) { - try { - writeImpl(buffer); - return true; - } catch (Exception e) { - handleException(buffer, e); - return false; - } - } - - /** - * The process of writing this packet to the buffer. - * - * @param buffer the buffer - */ - protected void writeImpl(ByteBuffer buffer) {} -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultPacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultNetworkPacketReader.java similarity index 51% rename from rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultPacketReader.java rename to rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultNetworkPacketReader.java index 2bcb97e4..5eb079e5 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultPacketReader.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultNetworkPacketReader.java @@ -2,35 +2,36 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; +import java.util.function.Consumer; import java.util.function.IntFunction; -import javasabr.rlib.common.function.NotNullConsumer; import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.ReadablePacket; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; /** - * @param the readable packet's type. - * @param the connections' type. * @author JavaSaBR */ -public class DefaultPacketReader> extends - AbstractPacketReader { +@FieldDefaults(level = AccessLevel.PROTECTED) +public class DefaultNetworkPacketReader> + extends AbstractNetworkPacketReader { - private final IntFunction readPacketFactory; - private final int packetLengthHeaderSize; + final IntFunction readablePacketFactory; + final int packetLengthHeaderSize; - public DefaultPacketReader( + public DefaultNetworkPacketReader( C connection, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, Runnable updateActivityFunction, - NotNullConsumer readPacketHandler, - IntFunction readPacketFactory, + Consumer packetHandler, + IntFunction readablePacketFactory, int packetLengthHeaderSize, int maxPacketsByRead) { - super(connection, channel, bufferAllocator, updateActivityFunction, readPacketHandler, maxPacketsByRead); - this.readPacketFactory = readPacketFactory; + super(connection, channel, bufferAllocator, updateActivityFunction, packetHandler, maxPacketsByRead); + this.readablePacketFactory = readablePacketFactory; this.packetLengthHeaderSize = packetLengthHeaderSize; } @@ -40,16 +41,17 @@ protected boolean canStartReadPacket(ByteBuffer buffer) { } @Override - protected int readPacketLength(ByteBuffer buffer) { + protected int readFullPacketLength(ByteBuffer buffer) { return readHeader(buffer, packetLengthHeaderSize); } + @Nullable @Override - protected @Nullable R createPacketFor( + protected R createPacketFor( ByteBuffer buffer, int startPacketPosition, - int packetLength, - int dataLength) { - return readPacketFactory.apply(dataLength); + int packetFullLength, + int packetDataLength) { + return readablePacketFactory.apply(packetDataLength); } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultNetworkPacketWriter.java similarity index 53% rename from rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultPacketWriter.java rename to rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultNetworkPacketWriter.java index ea6a8470..fe6a1e99 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultPacketWriter.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultNetworkPacketWriter.java @@ -2,48 +2,52 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; -import javasabr.rlib.common.function.NotNullBiConsumer; -import javasabr.rlib.common.function.NotNullConsumer; -import javasabr.rlib.common.function.NullableSupplier; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javasabr.rlib.functions.ObjBoolConsumer; import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.WritablePacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; /** * @author JavaSaBr */ -public class DefaultPacketWriter> extends - AbstractPacketWriter { +@FieldDefaults(level = AccessLevel.PROTECTED) +public class DefaultNetworkPacketWriter> + extends AbstractNetworkPacketWriter { - protected final int packetLengthHeaderSize; + final int packetLengthHeaderSize; - public DefaultPacketWriter( + public DefaultNetworkPacketWriter( C connection, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, Runnable updateActivityFunction, - NullableSupplier nextWritePacketSupplier, - NotNullConsumer writtenPacketHandler, - NotNullBiConsumer sentPacketHandler, + Supplier<@Nullable WritableNetworkPacket> packetProvider, + Consumer serializedToChannelPacketHandler, + ObjBoolConsumer sentPacketHandler, int packetLengthHeaderSize) { super( connection, channel, bufferAllocator, updateActivityFunction, - nextWritePacketSupplier, - writtenPacketHandler, + packetProvider, + serializedToChannelPacketHandler, sentPacketHandler); this.packetLengthHeaderSize = packetLengthHeaderSize; } @Override - protected int getTotalSize(WritablePacket packet, int expectedLength) { + protected int totalSize(WritableNetworkPacket packet, int expectedLength) { return expectedLength + packetLengthHeaderSize; } @Override - protected boolean onBeforeWrite( + protected boolean onBeforeSerialize( W packet, int expectedLength, int totalSize, @@ -56,13 +60,14 @@ protected boolean onBeforeWrite( } @Override - protected ByteBuffer onResult( + protected ByteBuffer onSerializeResult( W packet, int expectedLength, int totalSize, ByteBuffer firstBuffer, ByteBuffer secondBuffer) { - return writePacketLength(firstBuffer, firstBuffer.limit()).position(0); + return writePacketLength(firstBuffer, firstBuffer.limit()) + .position(0); } protected ByteBuffer writePacketLength(ByteBuffer buffer, int packetLength) { diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultReadableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultReadableNetworkPacket.java new file mode 100644 index 00000000..71115e89 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultReadableNetworkPacket.java @@ -0,0 +1,9 @@ +package javasabr.rlib.network.packet.impl; + +/** + * @author JavaSaBr + */ +public class DefaultReadableNetworkPacket extends + AbstractIdBasedReadableNetworkPacket { + +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultReadablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultReadablePacket.java deleted file mode 100644 index 2246885c..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultReadablePacket.java +++ /dev/null @@ -1,10 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import javasabr.rlib.network.impl.DefaultConnection; - -/** - * @author JavaSaBr - */ -public class DefaultReadablePacket extends AbstractIdBasedReadablePacket { - -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSSLPacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSslNetworkPacketReader.java similarity index 54% rename from rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSSLPacketReader.java rename to rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSslNetworkPacketReader.java index 814eaf51..0f46f607 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSSLPacketReader.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSslNetworkPacketReader.java @@ -2,35 +2,36 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; +import java.util.function.Consumer; import java.util.function.IntFunction; -import javasabr.rlib.common.function.NotNullConsumer; import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.ReadablePacket; -import javasabr.rlib.network.packet.WritablePacket; +import javasabr.rlib.network.packet.ReadableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; import javax.net.ssl.SSLEngine; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; /** - * @param the readable packet's type. - * @param the connections' type. * @author JavaSaBR */ -public class DefaultSSLPacketReader> extends - AbstractSSLPacketReader { +@FieldDefaults(level = AccessLevel.PROTECTED) +public class DefaultSslNetworkPacketReader> extends + AbstractSslNetworkPacketReader { - private final IntFunction readPacketFactory; - private final int packetLengthHeaderSize; + final IntFunction packetResolver; + final int packetLengthHeaderSize; - public DefaultSSLPacketReader( + public DefaultSslNetworkPacketReader( C connection, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, Runnable updateActivityFunction, - NotNullConsumer readPacketHandler, - IntFunction readPacketFactory, + Consumer packetHandler, + IntFunction packetResolver, SSLEngine sslEngine, - NotNullConsumer packetWriter, + Consumer packetWriter, int packetLengthHeaderSize, int maxPacketsByRead) { super( @@ -38,11 +39,11 @@ public DefaultSSLPacketReader( channel, bufferAllocator, updateActivityFunction, - readPacketHandler, + packetHandler, sslEngine, packetWriter, maxPacketsByRead); - this.readPacketFactory = readPacketFactory; + this.packetResolver = packetResolver; this.packetLengthHeaderSize = packetLengthHeaderSize; } @@ -52,16 +53,17 @@ protected boolean canStartReadPacket(ByteBuffer buffer) { } @Override - protected int readPacketLength(ByteBuffer buffer) { + protected int readFullPacketLength(ByteBuffer buffer) { return readHeader(buffer, packetLengthHeaderSize); } + @Nullable @Override - protected @Nullable R createPacketFor( + protected R createPacketFor( ByteBuffer buffer, int startPacketPosition, - int packetLength, - int dataLength) { - return readPacketFactory.apply(dataLength); + int packetFullLength, + int packetDataLength) { + return packetResolver.apply(packetDataLength); } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSSLPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSslNetworkPacketWriter.java similarity index 58% rename from rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSSLPacketWriter.java rename to rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSslNetworkPacketWriter.java index 588b09ac..52438cbe 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSSLPacketWriter.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultSslNetworkPacketWriter.java @@ -2,33 +2,36 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; -import javasabr.rlib.common.function.NotNullBiConsumer; -import javasabr.rlib.common.function.NotNullConsumer; -import javasabr.rlib.common.function.NullableSupplier; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javasabr.rlib.functions.ObjBoolConsumer; import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.WritablePacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; import javax.net.ssl.SSLEngine; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; /** * @author JavaSaBr */ -public class DefaultSSLPacketWriter> extends - AbstractSSLPacketWriter { +@FieldDefaults(level = AccessLevel.PROTECTED) +public class DefaultSslNetworkPacketWriter> extends + AbstractSslNetworkPacketWriter { - protected final int packetLengthHeaderSize; + final int packetLengthHeaderSize; - public DefaultSSLPacketWriter( + public DefaultSslNetworkPacketWriter( C connection, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, Runnable updateActivityFunction, - NullableSupplier nextWritePacketSupplier, - NotNullConsumer writtenPacketHandler, - NotNullBiConsumer sentPacketHandler, + Supplier<@Nullable WritableNetworkPacket> nextWritePacketSupplier, + Consumer serializedToChannelPacketHandler, + ObjBoolConsumer sentPacketHandler, SSLEngine sslEngine, - NotNullConsumer packetWriter, - NotNullConsumer queueAtFirst, + Consumer queueAtFirst, int packetLengthHeaderSize) { super( connection, @@ -36,21 +39,20 @@ public DefaultSSLPacketWriter( bufferAllocator, updateActivityFunction, nextWritePacketSupplier, - writtenPacketHandler, + serializedToChannelPacketHandler, sentPacketHandler, sslEngine, - packetWriter, queueAtFirst); this.packetLengthHeaderSize = packetLengthHeaderSize; } @Override - protected int getTotalSize(WritablePacket packet, int expectedLength) { + protected int totalSize(WritableNetworkPacket packet, int expectedLength) { return expectedLength + packetLengthHeaderSize; } @Override - protected boolean onBeforeWrite( + protected boolean onBeforeSerialize( W packet, int expectedLength, int totalSize, @@ -63,7 +65,7 @@ protected boolean onBeforeWrite( } @Override - protected ByteBuffer onResult( + protected ByteBuffer onSerializeResult( W packet, int expectedLength, int totalSize, diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultWritableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultWritableNetworkPacket.java new file mode 100644 index 00000000..c134ac95 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultWritableNetworkPacket.java @@ -0,0 +1,9 @@ +package javasabr.rlib.network.packet.impl; + +import javasabr.rlib.network.packet.IdBasedWritableNetworkPacket; + +/** + * @author JavaSaBr + */ +public class DefaultWritableNetworkPacket extends AbstractWritableNetworkPacket implements + IdBasedWritableNetworkPacket {} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultWritablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultWritablePacket.java deleted file mode 100644 index 63753ed3..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/DefaultWritablePacket.java +++ /dev/null @@ -1,8 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import javasabr.rlib.network.packet.IdBasedWritablePacket; - -/** - * @author JavaSaBr - */ -public class DefaultWritablePacket extends AbstractWritablePacket implements IdBasedWritablePacket {} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedNetworkPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedNetworkPacketWriter.java new file mode 100644 index 00000000..7ee8f037 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedNetworkPacketWriter.java @@ -0,0 +1,60 @@ +package javasabr.rlib.network.packet.impl; + +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javasabr.rlib.common.function.NotNullBiConsumer; +import javasabr.rlib.common.function.NotNullConsumer; +import javasabr.rlib.common.function.NullableSupplier; +import javasabr.rlib.functions.ObjBoolConsumer; +import javasabr.rlib.network.BufferAllocator; +import javasabr.rlib.network.Connection; +import javasabr.rlib.network.packet.IdBasedWritableNetworkPacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +/** + * @author JavaSaBr + */ +@FieldDefaults(level = AccessLevel.PROTECTED) +public class IdBasedNetworkPacketWriter> + extends DefaultNetworkPacketWriter { + + final int packetIdHeaderSize; + + public IdBasedNetworkPacketWriter( + C connection, + AsynchronousSocketChannel channel, + BufferAllocator bufferAllocator, + Runnable updateActivityFunction, + Supplier<@Nullable WritableNetworkPacket> packetProvider, + Consumer serializedToChannelPacketHandler, + ObjBoolConsumer sentPacketHandler, + int packetLengthHeaderSize, + int packetIdHeaderSize) { + super( + connection, + channel, + bufferAllocator, + updateActivityFunction, + packetProvider, + serializedToChannelPacketHandler, + sentPacketHandler, + packetLengthHeaderSize); + this.packetIdHeaderSize = packetIdHeaderSize; + } + + @Override + protected boolean doSerialize( + W packet, + int expectedLength, + int totalSize, + ByteBuffer firstBuffer, + ByteBuffer secondBuffer) { + writeHeader(firstBuffer, packet.packetId(), packetIdHeaderSize); + return super.doSerialize(packet, expectedLength, totalSize, firstBuffer, secondBuffer); + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedPacketReader.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedPacketReader.java index e4585319..061b1f04 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedPacketReader.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedPacketReader.java @@ -2,36 +2,37 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; -import javasabr.rlib.common.function.NotNullConsumer; +import java.util.function.Consumer; import javasabr.rlib.network.BufferAllocator; import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.IdBasedReadablePacket; -import javasabr.rlib.network.packet.registry.ReadablePacketRegistry; +import javasabr.rlib.network.packet.IdBasedReadableNetworkPacket; +import javasabr.rlib.network.packet.registry.ReadableNetworkPacketRegistry; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; /** - * @param the readable packet's type. - * @param the connection's type. * @author JavaSaBr */ -public class IdBasedPacketReader, C extends Connection> extends - AbstractPacketReader { +@FieldDefaults(level = AccessLevel.PROTECTED) +public class IdBasedPacketReader, C extends Connection> extends + AbstractNetworkPacketReader { - private final ReadablePacketRegistry packetRegistry; - private final int packetLengthHeaderSize; - private final int packetIdHeaderSize; + final ReadableNetworkPacketRegistry packetRegistry; + final int packetLengthHeaderSize; + final int packetIdHeaderSize; public IdBasedPacketReader( C connection, AsynchronousSocketChannel channel, BufferAllocator bufferAllocator, Runnable updateActivityFunction, - NotNullConsumer readPacketHandler, + Consumer packetHandler, int packetLengthHeaderSize, int maxPacketsByRead, int packetIdHeaderSize, - ReadablePacketRegistry packetRegistry) { - super(connection, channel, bufferAllocator, updateActivityFunction, readPacketHandler, maxPacketsByRead); + ReadableNetworkPacketRegistry packetRegistry) { + super(connection, channel, bufferAllocator, updateActivityFunction, packetHandler, maxPacketsByRead); this.packetLengthHeaderSize = packetLengthHeaderSize; this.packetIdHeaderSize = packetIdHeaderSize; this.packetRegistry = packetRegistry; @@ -43,18 +44,19 @@ protected boolean canStartReadPacket(ByteBuffer buffer) { } @Override - protected int readPacketLength(ByteBuffer buffer) { + protected int readFullPacketLength(ByteBuffer buffer) { return readHeader(buffer, packetLengthHeaderSize); } + @Nullable @Override - protected @Nullable R createPacketFor( + protected R createPacketFor( ByteBuffer buffer, int startPacketPosition, - int packetLength, - int dataLength) { + int packetFullLength, + int packetDataLength) { return packetRegistry - .findById(readHeader(buffer, packetIdHeaderSize)) + .resolvePrototypeById(readHeader(buffer, packetIdHeaderSize)) .newInstance(); } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedPacketWriter.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedPacketWriter.java deleted file mode 100644 index 62ec9c39..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/IdBasedPacketWriter.java +++ /dev/null @@ -1,53 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousSocketChannel; -import javasabr.rlib.common.function.NotNullBiConsumer; -import javasabr.rlib.common.function.NotNullConsumer; -import javasabr.rlib.common.function.NullableSupplier; -import javasabr.rlib.network.BufferAllocator; -import javasabr.rlib.network.Connection; -import javasabr.rlib.network.packet.IdBasedWritablePacket; -import javasabr.rlib.network.packet.WritablePacket; - -/** - * @author JavaSaBr - */ -public class IdBasedPacketWriter> extends - DefaultPacketWriter { - - protected final int packetIdHeaderSize; - - public IdBasedPacketWriter( - C connection, - AsynchronousSocketChannel channel, - BufferAllocator bufferAllocator, - Runnable updateActivityFunction, - NullableSupplier nextWritePacketSupplier, - NotNullConsumer writtenPacketHandler, - NotNullBiConsumer sentPacketHandler, - int packetLengthHeaderSize, - int packetIdHeaderSize) { - super( - connection, - channel, - bufferAllocator, - updateActivityFunction, - nextWritePacketSupplier, - writtenPacketHandler, - sentPacketHandler, - packetLengthHeaderSize); - this.packetIdHeaderSize = packetIdHeaderSize; - } - - @Override - protected boolean onWrite( - W packet, - int expectedLength, - int totalSize, - ByteBuffer firstBuffer, - ByteBuffer secondBuffer) { - writeHeader(firstBuffer, packet.getPacketId(), packetIdHeaderSize); - return super.onWrite(packet, expectedLength, totalSize, firstBuffer, secondBuffer); - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/SSLWritablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/SSLWritablePacket.java deleted file mode 100644 index f989d60c..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/SSLWritablePacket.java +++ /dev/null @@ -1,20 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import java.nio.ByteBuffer; - -/** - * Packet marker. - */ -public class SSLWritablePacket extends AbstractWritablePacket { - - private static final SSLWritablePacket INSTANCE = new SSLWritablePacket(); - - public static SSLWritablePacket getInstance() { - return INSTANCE; - } - - @Override - public boolean write(ByteBuffer buffer) { - return true; - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/SslWrapRequestPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/SslWrapRequestPacket.java new file mode 100644 index 00000000..4d12d963 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/SslWrapRequestPacket.java @@ -0,0 +1,19 @@ +package javasabr.rlib.network.packet.impl; + +import java.nio.ByteBuffer; +import javasabr.rlib.network.packet.MarkerNetworkPacket; + +public class SslWrapRequestPacket extends AbstractWritableNetworkPacket + implements MarkerNetworkPacket { + + private static final SslWrapRequestPacket INSTANCE = new SslWrapRequestPacket(); + + public static SslWrapRequestPacket getInstance() { + return INSTANCE; + } + + @Override + public boolean write(ByteBuffer buffer) { + return true; + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringReadablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringReadablePacket.java index 6f8911d1..a896ef20 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringReadablePacket.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringReadablePacket.java @@ -1,35 +1,36 @@ package javasabr.rlib.network.packet.impl; import java.nio.ByteBuffer; -import javasabr.rlib.network.Connection; +import lombok.AccessLevel; import lombok.Getter; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; /** * @author JavaSaBr */ @Getter -public class StringReadablePacket extends AbstractReadablePacket> { +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +public class StringReadablePacket extends AbstractReadableNetworkPacket { - /** - * Read data. - */ - private volatile @Nullable String data; + public static final int MAX_LENGTH = 100_000; + + @Nullable + volatile String data; @Override - protected void readImpl(Connection connection, ByteBuffer buffer) { - this.data = readString(buffer); + protected void readImpl(ByteBuffer buffer) { + this.data = readString(buffer, MAX_LENGTH); } @Override public String toString() { - - var data = getData(); - + String data = data(); if (data != null && data.length() > 20) { data = data.substring(0, 9) + "..." + data.substring(9, 19); } - return "StringReadablePacket(" + "data='" + data + '\'' + ')'; } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringWritableNetworkPacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringWritableNetworkPacket.java new file mode 100644 index 00000000..0c7a4542 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringWritableNetworkPacket.java @@ -0,0 +1,36 @@ +package javasabr.rlib.network.packet.impl; + +import java.nio.ByteBuffer; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; + +/** + * @author JavaSaBr + */ +@Getter +@RequiredArgsConstructor +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class StringWritableNetworkPacket extends AbstractWritableNetworkPacket { + + String data; + + @Override + protected void writeImpl(ByteBuffer buffer) { + super.writeImpl(buffer); + writeString(buffer, data); + } + + @Override + public int expectedLength() { + return 4 + data.length() * 2; + } + + @Override + public String toString() { + return "StringWritablePacket[length:" + data.length() + "]"; + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringWritablePacket.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringWritablePacket.java deleted file mode 100644 index 20d9eea3..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/StringWritablePacket.java +++ /dev/null @@ -1,29 +0,0 @@ -package javasabr.rlib.network.packet.impl; - -import java.nio.ByteBuffer; -import lombok.RequiredArgsConstructor; - -/** - * @author JavaSaBr - */ -@RequiredArgsConstructor -public class StringWritablePacket extends AbstractWritablePacket { - - private final String data; - - @Override - protected void writeImpl(ByteBuffer buffer) { - super.writeImpl(buffer); - writeString(buffer, data); - } - - @Override - public int getExpectedLength() { - return 4 + data.length() * 2; - } - - @Override - public String toString() { - return "StringWritablePacket {\n" + "\"\tdataLength\":" + data.length() + "\n}"; - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/WritablePacketWrapper.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/WritablePacketWrapper.java index 2aa320fa..668e1ba1 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/WritablePacketWrapper.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/impl/WritablePacketWrapper.java @@ -1,7 +1,7 @@ package javasabr.rlib.network.packet.impl; import java.nio.ByteBuffer; -import javasabr.rlib.network.packet.WritablePacket; +import javasabr.rlib.network.packet.WritableNetworkPacket; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -13,7 +13,8 @@ */ @Getter @RequiredArgsConstructor -public class WritablePacketWrapper implements WritablePacket { +public class WritablePacketWrapper + implements WritableNetworkPacket { private final A attachment; private final W packet; @@ -24,12 +25,12 @@ public boolean write(ByteBuffer buffer) { } @Override - public int getExpectedLength() { - return packet.getExpectedLength(); + public int expectedLength() { + return packet.expectedLength(); } @Override - public String getName() { + public String name() { return "WritablePacketWrapper"; } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadableNetworkPacketRegistry.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadableNetworkPacketRegistry.java new file mode 100644 index 00000000..3a0cf08a --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadableNetworkPacketRegistry.java @@ -0,0 +1,99 @@ +package javasabr.rlib.network.packet.registry; + +import javasabr.rlib.classpath.ClassPathScanner; +import javasabr.rlib.classpath.ClassPathScannerFactory; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayCollectors; +import javasabr.rlib.network.annotation.NetworkPacketDescription; +import javasabr.rlib.network.packet.IdBasedReadableNetworkPacket; +import javasabr.rlib.network.packet.registry.impl.IdBasedReadableNetworkPacketRegistry; + +/** + * The interface to implement a registry of readable packets. + * + * @author JavaSaBr + */ +public interface ReadableNetworkPacketRegistry> { + + /** + * Creates a new empty readable packet registry. + */ + static ReadableNetworkPacketRegistry empty() { + return new IdBasedReadableNetworkPacketRegistry<>(IdBasedReadableNetworkPacket.class); + } + + /** + * Create a new empty readable packet registry. + */ + static > ReadableNetworkPacketRegistry empty(Class type) { + return new IdBasedReadableNetworkPacketRegistry<>(type); + } + + /** + * Creates a new class path scanning based readable packet registry. + */ + static ReadableNetworkPacketRegistry classPathBased() { + + var scanner = ClassPathScannerFactory.newDefaultScanner(); + scanner.useSystemClassPath(true); + scanner.scan(); + + return of(scanner); + } + + /** + * Creates a new class path scanning based readable packet registry by scanning the main class. + */ + static ReadableNetworkPacketRegistry classPathBased(Class mainClass) { + + var scanner = ClassPathScannerFactory.newManifestScanner(mainClass); + scanner.useSystemClassPath(false); + scanner.scan(); + + return of(scanner); + } + + /** + * Creates a new class path scanning based readable packet registry. + */ + static ReadableNetworkPacketRegistry of(ClassPathScanner scanner) { + + var result = scanner + .findImplementations(IdBasedReadableNetworkPacket.class) + .stream() + .filter(type -> type.getAnnotation(NetworkPacketDescription.class) != null) + .collect(ArrayCollectors.>toArray(Class.class)); + return new IdBasedReadableNetworkPacketRegistry<>(IdBasedReadableNetworkPacket.class) + .register(result); + } + + /** + * Creates a new readable packet registry. + */ + @SafeVarargs + static > ReadableNetworkPacketRegistry of( + Class type, + Class... classes) { + return new IdBasedReadableNetworkPacketRegistry<>(type) + .register(classes, classes.length); + } + + /** + * Creates a new readable packet registry. + */ + static > ReadableNetworkPacketRegistry of( + Class type, + Array> classes) { + return new IdBasedReadableNetworkPacketRegistry<>(type) + .register(classes); + } + + /** + * Resolve a network packet prototype based on packet id. + * + * @param id the network packet id. + * @return the resolve prototype. + * @throws IllegalArgumentException if can't resolve a prototype by the id. + */ + R resolvePrototypeById(int id); +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadablePacketRegistry.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadablePacketRegistry.java deleted file mode 100644 index 89595677..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/ReadablePacketRegistry.java +++ /dev/null @@ -1,130 +0,0 @@ -package javasabr.rlib.network.packet.registry; - -import javasabr.rlib.classpath.ClassPathScanner; -import javasabr.rlib.classpath.ClassPathScannerFactory; -import javasabr.rlib.collections.array.Array; -import javasabr.rlib.collections.array.ArrayCollectors; -import javasabr.rlib.network.annotation.PacketDescription; -import javasabr.rlib.network.packet.IdBasedReadablePacket; -import javasabr.rlib.network.packet.registry.impl.IdBasedReadablePacketRegistry; - -/** - * The interface to implement a registry of readable packets. - * - * @author JavaSaBr - */ -public interface ReadablePacketRegistry> { - - /** - * Create a new empty readable packet registry. - * - * @return the new packet registry. - */ - static ReadablePacketRegistry empty() { - return new IdBasedReadablePacketRegistry<>(IdBasedReadablePacket.class); - } - - /** - * Create a new empty readable packet registry. - * - * @param type the packet's type. - * @param the packet's type. - * @return the new packet registry. - */ - static > ReadablePacketRegistry empty(Class type) { - return new IdBasedReadablePacketRegistry<>(type); - } - - /** - * Create a new default readable packet registry. - * - * @return the new packet registry. - */ - static ReadablePacketRegistry newDefault() { - - var scanner = ClassPathScannerFactory.newDefaultScanner(); - scanner.useSystemClassPath(true); - scanner.scan(); - - return of(scanner); - } - - /** - * Create a new default readable packet registry by the result of scanning classpath of the main class. - * - * @param mainClass the main class of application. - * @return the new packet registry. - */ - static ReadablePacketRegistry newDefault(Class mainClass) { - - var scanner = ClassPathScannerFactory.newManifestScanner(mainClass); - scanner.useSystemClassPath(false); - scanner.scan(); - - return of(scanner); - } - - /** - * Creates a new default readable packet registry by the classpath scanner. - * - * @param scanner the classpath scanner. - * @return the new packet registry. - */ - static ReadablePacketRegistry of(ClassPathScanner scanner) { - - var result = scanner - .findImplementations(IdBasedReadablePacket.class) - .stream() - .filter(type -> type.getAnnotation(PacketDescription.class) != null) - .collect(ArrayCollectors.>toArray(Class.class)); - - var registry = new IdBasedReadablePacketRegistry<>(IdBasedReadablePacket.class); - registry.register(result); - - return registry; - } - - /** - * Create a new readable packet registry by array of classes. - * - * @param type the base packet's type. - * @param classes the classes array. - * @param the packet's type. - * @return the new packet registry. - */ - @SafeVarargs - static > ReadablePacketRegistry of( - Class type, - Class... classes) { - var registry = new IdBasedReadablePacketRegistry<>(type); - registry.register(classes, classes.length); - return registry; - } - - /** - * Create a new readable packet registry by array of classes. - * - * @param type the base packet's type. - * @param classes the classes array. - * @param the packet's type. - * @return the new packet registry. - */ - static > ReadablePacketRegistry of( - Class type, - Array> classes) { - - var registry = new IdBasedReadablePacketRegistry<>(type); - registry.register(classes); - - return registry; - } - - /** - * Find a packet by the id. - * - * @param id the packet id. - * @return the packet. - * @throws IllegalArgumentException if can't find a packet by the id. - */ - R findById(int id); -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadableNetworkPacketRegistry.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadableNetworkPacketRegistry.java new file mode 100644 index 00000000..815911c2 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadableNetworkPacketRegistry.java @@ -0,0 +1,168 @@ +package javasabr.rlib.network.packet.registry.impl; + +import java.util.Arrays; +import java.util.Optional; +import java.util.function.Supplier; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.common.util.ArrayUtils; +import javasabr.rlib.common.util.ClassUtils; +import javasabr.rlib.common.util.ObjectUtils; +import javasabr.rlib.network.annotation.NetworkPacketDescription; +import javasabr.rlib.network.packet.IdBasedReadableNetworkPacket; +import javasabr.rlib.network.packet.registry.ReadableNetworkPacketRegistry; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; + +/** + * Id based implementation of readable network packet's registry. + * + * @author JavaSaBr + */ +@CustomLog +@Accessors(fluent = true, chain = false) +@FieldDefaults(level = AccessLevel.PROTECTED) +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +public class IdBasedReadableNetworkPacketRegistry> + implements ReadableNetworkPacketRegistry { + + @Getter(AccessLevel.PROTECTED) + final Class type; + + @Getter(AccessLevel.PROTECTED) + final @Nullable R[] idToPrototype; + + public IdBasedReadableNetworkPacketRegistry(Class type) { + this.idToPrototype = ArrayUtils.create(type, 0); + this.type = type; + } + + /** + * Registers classes of readable network packets. + * + * @throws IllegalArgumentException if found a class without description annotation or if found duplication by id. + */ + public IdBasedReadableNetworkPacketRegistry register(Array> classes) { + return register(classes.toArray(), classes.size()); + } + + /** + * Registers classes of readable network packets. + * + * @throws IllegalArgumentException if found a class without description annotation or if found duplication by id. + * id. + */ + @SafeVarargs + public final IdBasedReadableNetworkPacketRegistry register(Class... classes) { + return register(classes, classes.length); + } + + /** + * Registers classes of readable network packets. + * + * @throws IllegalArgumentException if found a class without description annotation or if found duplication by id. + */ + public IdBasedReadableNetworkPacketRegistry register(Class[] classes, int length) { + + Optional> incorrectClass = Arrays + .stream(classes, 0, length) + .filter(type -> type.getAnnotation(NetworkPacketDescription.class) == null) + .findFirst(); + + if (incorrectClass.isPresent()) { + throw new IllegalArgumentException("Have found class:[%s] without description annotation".formatted(incorrectClass.get())); + } + + int maxId = Arrays + .stream(classes, 0, length) + .map(type -> type.getAnnotation(NetworkPacketDescription.class)) + .mapToInt(NetworkPacketDescription::id) + .max() + .orElseThrow(() -> new IllegalStateException("Not found any packet id")); + + @Nullable R[] idToPrototype = ArrayUtils.create(type, maxId + 1); + + for (int i = 0; i < length; i++) { + Class cs = classes[i]; + if (!type.isAssignableFrom(cs)) { + log.warning(cs, type, "Found incompatibility packet's type:[%s] with type:[%s]"::formatted); + continue; + } + + var description = cs.getAnnotation(NetworkPacketDescription.class); + var id = description.id(); + + R exist = idToPrototype[id]; + if (exist != null) { + throw new IllegalArgumentException("Found duplication by id:[%d], existed packet is:[%s], new packet is:[%s]" + .formatted(id, exist.getClass(), cs)); + } + + idToPrototype[id] = ClassUtils.newInstance(cs); + } + + return new IdBasedReadableNetworkPacketRegistry<>(type, idToPrototype); + } + + /** + * Registers a class of readable network packet. + * + * @throws IllegalArgumentException if this class doesn't have {@link NetworkPacketDescription}, wrong id or some class is + * already presented with the same id. + */ + public IdBasedReadableNetworkPacketRegistry register(Class cs) { + return register(cs, () -> ClassUtils.newInstance(cs)); + } + + /** + * Registers a class of readable packet. + * + * @return the reference to this registry. + * @throws IllegalArgumentException if this class doesn't have {@link NetworkPacketDescription}, wrong id or some class is + * already presented with the same id. + */ + public

IdBasedReadableNetworkPacketRegistry register(Class

cs, Supplier

factory) { + + var description = cs.getAnnotation(NetworkPacketDescription.class); + if (description == null) { + throw new IllegalArgumentException("Class:[%s] doesn't have description annotation.".formatted(cs)); + } + + var id = description.id(); + if (id < 0) { + throw new IllegalArgumentException("Class:[%s] has unexpected packet id:[%d]".formatted(cs, id)); + } + + @Nullable R[] idToPrototype = idToPrototype(); + + if (id < idToPrototype.length) { + R exist = idToPrototype[id]; + if (exist != null) { + throw new IllegalArgumentException("Class:[%s] is already has the same id:[%d]" + .formatted(exist.getClass(), id)); + } else { + idToPrototype = ArrayUtils.copyOf(idToPrototype); + idToPrototype[id] = ClassUtils.newInstance(cs); + return new IdBasedReadableNetworkPacketRegistry<>(type, idToPrototype); + } + } + + idToPrototype = Arrays.copyOf(idToPrototype, id + 1); + idToPrototype[id] = factory.get(); + + return new IdBasedReadableNetworkPacketRegistry<>(type, idToPrototype); + } + + @Override + public R resolvePrototypeById(int id) { + try { + return ObjectUtils.notNull(idToPrototype[id], id, integer -> new IllegalArgumentException("Not found a packet for the id " + integer)); + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException("Not found prototype for the id " + id); + } + } +} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadablePacketRegistry.java b/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadablePacketRegistry.java deleted file mode 100644 index 1ed08492..00000000 --- a/rlib-network/src/main/java/javasabr/rlib/network/packet/registry/impl/IdBasedReadablePacketRegistry.java +++ /dev/null @@ -1,201 +0,0 @@ -package javasabr.rlib.network.packet.registry.impl; - -import java.util.Arrays; -import java.util.function.Supplier; -import javasabr.rlib.collections.array.Array; -import javasabr.rlib.common.util.ArrayUtils; -import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.annotation.PacketDescription; -import javasabr.rlib.network.packet.IdBasedReadablePacket; -import javasabr.rlib.network.packet.registry.ReadablePacketRegistry; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; - -/** - * The id based implementation of readable packets registry. - * - * @author JavaSaBr - */ -public class IdBasedReadablePacketRegistry> implements ReadablePacketRegistry { - - private static final Logger LOGGER = LoggerManager.getLogger(IdBasedReadablePacketRegistry.class); - - /** - * The packet's type in this registry. - */ - private final Class type; - - /** - * The array of packet id to packet instance. - */ - @Getter(AccessLevel.PRIVATE) - @Setter(AccessLevel.PRIVATE) - private volatile R[] idToPacket; - - public IdBasedReadablePacketRegistry(Class type) { - this.idToPacket = ArrayUtils.create(type, 0); - this.type = type; - } - - /** - * Register classes of readable packets. - * - * @param classes the classes array. - * @return the reference to this registry. - * @throws IllegalArgumentException if found a class without packet description annotation or if found duplication by - * id. - */ - public IdBasedReadablePacketRegistry register(Array> classes) { - return register(classes.toArray(), classes.size()); - } - - /** - * Register classes of readable packets. - * - * @param classes the classes array. - * @return the reference to this registry. - * @throws IllegalArgumentException if found a class without packet description annotation or if found duplication by - * id. - */ - @SafeVarargs - public final IdBasedReadablePacketRegistry register(Class... classes) { - return register(classes, classes.length); - } - - /** - * Register classes of readable packets. - * - * @param classes the classes array. - * @param length the length of the classes. - * @return the reference to this registry. - * @throws IllegalArgumentException if found a class without packet description annotation or if found duplication by - * id. - */ - public IdBasedReadablePacketRegistry register(Class[] classes, int length) { - - var incorrectClass = Arrays - .stream(classes, 0, length) - .filter(type -> type.getAnnotation(PacketDescription.class) == null) - .findFirst(); - - if (incorrectClass.isPresent()) { - throw new IllegalArgumentException( - "Have found a class " + incorrectClass.get() + " without the packet description annotation."); - } - - var maxId = Arrays - .stream(classes, 0, length) - .map(type -> type.getAnnotation(PacketDescription.class)) - .mapToInt(PacketDescription::id) - .max() - .orElseThrow(() -> new IllegalStateException("Not found any packet id")); - - setIdToPacket(Arrays.copyOf(getIdToPacket(), maxId + 1)); - - var idToPacket = getIdToPacket(); - - for (int i = 0; i < length; i++) { - - var cs = classes[i]; - - if (!type.isAssignableFrom(cs)) { - LOGGER.warning( - cs, - type, - (detected, check) -> "Found incompatibility packet's type: " + detected + " with type: " + check); - continue; - } - - var description = cs.getAnnotation(PacketDescription.class); - var id = description.id(); - - if (idToPacket[id] != null) { - throw new IllegalArgumentException( - "Have found duplication by id " + id + ", existed packet is " + idToPacket[id].getClass() - + ", new packet is " + cs); - } - - idToPacket[id] = ClassUtils.newInstance(cs); - } - - return this; - } - - /** - * Register a class of readable packet. - * - * @param cs the class. - * @return the reference to this registry. - * @throws IllegalArgumentException if this class doesn't have {@link PacketDescription}, wrong id or some class is - * already presented with the same id. - */ - public IdBasedReadablePacketRegistry register(Class cs) { - return register(cs, () -> ClassUtils.newInstance(cs)); - } - - /** - * Register a class of readable packet. - * - * @param cs the class. - * @param factory the instance factory. - * @param

the packet's type. - * @return the reference to this registry. - * @throws IllegalArgumentException if this class doesn't have {@link PacketDescription}, wrong id or some class is - * already presented with the same id. - */ - public

IdBasedReadablePacketRegistry register( - Class

cs, - Supplier

factory) { - - var description = cs.getAnnotation(PacketDescription.class); - - if (description == null) { - throw new IllegalArgumentException("Class " + cs + " doesn't have packet description annotation."); - } - - var id = description.id(); - - if (id < 0) { - throw new IllegalArgumentException("Class " + cs + " has wrong packet id: " + id); - } - - var idToPacket = getIdToPacket(); - - if (id < idToPacket.length) { - if (idToPacket[id] != null) { - throw new IllegalArgumentException("Class " + idToPacket[id].getClass() + " is already has the same id: " + id); - } else { - idToPacket[id] = ClassUtils.newInstance(cs); - return this; - } - } - - idToPacket = Arrays.copyOf(getIdToPacket(), id + 1); - idToPacket[id] = factory.get(); - - setIdToPacket(idToPacket); - - return this; - } - - @Override - public R findById(int id) { - - R[] idToPacket = getIdToPacket(); - - if (id < 0 || id >= idToPacket.length) { - throw new IllegalArgumentException("Not found a packet for the id " + id); - } - - var packet = idToPacket[id]; - - if (packet == null) { - throw new IllegalArgumentException("Not found a packet for the id " + id); - } - - return packet; - } -} diff --git a/rlib-network/src/main/java/javasabr/rlib/network/server/ServerNetwork.java b/rlib-network/src/main/java/javasabr/rlib/network/server/ServerNetwork.java index 877cd9ac..1fa6aa30 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/server/ServerNetwork.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/server/ServerNetwork.java @@ -15,31 +15,21 @@ public interface ServerNetwork> extends Network { /** * Start a server using any available address. - * - * @return this server's address. */ InetSocketAddress start(); /** * Start a server by the address. - * - * @param serverAddress the sever address. - * @param the server network's type. - * @return this network. */ > S start(InetSocketAddress serverAddress); /** * Register a consumer of new connections. - * - * @param consumer the consumer of new connections. */ void onAccept(Consumer consumer); /** * Get a stream of new accepted connections. - * - * @return the stream of new accepted connections. */ Flux accepted(); } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java b/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java index 579a04ea..46e6e2a2 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/server/impl/DefaultServerNetwork.java @@ -1,7 +1,5 @@ package javasabr.rlib.network.server.impl; -import static javasabr.rlib.common.util.Utils.uncheckedGet; - import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.AcceptPendingException; @@ -11,6 +9,7 @@ import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -22,14 +21,15 @@ import javasabr.rlib.common.util.ClassUtils; import javasabr.rlib.common.util.GroupThreadFactory; import javasabr.rlib.common.util.Utils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.Network; import javasabr.rlib.network.ServerNetworkConfig; import javasabr.rlib.network.UnsafeConnection; import javasabr.rlib.network.impl.AbstractNetwork; import javasabr.rlib.network.server.ServerNetwork; import javasabr.rlib.network.util.NetworkUtils; +import lombok.AccessLevel; +import lombok.CustomLog; +import lombok.experimental.FieldDefaults; import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; @@ -38,10 +38,10 @@ * * @author JavaSaBr */ -public final class DefaultServerNetwork> extends AbstractNetwork implements - ServerNetwork { - - protected static final Logger LOGGER = LoggerManager.getLogger(DefaultServerNetwork.class); +@CustomLog +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +public class DefaultServerNetwork> + extends AbstractNetwork implements ServerNetwork { private interface ServerCompletionHandler> extends CompletionHandler> {} @@ -50,8 +50,8 @@ private interface ServerCompletionHandler> exte @Override public void completed(AsynchronousSocketChannel channel, DefaultServerNetwork network) { - var connection = network.channelToConnection.apply(DefaultServerNetwork.this, channel); - LOGGER.debug(connection, conn -> "Accepted new connection: " + conn.getRemoteAddress()); + var connection = network.channelToConnection.apply(network, channel); + log.debug(connection.remoteAddress(), "Accepted new connection:[%s]"::formatted); network.onAccept(connection); network.acceptNext(); } @@ -59,11 +59,10 @@ public void completed(AsynchronousSocketChannel channel, DefaultServerNetwork @Override public void failed(Throwable exc, DefaultServerNetwork network) { if (exc instanceof AsynchronousCloseException) { - LOGGER.warning("Server network was closed"); + log.warning("Server network was closed"); } else { - LOGGER.error("Got exception during accepting new connection:"); - LOGGER.error(exc); - + log.error("Got exception during accepting new connection:"); + log.error(exc); if (channel.isOpen()) { network.acceptNext(); } @@ -71,56 +70,25 @@ public void failed(Throwable exc, DefaultServerNetwork network) { } }; - protected final AsynchronousChannelGroup group; - protected final AsynchronousServerSocketChannel channel; - protected final MutableArray> subscribers; + AsynchronousChannelGroup group; + AsynchronousServerSocketChannel channel; + MutableArray> subscribers; public DefaultServerNetwork( ServerNetworkConfig config, BiFunction, AsynchronousSocketChannel, C> channelToConnection) { - super(config, channelToConnection); - - var threadFactory = new GroupThreadFactory( - config.getThreadGroupName(), - config.getThreadConstructor(), - config.getThreadPriority(), - false); - - var executor = config.getThreadGroupMinSize() < config.getThreadGroupMaxSize() - ? new ThreadPoolExecutor( - config.getThreadGroupMinSize(), - config.getThreadGroupMaxSize(), - 120, - TimeUnit.SECONDS, - new SynchronousQueue<>(), - threadFactory, - new ThreadPoolExecutor.CallerRunsPolicy()) - : Executors.newFixedThreadPool(config.getThreadGroupMinSize(), threadFactory); - - // activate the executor - executor.submit(() -> {}); - - LOGGER.info( - config, - conf -> "Server network configuration: {\n" + " minThreads: " + conf.getThreadGroupMinSize() + ",\n" - + " maxThreads: " + conf.getThreadGroupMaxSize() + ",\n" + " priority: " + conf.getThreadPriority() - + ",\n" + " groupName: \"" + conf.getThreadGroupName() + "\",\n" + " readBufferSize: " - + conf.getReadBufferSize() + ",\n" + " pendingBufferSize: " + conf.getPendingBufferSize() + ",\n" - + " writeBufferSize: " + conf.getWriteBufferSize() + "\n" + "}"); - - this.group = uncheckedGet(executor, AsynchronousChannelGroup::withThreadPool); - this.channel = uncheckedGet(group, AsynchronousServerSocketChannel::open); + this.group = Utils.uncheckedGet(buildExecutor(config), AsynchronousChannelGroup::withThreadPool); + this.channel = Utils.uncheckedGet(group, AsynchronousServerSocketChannel::open); this.subscribers = ArrayFactory.copyOnModifyArray(Consumer.class); + log.info(config, DefaultServerNetwork::buildConfigDescription); } @Override public InetSocketAddress start() { InetSocketAddress address = null; - while (address == null) { - address = new InetSocketAddress(NetworkUtils.getAvailablePort(1500)); try { channel.bind(address); @@ -129,7 +97,7 @@ public InetSocketAddress start() { } } - LOGGER.info(address, adr -> "Started server socket on address: " + adr); + log.info(address, "Started server socket on address:[%s]"::formatted); if (!subscribers.isEmpty()) { acceptNext(); @@ -141,13 +109,10 @@ public InetSocketAddress start() { @Override public > S start(InetSocketAddress serverAddress) { Utils.unchecked(channel, serverAddress, AsynchronousServerSocketChannel::bind); - - LOGGER.info(serverAddress, addr -> "Started server socket on address: " + addr); - + log.info(serverAddress, addr -> "Started server socket on address: " + addr); if (!subscribers.isEmpty()) { acceptNext(); } - return ClassUtils.unsafeNNCast(this); } @@ -155,10 +120,9 @@ protected void acceptNext() { if (channel.isOpen()) { try { channel.accept(this, acceptHandler); - } catch (AcceptPendingException ignored) { - } + } catch (AcceptPendingException ignored) {} } else { - LOGGER.warning("Cannot accept a next connection because server channel is already closed"); + log.error("Cannot accept next connection because server channel is already closed"); } } @@ -191,4 +155,39 @@ public void shutdown() { Utils.unchecked(channel, AsynchronousChannel::close); group.shutdown(); } + + protected ExecutorService buildExecutor(ServerNetworkConfig config) { + + var threadFactory = new GroupThreadFactory( + config.threadGroupName(), + config.threadConstructor(), + config.threadPriority(), + false); + + ExecutorService executorService; + if (config.threadGroupMinSize() < config.threadGroupMaxSize()) { + executorService = new ThreadPoolExecutor( + config.threadGroupMinSize(), + config.threadGroupMaxSize(), + 120, + TimeUnit.SECONDS, + new SynchronousQueue<>(), + threadFactory, + new ThreadPoolExecutor.CallerRunsPolicy()); + } else { + executorService = Executors.newFixedThreadPool(config.threadGroupMinSize(), threadFactory); + } + + // activate the executor + executorService.submit(() -> {}); + return executorService; + } + + private static String buildConfigDescription(ServerNetworkConfig conf) { + return "Server network configuration: {\n" + " minThreads: " + conf.threadGroupMinSize() + ",\n" + " maxThreads: " + + conf.threadGroupMaxSize() + ",\n" + " priority: " + conf.threadPriority() + ",\n" + " groupName: \"" + + conf.threadGroupName() + "\",\n" + " readBufferSize: " + conf.readBufferSize() + ",\n" + + " pendingBufferSize: " + conf.pendingBufferSize() + ",\n" + " writeBufferSize: " + conf.writeBufferSize() + + "\n" + "}"; + } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/util/NetworkUtils.java b/rlib-network/src/main/java/javasabr/rlib/network/util/NetworkUtils.java index de891beb..71e21c6c 100644 --- a/rlib-network/src/main/java/javasabr/rlib/network/util/NetworkUtils.java +++ b/rlib-network/src/main/java/javasabr/rlib/network/util/NetworkUtils.java @@ -15,7 +15,6 @@ import javasabr.rlib.network.BufferAllocator; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; @@ -37,11 +36,9 @@ public X509Certificate[] getAcceptedIssuers() { return EMPTY_CERTS; } - public void checkClientTrusted(X509Certificate[] certificates, String arg1) { - } + public void checkClientTrusted(X509Certificate[] certificates, String arg1) {} - public void checkServerTrusted(X509Certificate[] certificates, String arg1) { - } + public void checkServerTrusted(X509Certificate[] certificates, String arg1) {} } public static SocketAddress getRemoteAddress(AsynchronousSocketChannel socketChannel) { @@ -96,14 +93,10 @@ public static SSLContext createSslContext( } public static SSLContext createAllTrustedClientSslContext() { - try { - var sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(null, new TrustManager[]{new AllTrustManager()}, new SecureRandom()); - return sslContext; - } catch (Exception e) { throw new RuntimeException(e); } @@ -172,14 +165,12 @@ public static String hexDump(byte[] array, int offset, int length) { hexDigit(builder, (byte) ((offset >>> 8) & 0xFF)); hexDigit(builder, (byte) (offset & 0xFF)); - builder.append(": "); + builder.append(": "); for (int i = offset; i < length; i++) { - byte val = array[i]; char ch; - if (val < ' ' || val > 'z') { ch = '.'; } else { @@ -187,45 +178,31 @@ public static String hexDump(byte[] array, int offset, int length) { } if (i == end) { - chars[count] = ch; - hexDigit(builder, val) .append(" ".repeat(15 - count)) .append(" "); - if (count < 9) { builder.append(" "); } - builder.append(chars); - } else if (count < 15) { - chars[count++] = ch; hexDigit(builder, val).append(' '); - if (count == 8) { builder.append(" "); } - } else { - chars[15] = ch; - hexDigit(builder, val) .append(" ") .append(chars) .append('\n'); - var nextPos = i + 1; - hexDigit(builder, (byte) ((nextPos >>> 8) & 0xFF)); hexDigit(builder, (byte) (nextPos & 0xFF)); builder.append(": "); - count = 0; - for (int g = 0; g < 16; g++) { chars[g] = '.'; } @@ -301,20 +278,10 @@ public static int getAvailablePort(int port) { .orElse(-1); } - public static ByteBuffer enlargePacketBuffer(BufferAllocator allocator, SSLEngine engine) { - return allocator.takeBuffer(engine - .getSession() - .getPacketBufferSize()); - } - public static ByteBuffer increaseApplicationBuffer( - ByteBuffer current, - BufferAllocator allocator, - SSLEngine engine) { + public static ByteBuffer increaseBuffer(ByteBuffer current, BufferAllocator allocator, int newSize) { - var newBuffer = allocator.takeBuffer(engine - .getSession() - .getApplicationBufferSize()); + var newBuffer = allocator.takeBuffer(newSize); newBuffer.put(current); if (current.capacity() > 0) { @@ -324,28 +291,13 @@ public static ByteBuffer increaseApplicationBuffer( return newBuffer; } - public static ByteBuffer increasePacketBuffer( - ByteBuffer current, - BufferAllocator allocator, - SSLEngine engine) { - - var newBuffer = allocator.takeBuffer(engine - .getSession() - .getPacketBufferSize()); - newBuffer.put(current); - - if (current.capacity() > 0) { - allocator.putBuffer(current); - } - - return newBuffer; + public static void cleanNetworkBuffer(ByteBuffer networkBuffer) { + networkBuffer.clear().limit(0); } - public static ByteBuffer enlargeApplicationBuffer( - BufferAllocator allocator, - SSLEngine engine) { - return allocator.takeBuffer(engine - .getSession() - .getApplicationBufferSize()); + public static void compactNetworkBufferIfNeed(ByteBuffer networkBuffer) { + if (networkBuffer.position() > 0) { + networkBuffer.compact().limit(networkBuffer.position()); + } } } diff --git a/rlib-network/src/main/java/javasabr/rlib/network/util/SslUtils.java b/rlib-network/src/main/java/javasabr/rlib/network/util/SslUtils.java new file mode 100644 index 00000000..45917c19 --- /dev/null +++ b/rlib-network/src/main/java/javasabr/rlib/network/util/SslUtils.java @@ -0,0 +1,24 @@ +package javasabr.rlib.network.util; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class SslUtils { + + public static boolean isReadyToCrypt(HandshakeStatus status) { + return status == HandshakeStatus.FINISHED || status == HandshakeStatus.NOT_HANDSHAKING; + } + + public static boolean needToProcess(HandshakeStatus status) { + return status != HandshakeStatus.FINISHED && status != HandshakeStatus.NOT_HANDSHAKING; + } + + public static HandshakeStatus executeSslTasks(SSLEngine engine) { + for (Runnable task = engine.getDelegatedTask(); task != null; task = engine.getDelegatedTask()) { + task.run(); + } + return engine.getHandshakeStatus(); + } +} diff --git a/rlib-network/src/test/java/javasabr/rlib/network/BaseNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/BaseNetworkTest.java index 3ccfaaeb..9fc8f5b4 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/BaseNetworkTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/BaseNetworkTest.java @@ -5,9 +5,9 @@ import javasabr.rlib.network.impl.DefaultBufferAllocator; import javasabr.rlib.network.impl.DefaultConnection; import javasabr.rlib.network.impl.StringDataConnection; -import javasabr.rlib.network.impl.StringDataSSLConnection; -import javasabr.rlib.network.packet.impl.DefaultReadablePacket; -import javasabr.rlib.network.packet.registry.ReadablePacketRegistry; +import javasabr.rlib.network.impl.StringDataSslConnection; +import javasabr.rlib.network.packet.impl.DefaultReadableNetworkPacket; +import javasabr.rlib.network.packet.registry.ReadableNetworkPacketRegistry; import javasabr.rlib.network.server.ServerNetwork; import javax.net.ssl.SSLContext; import lombok.AllArgsConstructor; @@ -48,7 +48,7 @@ protected TestNetwork buildStringNetwork() { new DefaultBufferAllocator(NetworkConfig.DEFAULT_CLIENT)); } - protected TestNetwork buildStringSSLNetwork( + protected TestNetwork buildStringSSLNetwork( SSLContext serverSSLContext, SSLContext clientSSLContext) { return buildStringSSLNetwork( @@ -89,14 +89,14 @@ protected TestNetwork buildStringNetwork( var asyncClientToServer = new CompletableFuture(); var asyncServerToClient = new CompletableFuture(); - var serverNetwork = NetworkFactory.newStringDataServerNetwork(serverNetworkConfig, serverBufferAllocator); + var serverNetwork = NetworkFactory.stringDataServerNetwork(serverNetworkConfig, serverBufferAllocator); var serverAddress = serverNetwork.start(); serverNetwork.onAccept(asyncServerToClient::complete); - var clientNetwork = NetworkFactory.newStringDataClientNetwork(clientNetworkConfig, clientBufferAllocator); + var clientNetwork = NetworkFactory.stringDataClientNetwork(clientNetworkConfig, clientBufferAllocator); clientNetwork - .connect(serverAddress) + .connectAsync(serverAddress) .thenApply(asyncClientToServer::complete); return new TestNetwork<>( @@ -108,7 +108,7 @@ protected TestNetwork buildStringNetwork( clientNetwork); } - protected TestNetwork buildStringSSLNetwork( + protected TestNetwork buildStringSSLNetwork( ServerNetworkConfig serverNetworkConfig, BufferAllocator serverBufferAllocator, SSLContext serverSSLContext, @@ -116,10 +116,10 @@ protected TestNetwork buildStringSSLNetwork( BufferAllocator clientBufferAllocator, SSLContext clientSSLContext) { - var asyncClientToServer = new CompletableFuture(); - var asyncServerToClient = new CompletableFuture(); + var asyncClientToServer = new CompletableFuture(); + var asyncServerToClient = new CompletableFuture(); - var serverNetwork = NetworkFactory.newStringDataSSLServerNetwork( + var serverNetwork = NetworkFactory.stringDataSslServerNetwork( serverNetworkConfig, serverBufferAllocator, serverSSLContext); @@ -127,13 +127,13 @@ protected TestNetwork buildStringSSLNetwork( var serverAddress = serverNetwork.start(); serverNetwork.onAccept(asyncServerToClient::complete); - var clientNetwork = NetworkFactory.newStringDataSSLClientNetwork( + var clientNetwork = NetworkFactory.stringDataSslClientNetwork( clientNetworkConfig, clientBufferAllocator, clientSSLContext); clientNetwork - .connect(serverAddress) + .connectAsync(serverAddress) .thenApply(asyncClientToServer::complete); return new TestNetwork<>( @@ -146,8 +146,8 @@ protected TestNetwork buildStringSSLNetwork( } protected TestNetwork buildDefaultNetwork( - ReadablePacketRegistry serverPacketRegistry, - ReadablePacketRegistry clientPacketRegistry) { + ReadableNetworkPacketRegistry serverPacketRegistry, + ReadableNetworkPacketRegistry clientPacketRegistry) { return buildDefaultNetwork( ServerNetworkConfig.DEFAULT_SERVER, new DefaultBufferAllocator(ServerNetworkConfig.DEFAULT_SERVER), @@ -159,9 +159,9 @@ protected TestNetwork buildDefaultNetwork( protected TestNetwork buildDefaultNetwork( BufferAllocator serverBufferAllocator, - ReadablePacketRegistry serverPacketRegistry, + ReadableNetworkPacketRegistry serverPacketRegistry, BufferAllocator clientBufferAllocator, - ReadablePacketRegistry clientPacketRegistry) { + ReadableNetworkPacketRegistry clientPacketRegistry) { return buildDefaultNetwork( ServerNetworkConfig.DEFAULT_SERVER, serverBufferAllocator, @@ -174,15 +174,15 @@ protected TestNetwork buildDefaultNetwork( protected TestNetwork buildDefaultNetwork( ServerNetworkConfig serverNetworkConfig, BufferAllocator serverBufferAllocator, - ReadablePacketRegistry serverPacketRegistry, + ReadableNetworkPacketRegistry serverPacketRegistry, NetworkConfig clientNetworkConfig, BufferAllocator clientBufferAllocator, - ReadablePacketRegistry clientPacketRegistry) { + ReadableNetworkPacketRegistry clientPacketRegistry) { var asyncClientToServer = new CompletableFuture(); var asyncServerToClient = new CompletableFuture(); - var serverNetwork = NetworkFactory.newDefaultServerNetwork( + var serverNetwork = NetworkFactory.defaultServerNetwork( serverNetworkConfig, serverBufferAllocator, serverPacketRegistry); @@ -190,12 +190,12 @@ protected TestNetwork buildDefaultNetwork( serverNetwork.onAccept(asyncServerToClient::complete); - var clientNetwork = NetworkFactory.newDefaultClientNetwork( + var clientNetwork = NetworkFactory.defaultClientNetwork( clientNetworkConfig, clientBufferAllocator, clientPacketRegistry); clientNetwork - .connect(serverAddress) + .connectAsync(serverAddress) .thenApply(asyncClientToServer::complete); return new TestNetwork<>( diff --git a/rlib-network/src/test/java/javasabr/rlib/network/DefaultNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/DefaultNetworkTest.java index cca79717..43d86534 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/DefaultNetworkTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/DefaultNetworkTest.java @@ -1,33 +1,33 @@ package javasabr.rlib.network; -import static java.util.stream.Collectors.toList; -import static javasabr.rlib.network.NetworkFactory.newDefaultClientNetwork; -import static javasabr.rlib.network.NetworkFactory.newDefaultServerNetwork; import static javasabr.rlib.network.ServerNetworkConfig.DEFAULT_SERVER; +import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.time.Duration; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import javasabr.rlib.common.util.ObjectUtils; import javasabr.rlib.common.util.StringUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.annotation.PacketDescription; +import javasabr.rlib.network.annotation.NetworkPacketDescription; import javasabr.rlib.network.impl.DefaultBufferAllocator; import javasabr.rlib.network.impl.DefaultConnection; -import javasabr.rlib.network.packet.impl.DefaultReadablePacket; -import javasabr.rlib.network.packet.impl.DefaultWritablePacket; -import javasabr.rlib.network.packet.registry.ReadablePacketRegistry; +import javasabr.rlib.network.packet.MarkerNetworkPacket; +import javasabr.rlib.network.packet.impl.DefaultReadableNetworkPacket; +import javasabr.rlib.network.packet.impl.DefaultWritableNetworkPacket; +import javasabr.rlib.network.packet.registry.ReadableNetworkPacketRegistry; +import javasabr.rlib.network.server.ServerNetwork; +import lombok.CustomLog; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; -import lombok.ToString; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -36,16 +36,15 @@ * * @author JavaSaBr */ +@CustomLog public class DefaultNetworkTest extends BaseNetworkTest { - private static final Logger LOGGER = LoggerManager.getLogger(DefaultNetworkTest.class); - // client packets interface ClientPackets { @RequiredArgsConstructor - @PacketDescription(id = 1) - class RequestEchoMessage extends DefaultWritablePacket { + @NetworkPacketDescription(id = 1) + class RequestEchoMessage extends DefaultWritableNetworkPacket { private final String message; @@ -56,80 +55,80 @@ protected void writeImpl(ByteBuffer buffer) { } } - @PacketDescription(id = 2) - class RequestServerTime extends DefaultWritablePacket {} + @NetworkPacketDescription(id = 2) + class RequestServerTime extends DefaultWritableNetworkPacket implements MarkerNetworkPacket {} - @ToString @RequiredArgsConstructor - @PacketDescription(id = 3) - class ResponseEchoMessage extends DefaultReadablePacket { + @NetworkPacketDescription(id = 3) + class ResponseEchoMessage extends DefaultReadableNetworkPacket { @Getter + @Nullable private volatile String message; @Override - protected void readImpl(DefaultConnection connection, ByteBuffer buffer) { - super.readImpl(connection, buffer); - message = readString(buffer); + protected void readImpl(ByteBuffer buffer) { + message = readString(buffer, Integer.MAX_VALUE); + } + + @Override + public String toString() { + return "Message:[" + message + "]"; } } - @ToString - @PacketDescription(id = 4) - class ResponseServerTime extends DefaultReadablePacket { + @NetworkPacketDescription(id = 4) + class ResponseServerTime extends DefaultReadableNetworkPacket { @Getter + @Nullable private volatile LocalDateTime localDateTime; @Override - protected void readImpl(DefaultConnection connection, ByteBuffer buffer) { - super.readImpl(connection, buffer); + protected void readImpl(ByteBuffer buffer) { + super.readImpl(buffer); localDateTime = LocalDateTime.ofEpochSecond(readLong(buffer), 0, ZoneOffset.ofTotalSeconds(readInt(buffer))); } + + @Override + public String toString() { + return "ResponseServerTime"; + } } } // server packets interface ServerPackets { - @ToString - @PacketDescription(id = 1) - class RequestEchoMessage extends DefaultReadablePacket { + @NetworkPacketDescription(id = 1) + class RequestEchoMessage extends DefaultReadableNetworkPacket { private volatile String message; @Override - protected void readImpl(DefaultConnection connection, ByteBuffer buffer) { - super.readImpl(connection, buffer); - message = readString(buffer); + protected void readImpl(ByteBuffer buffer) { + super.readImpl(buffer); + message = readString(buffer, Integer.MAX_VALUE); } @Override - protected void executeImpl(DefaultConnection connection) { - super.executeImpl(connection); - connection.send(new ResponseEchoMessage(message)); + public String toString() { + return "Message:[" + message + "]"; } } - @ToString - @PacketDescription(id = 2) - class RequestServerTime extends DefaultReadablePacket { - - @Override - protected void readImpl(DefaultConnection connection, ByteBuffer buffer) { - super.readImpl(connection, buffer); - } + @NetworkPacketDescription(id = 2) + class RequestServerTime extends DefaultReadableNetworkPacket implements MarkerNetworkPacket { @Override - protected void executeImpl(DefaultConnection connection) { - super.executeImpl(connection); - connection.send(new ResponseServerTime()); + public String toString() { + return "RequestServerTime"; } } @RequiredArgsConstructor - @PacketDescription(id = 3) - class ResponseEchoMessage extends DefaultWritablePacket { + @NetworkPacketDescription(id = 3) + class ResponseEchoMessage extends DefaultWritableNetworkPacket { private final String message; @@ -140,19 +139,15 @@ protected void writeImpl(ByteBuffer buffer) { } } - @PacketDescription(id = 4) - class ResponseServerTime extends DefaultWritablePacket { + @NetworkPacketDescription(id = 4) + class ResponseServerTime extends DefaultWritableNetworkPacket { @Override protected void writeImpl(ByteBuffer buffer) { super.writeImpl(buffer); var dateTime = ZonedDateTime.now(); writeLong(buffer, dateTime.toEpochSecond()); - writeInt( - buffer, - dateTime - .getOffset() - .getTotalSeconds()); + writeInt(buffer, dateTime.getOffset().getTotalSeconds()); } } } @@ -160,26 +155,41 @@ protected void writeImpl(ByteBuffer buffer) { @Test @SneakyThrows void echoNetworkTest() { + //LoggerManager.enable(DefaultNetworkTest.class, LoggerLevel.DEBUG); + //LoggerManager.enable(DefaultNetworkTest.class, LoggerLevel.INFO); + //LoggerManager.enable(AbstractNetworkPacketReader.class, LoggerLevel.DEBUG); - var serverNetwork = newDefaultServerNetwork(ReadablePacketRegistry.of( - DefaultReadablePacket.class, + ReadableNetworkPacketRegistry serverPackets = ReadableNetworkPacketRegistry.of( + DefaultReadableNetworkPacket.class, ServerPackets.RequestEchoMessage.class, - ServerPackets.RequestServerTime.class)); - var serverAddress = serverNetwork.start(); + ServerPackets.RequestServerTime.class); + ReadableNetworkPacketRegistry clientPackets = ReadableNetworkPacketRegistry.of( + DefaultReadableNetworkPacket.class, + ClientPackets.ResponseEchoMessage.class, + ClientPackets.ResponseServerTime.class); + + ServerNetwork serverNetwork = NetworkFactory.defaultServerNetwork(serverPackets); + InetSocketAddress serverAddress = serverNetwork.start(); + var counter = new CountDownLatch(90); serverNetwork .accepted() .flatMap(Connection::receivedEvents) - .doOnNext(event -> event.packet.execute(event.connection)) - .subscribe(event -> LOGGER.info("Received from client: " + event.packet)); - - var clientNetwork = newDefaultClientNetwork(ReadablePacketRegistry.of( - DefaultReadablePacket.class, - ClientPackets.ResponseEchoMessage.class, - ClientPackets.ResponseServerTime.class)); + .doOnNext(event -> { + var connection = event.connection(); + var packet = event.packet(); + if (packet instanceof ServerPackets.RequestEchoMessage request) { + connection.send(new ServerPackets.ResponseEchoMessage(request.message)); + } else if (packet instanceof ServerPackets.RequestServerTime request) { + connection.send(new ServerPackets.ResponseServerTime()); + } + }) + .subscribe(event -> log.info(event, "Received from client:[%s]"::formatted)); + + var clientNetwork = NetworkFactory.defaultClientNetwork(clientPackets); clientNetwork - .connected(serverAddress) + .connectReactive(serverAddress) .doOnNext(connection -> IntStream .range(10, 100) .forEach(length -> { @@ -191,7 +201,7 @@ void echoNetworkTest() { })) .flatMapMany(Connection::receivedEvents) .subscribe(event -> { - LOGGER.info("Received from server: " + event.packet); + log.info(event, "Received from server:[%s]"::formatted); counter.countDown(); }); @@ -206,12 +216,12 @@ void echoNetworkTest() { @Test void shouldNotUseMappedBuffers() { - var serverPacketRegistry = ReadablePacketRegistry.of( - DefaultReadablePacket.class, + var serverPacketRegistry = ReadableNetworkPacketRegistry.of( + DefaultReadableNetworkPacket.class, ServerPackets.RequestEchoMessage.class, ServerPackets.RequestServerTime.class); - var clientPacketRegistry = ReadablePacketRegistry.of( - DefaultReadablePacket.class, + var clientPacketRegistry = ReadableNetworkPacketRegistry.of( + DefaultReadableNetworkPacket.class, ClientPackets.ResponseEchoMessage.class, ClientPackets.ResponseServerTime.class); @@ -233,32 +243,32 @@ public ByteBuffer takeBuffer(int bufferSize) { int packetCount = 200; - try (var testNetwork = buildDefaultNetwork( + try (TestNetwork testNetwork = buildDefaultNetwork( serverAllocator, serverPacketRegistry, clientAllocator, clientPacketRegistry)) { - var bufferSize = testNetwork.serverNetworkConfig.getReadBufferSize() / 3; - + int bufferSize = testNetwork.serverNetworkConfig.readBufferSize() / 3; var random = ThreadLocalRandom.current(); - var clientToServer = testNetwork.clientToServer; - var serverToClient = testNetwork.serverToClient; + DefaultConnection clientToServer = testNetwork.clientToServer; + DefaultConnection serverToClient = testNetwork.serverToClient; var pendingPacketsOnServer = serverToClient .receivedPackets() .buffer(packetCount); - var messages = IntStream + List messages = IntStream .range(0, packetCount) .mapToObj(value -> StringUtils.generate(random.nextInt(0, bufferSize))) .peek(message -> clientToServer.send(new ClientPackets.RequestEchoMessage(message))) - .collect(toList()); + .toList(); - var receivedPackets = ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5))); + List receivedPackets = + ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5))); - Assertions.assertEquals(receivedPackets.size(), packetCount, "Didn't receive all packets"); + Assertions.assertEquals(packetCount, receivedPackets.size(), "Didn't receive all packets"); var wrongPacket = receivedPackets .stream() diff --git a/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadableNetworkPacketRegistryTest.java b/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadableNetworkPacketRegistryTest.java new file mode 100644 index 00000000..c30249ee --- /dev/null +++ b/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadableNetworkPacketRegistryTest.java @@ -0,0 +1,123 @@ +package javasabr.rlib.network; + +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.common.util.ClassUtils; +import javasabr.rlib.network.annotation.NetworkPacketDescription; +import javasabr.rlib.network.impl.DefaultConnection; +import javasabr.rlib.network.packet.IdBasedReadableNetworkPacket; +import javasabr.rlib.network.packet.impl.AbstractIdBasedReadableNetworkPacket; +import javasabr.rlib.network.packet.impl.DefaultReadableNetworkPacket; +import javasabr.rlib.network.packet.registry.impl.IdBasedReadableNetworkPacketRegistry; +import lombok.NoArgsConstructor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author JavaSaBr + */ +public class IdBasedReadableNetworkPacketRegistryTest { + + @NoArgsConstructor + @NetworkPacketDescription(id = 1) + public static class Impl1 extends DefaultReadableNetworkPacket {} + + @NoArgsConstructor + @NetworkPacketDescription(id = 2) + public static class Impl2 extends DefaultReadableNetworkPacket {} + + @NoArgsConstructor + @NetworkPacketDescription(id = 3) + public static class Impl3 extends DefaultReadableNetworkPacket {} + + @NoArgsConstructor + private static class PrivateBase extends AbstractIdBasedReadableNetworkPacket {} + + @NoArgsConstructor + @NetworkPacketDescription(id = 1) + private static class PrivateImpl1 extends PrivateBase {} + + @NoArgsConstructor + @NetworkPacketDescription(id = 10) + private static class PrivateImpl2 extends PrivateBase {} + + @NoArgsConstructor + public static class PublicBase extends AbstractIdBasedReadableNetworkPacket {} + + @NoArgsConstructor + @NetworkPacketDescription(id = 1) + public static class PublicImpl1 extends PublicBase {} + + @NoArgsConstructor + @NetworkPacketDescription(id = 5) + public static class PublicImpl2 extends PublicBase {} + + @Test + void shouldBeCreated() { + Assertions.assertDoesNotThrow( + () -> new IdBasedReadableNetworkPacketRegistry<>(IdBasedReadableNetworkPacket.class)); + } + + @Test + void shouldRegister3PacketsByArray() { + + var registry = new IdBasedReadableNetworkPacketRegistry<>(IdBasedReadableNetworkPacket.class) + .register(Array.typed(Class.class, Impl1.class, Impl2.class, Impl3.class)); + + Assertions.assertInstanceOf(Impl1.class, registry.resolvePrototypeById(1)); + Assertions.assertInstanceOf(Impl2.class, registry.resolvePrototypeById(2)); + Assertions.assertInstanceOf(Impl3.class, registry.resolvePrototypeById(3)); + } + + @Test + void shouldRegister3PacketsByVarargs() { + + var registry = new IdBasedReadableNetworkPacketRegistry<>(IdBasedReadableNetworkPacket.class) + .register(Impl1.class, Impl2.class, Impl3.class); + + Assertions.assertInstanceOf(Impl1.class, registry.resolvePrototypeById(1)); + Assertions.assertInstanceOf(Impl2.class, registry.resolvePrototypeById(2)); + Assertions.assertInstanceOf(Impl3.class, registry.resolvePrototypeById(3)); + } + + @Test + void shouldRegister3PacketsBySingle() { + + var registry = new IdBasedReadableNetworkPacketRegistry<>(IdBasedReadableNetworkPacket.class) + .register(Impl1.class) + .register(Impl2.class) + .register(Impl3.class); + + Assertions.assertInstanceOf(Impl1.class, registry.resolvePrototypeById(1)); + Assertions.assertInstanceOf(Impl2.class, registry.resolvePrototypeById(2)); + Assertions.assertInstanceOf(Impl3.class, registry.resolvePrototypeById(3)); + } + + @Test + void shouldRegister2PrivatePacketsBySingle() { + + var registry = new IdBasedReadableNetworkPacketRegistry<>(PrivateBase.class) + .register(PrivateImpl1.class, PrivateImpl1::new) + .register(PrivateImpl2.class, PrivateImpl2::new); + + Assertions.assertInstanceOf(PrivateImpl1.class, registry.resolvePrototypeById(1)); + Assertions.assertInstanceOf(PrivateImpl2.class, registry.resolvePrototypeById(10)); + } + + @Test + void shouldNotAcceptWrongTypes() { + + var array = Array.typed( + Class.class, + PrivateImpl1.class, + PrivateImpl2.class, + PublicImpl1.class, + PublicImpl2.class); + + var registry = new IdBasedReadableNetworkPacketRegistry<>(PublicBase.class) + .register(ClassUtils.>>unsafeNNCast(array)); + + Assertions.assertInstanceOf(PublicImpl1.class, registry.resolvePrototypeById(1)); + Assertions.assertThrows(IllegalArgumentException.class, () -> registry.resolvePrototypeById(2)); + Assertions.assertInstanceOf(PublicImpl2.class, registry.resolvePrototypeById(5)); + } +} diff --git a/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadablePacketRegistryTest.java b/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadablePacketRegistryTest.java deleted file mode 100644 index dbfbd352..00000000 --- a/rlib-network/src/test/java/javasabr/rlib/network/IdBasedReadablePacketRegistryTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package javasabr.rlib.network; - -import javasabr.rlib.collections.array.Array; -import javasabr.rlib.common.util.ClassUtils; -import javasabr.rlib.network.annotation.PacketDescription; -import javasabr.rlib.network.impl.DefaultConnection; -import javasabr.rlib.network.packet.IdBasedReadablePacket; -import javasabr.rlib.network.packet.impl.AbstractIdBasedReadablePacket; -import javasabr.rlib.network.packet.impl.DefaultReadablePacket; -import javasabr.rlib.network.packet.registry.impl.IdBasedReadablePacketRegistry; -import lombok.NoArgsConstructor; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -/** - * @author JavaSaBr - */ -public class IdBasedReadablePacketRegistryTest { - - @NoArgsConstructor - @PacketDescription(id = 1) - public static class Impl1 extends DefaultReadablePacket {} - - @NoArgsConstructor - @PacketDescription(id = 2) - public static class Impl2 extends DefaultReadablePacket {} - - @NoArgsConstructor - @PacketDescription(id = 3) - public static class Impl3 extends DefaultReadablePacket {} - - @NoArgsConstructor - private static class PrivateBase extends AbstractIdBasedReadablePacket {} - - @NoArgsConstructor - @PacketDescription(id = 1) - private static class PrivateImpl1 extends PrivateBase {} - - @NoArgsConstructor - @PacketDescription(id = 10) - private static class PrivateImpl2 extends PrivateBase {} - - @NoArgsConstructor - public static class PublicBase extends AbstractIdBasedReadablePacket {} - - @NoArgsConstructor - @PacketDescription(id = 1) - public static class PublicImpl1 extends PublicBase {} - - @NoArgsConstructor - @PacketDescription(id = 5) - public static class PublicImpl2 extends PublicBase {} - - @Test - Object shouldBeCreated() { - return new IdBasedReadablePacketRegistry<>(IdBasedReadablePacket.class); - } - - @Test - void shouldRegister3PacketsByArray() { - - var registry = new IdBasedReadablePacketRegistry<>(IdBasedReadablePacket.class).register(Array.typed( - Class.class, - Impl1.class, - Impl2.class, - Impl3.class)); - - Assertions.assertTrue(registry.findById(1) instanceof Impl1); - Assertions.assertTrue(registry.findById(2) instanceof Impl2); - Assertions.assertTrue(registry.findById(3) instanceof Impl3); - } - - @Test - void shouldRegister3PacketsByVarargs() { - - var registry = new IdBasedReadablePacketRegistry<>(IdBasedReadablePacket.class).register( - Impl1.class, - Impl2.class, - Impl3.class); - - Assertions.assertTrue(registry.findById(1) instanceof Impl1); - Assertions.assertTrue(registry.findById(2) instanceof Impl2); - Assertions.assertTrue(registry.findById(3) instanceof Impl3); - } - - @Test - void shouldRegister3PacketsBySingle() { - - var registry = new IdBasedReadablePacketRegistry<>(IdBasedReadablePacket.class) - .register(Impl1.class) - .register(Impl2.class) - .register(Impl3.class); - - Assertions.assertTrue(registry.findById(1) instanceof Impl1); - Assertions.assertTrue(registry.findById(2) instanceof Impl2); - Assertions.assertTrue(registry.findById(3) instanceof Impl3); - } - - @Test - void shouldRegister2PrivatePacketsBySingle() { - - var registry = new IdBasedReadablePacketRegistry<>(PrivateBase.class) - .register(PrivateImpl1.class, PrivateImpl1::new) - .register(PrivateImpl2.class, PrivateImpl2::new); - - Assertions.assertTrue(registry.findById(1) instanceof PrivateImpl1); - Assertions.assertTrue(registry.findById(10) instanceof PrivateImpl2); - } - - @Test - void shouldNotAcceptWrongTypes() { - - var array = Array.typed(Class.class, PrivateImpl1.class, PrivateImpl2.class, PublicImpl1.class, PublicImpl2.class); - - var registry = new IdBasedReadablePacketRegistry<>(PublicBase.class) - .register(ClassUtils.>>unsafeNNCast(array)); - - Assertions.assertTrue(registry.findById(1) instanceof PublicImpl1); - Assertions.assertThrows(IllegalArgumentException.class, () -> registry.findById(2)); - Assertions.assertTrue(registry.findById(5) instanceof PublicImpl2); - } -} diff --git a/rlib-network/src/test/java/javasabr/rlib/network/StringNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/StringNetworkTest.java index eb02065f..9378b0d5 100644 --- a/rlib-network/src/test/java/javasabr/rlib/network/StringNetworkTest.java +++ b/rlib-network/src/test/java/javasabr/rlib/network/StringNetworkTest.java @@ -1,27 +1,30 @@ package javasabr.rlib.network; -import static java.util.stream.Collectors.toList; -import static javasabr.rlib.network.NetworkFactory.newStringDataClientNetwork; -import static javasabr.rlib.network.NetworkFactory.newStringDataServerNetwork; import static javasabr.rlib.network.ServerNetworkConfig.DEFAULT_SERVER; +import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.time.Duration; +import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; import javasabr.rlib.common.util.ObjectUtils; import javasabr.rlib.common.util.StringUtils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerManager; import javasabr.rlib.network.ServerNetworkConfig.SimpleServerNetworkConfig; import javasabr.rlib.network.client.ClientNetwork; import javasabr.rlib.network.impl.DefaultBufferAllocator; -import javasabr.rlib.network.packet.impl.StringWritablePacket; +import javasabr.rlib.network.impl.StringDataConnection; +import javasabr.rlib.network.packet.impl.StringReadablePacket; +import javasabr.rlib.network.packet.impl.StringWritableNetworkPacket; +import javasabr.rlib.network.server.ServerNetwork; +import lombok.CustomLog; import lombok.SneakyThrows; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -32,45 +35,59 @@ * * @author JavaSaBr */ +@CustomLog public class StringNetworkTest extends BaseNetworkTest { - private static final Logger LOGGER = LoggerManager.getLogger(StringNetworkTest.class); - @Test @SneakyThrows void echoNetworkTest() { + //LoggerManager.enable(StringNetworkTest.class, LoggerLevel.INFO); + //LoggerManager.enable(AbstractNetworkPacketReader.class, LoggerLevel.INFO); + //LoggerManager.enable(AbstractNetworkPacketReader.class, LoggerLevel.DEBUG); + + ServerNetwork serverNetwork = NetworkFactory.stringDataServerNetwork(); + InetSocketAddress serverAddress = serverNetwork.start(); + + log.info(serverAddress, "Server address:[%s]"::formatted); - var serverNetwork = newStringDataServerNetwork(); - var serverAddress = serverNetwork.start(); - var counter = new CountDownLatch(90); + var counter = new CountDownLatch(190); serverNetwork .accepted() .flatMap(Connection::receivedEvents) .subscribe(event -> { - var message = event.packet.getData(); - LOGGER.info("Received from client: " + message); - event.connection.send(new StringWritablePacket("Echo: " + message)); + String message = event.packet().data(); + log.info(message, "Received from client:[%s]"::formatted); + event.connection().send(new StringWritableNetworkPacket("Echo: " + message)); }); - var clientNetwork = newStringDataClientNetwork(); + ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + var clientNetwork = NetworkFactory.stringDataClientNetwork(); clientNetwork - .connected(serverAddress) + .connectReactive(serverAddress) .doOnNext(connection -> IntStream - .range(10, 100) - .forEach(length -> connection.send(new StringWritablePacket(StringUtils.generate(length))))) + .range(10, 200) + .forEach(length -> { + var packet = new StringWritableNetworkPacket(StringUtils.generate(length)); + int delay = ThreadLocalRandom + .current() + .nextInt(50); + executor.schedule(() -> connection.send(packet), delay, TimeUnit.MILLISECONDS); + })) .flatMapMany(Connection::receivedEvents) .subscribe(event -> { - LOGGER.info("Received from server: " + event.packet.getData()); + log.info(event.packet().data(), "Received from server:[%s]"::formatted); counter.countDown(); + log.info(counter.getCount(), "Still wait for:[%s]"::formatted); }); Assertions.assertTrue( - counter.await(10000, TimeUnit.MILLISECONDS), + counter.await(10000, TimeUnit.MINUTES), "Still wait for " + counter.getCount() + " packets..."); clientNetwork.shutdown(); serverNetwork.shutdown(); + executor.shutdown(); } @Test @@ -94,39 +111,38 @@ public ByteBuffer takeBuffer(int bufferSize) { int packetCount = 200; - try (var testNetwork = buildStringNetwork(serverAllocator, clientAllocator)) { - - var bufferSize = testNetwork.serverNetworkConfig.getReadBufferSize() / 3; - + try (TestNetwork testNetwork = buildStringNetwork(serverAllocator, clientAllocator)) { + int bufferSize = testNetwork.serverNetworkConfig.readBufferSize() / 3; var random = ThreadLocalRandom.current(); - var clientToServer = testNetwork.clientToServer; - var serverToClient = testNetwork.serverToClient; + StringDataConnection clientToServer = testNetwork.clientToServer; + StringDataConnection serverToClient = testNetwork.serverToClient; var pendingPacketsOnServer = serverToClient .receivedPackets() .buffer(packetCount); - var messages = IntStream + List messages = IntStream .range(0, packetCount) .mapToObj(value -> StringUtils.generate(random.nextInt(0, bufferSize))) .peek(message -> { - LOGGER.info("Send " + message.length() + " symbols to server"); - clientToServer.send(new StringWritablePacket(message)); + log.info(message.length(), "Send [%s] symbols to server"::formatted); + clientToServer.send(new StringWritableNetworkPacket(message)); }) - .collect(toList()); + .toList(); - var receivedPackets = ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5))); + List receivedPackets = ObjectUtils + .notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5))); - LOGGER.info("Received " + receivedPackets.size() + " packets from client"); + log.info(receivedPackets.size(), "Received [%s] packets from client"::formatted); - Assertions.assertEquals(receivedPackets.size(), packetCount, "Didn't receive all packets"); + Assertions.assertEquals(packetCount, receivedPackets.size(), "Didn't receive all packets"); var wrongPacket = receivedPackets .stream() .filter(packet -> messages .stream() - .noneMatch(message -> message.equals(packet.getData()))) + .noneMatch(message -> message.equals(packet.data()))) .findFirst() .orElse(null); @@ -136,20 +152,22 @@ public ByteBuffer takeBuffer(int bufferSize) { @Test void shouldReceiveManyPacketsFromSmallToBigSize() { + //LoggerManager.enable(StringNetworkTest.class, LoggerLevel.INFO); - int packetCount = 200; - - try (var testNetwork = buildStringNetwork()) { - - var bufferSize = testNetwork.serverNetworkConfig.getReadBufferSize(); + int packetCount = 2000; + try (TestNetwork testNetwork = buildStringNetwork(); + ScheduledExecutorService executor = Executors.newScheduledThreadPool(1)) { + int bufferSize = testNetwork.serverNetworkConfig.readBufferSize(); var random = ThreadLocalRandom.current(); - var clientToServer = testNetwork.clientToServer; - var serverToClient = testNetwork.serverToClient; + StringDataConnection clientToServer = testNetwork.clientToServer; + StringDataConnection serverToClient = testNetwork.serverToClient; var pendingPacketsOnServer = serverToClient .receivedPackets() + .doOnNext(packet -> + log.info(packet.data().length(), "Received [%s] symbols from client"::formatted)) .buffer(packetCount); var messages = IntStream @@ -159,22 +177,30 @@ void shouldReceiveManyPacketsFromSmallToBigSize() { return StringUtils.generate(length); }) .peek(message -> { - LOGGER.info("Send " + message.length() + " symbols to server"); - clientToServer.send(new StringWritablePacket(message)); + var packet = new StringWritableNetworkPacket(message); + int delay = ThreadLocalRandom + .current() + .nextInt(15); + executor.schedule( + () -> { + clientToServer.send(packet); + log.info(message.length(), "Send [%s] symbols to server"::formatted); + }, delay, TimeUnit.MILLISECONDS); }) - .collect(toList()); + .toList(); - var receivedPackets = ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5))); + List receivedPackets = + ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5))); - LOGGER.info("Received " + receivedPackets.size() + " packets from client"); + log.info(receivedPackets.size(), "Received [%s] packets from client"::formatted); - Assertions.assertEquals(receivedPackets.size(), packetCount, "Didn't receive all packets"); + Assertions.assertEquals(packetCount, receivedPackets.size(), "Didn't receive all packets"); var wrongPacket = receivedPackets .stream() .filter(packet -> messages .stream() - .noneMatch(message -> message.equals(packet.getData()))) + .noneMatch(message -> message.equals(packet.data()))) .findFirst() .orElse(null); @@ -184,47 +210,55 @@ void shouldReceiveManyPacketsFromSmallToBigSize() { @Test void shouldSendBiggerPacketThanWriteBuffer() { + //LoggerManager.enable(StringNetworkTest.class, LoggerLevel.INFO); int packetCount = 10_000; - try (var testNetwork = buildStringNetwork()) { - - var bufferSize = testNetwork.clientNetworkConfig.getWriteBufferSize(); - + try (TestNetwork testNetwork = buildStringNetwork(); + ScheduledExecutorService executor = Executors.newScheduledThreadPool(1)) { + int bufferSize = testNetwork.clientNetworkConfig.writeBufferSize(); var random = ThreadLocalRandom.current(); - var clientToServer = testNetwork.clientToServer; - var serverToClient = testNetwork.serverToClient; + StringDataConnection clientToServer = testNetwork.clientToServer; + StringDataConnection serverToClient = testNetwork.serverToClient; var pendingPacketsOnServer = serverToClient .receivedPackets() + .doOnNext(packet -> + log.info(packet.data().length(), "Received [%s] symbols from client"::formatted)) .buffer(packetCount); - var messages = IntStream + List messages = IntStream .range(0, packetCount) .mapToObj(value -> { - var length = random.nextBoolean() ? random.nextInt(bufferSize, bufferSize * 10) : random.nextInt(0, 200); - return StringUtils.generate(length); }) .peek(message -> { - LOGGER.info("Send " + message.length() + " symbols to server"); - clientToServer.send(new StringWritablePacket(message)); + var packet = new StringWritableNetworkPacket(message); + int delay = ThreadLocalRandom + .current() + .nextInt(15); + executor.schedule( + () -> { + clientToServer.send(packet); + log.info(message.length(), "Send [%s] symbols to server"::formatted); + }, delay, TimeUnit.MILLISECONDS); }) - .collect(toList()); + .toList(); - var receivedPackets = ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5))); + List receivedPackets = + ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5))); - LOGGER.info("Received " + receivedPackets.size() + " packets from client"); + log.info(receivedPackets.size(), "Received [%s] packets from client"::formatted); - Assertions.assertEquals(receivedPackets.size(), packetCount, "Didn't receive all packets"); + Assertions.assertEquals(packetCount, receivedPackets.size(), "Didn't receive all packets"); var wrongPacket = receivedPackets .stream() .filter(packet -> messages .stream() - .noneMatch(message -> message.equals(packet.getData()))) + .noneMatch(message -> message.equals(packet.data()))) .findFirst() .orElse(null); @@ -235,7 +269,6 @@ void shouldSendBiggerPacketThanWriteBuffer() { @Test @SneakyThrows void testServerWithMultiplyClients() { - //LoggerManager.enable(AbstractPacketWriter.class, LoggerLevel.DEBUG); var serverConfig = SimpleServerNetworkConfig @@ -246,30 +279,31 @@ void testServerWithMultiplyClients() { var serverAllocator = new DefaultBufferAllocator(serverConfig); var clientAllocator = new DefaultBufferAllocator(NetworkConfig.DEFAULT_CLIENT); - var clientCount = 100; - var packetsPerClient = 100; + int clientCount = 100; + int packetsPerClient = 100; + int minMessageLength = 10; + int maxMessageLength = (int) (DEFAULT_SERVER.readBufferSize() * 1.5); + var counter = new CountDownLatch(clientCount * packetsPerClient); - var minMessageLength = 10; - var maxMessageLength = (int) (DEFAULT_SERVER.getReadBufferSize() * 1.5); var sentPacketsToServer = new AtomicInteger(); var receivedPacketsOnServer = new AtomicInteger(); var receivedPacketsOnClients = new AtomicInteger(); - var serverNetwork = newStringDataServerNetwork(serverConfig, serverAllocator); - var serverAddress = serverNetwork.start(); + ServerNetwork serverNetwork = NetworkFactory.stringDataServerNetwork(serverConfig, serverAllocator); + InetSocketAddress serverAddress = serverNetwork.start(); serverNetwork .accepted() .flatMap(Connection::receivedEvents) .doOnNext(event -> receivedPacketsOnServer.incrementAndGet()) - .subscribe(event -> event.connection.send(newMessage(minMessageLength, maxMessageLength))); + .subscribe(event -> event.connection().send(newMessage(minMessageLength, maxMessageLength))); Flux .fromStream(IntStream .range(0, clientCount) - .mapToObj(value -> newStringDataClientNetwork(NetworkConfig.DEFAULT_CLIENT, clientAllocator))) + .mapToObj(value -> NetworkFactory.stringDataClientNetwork(NetworkConfig.DEFAULT_CLIENT, clientAllocator))) .doOnDiscard(ClientNetwork.class, Network::shutdown) - .flatMap(client -> client.connected(serverAddress)) + .flatMap(client -> client.connectReactive(serverAddress)) .flatMap(connection -> { var receivedEvents = connection.receivedEvents(); @@ -299,17 +333,19 @@ void testServerWithMultiplyClients() { @SneakyThrows void testServerWithMultiplyClientsUsingOldApi() { - var serverNetwork = newStringDataServerNetwork(SimpleServerNetworkConfig + var serverNetwork = NetworkFactory.stringDataServerNetwork(SimpleServerNetworkConfig .builder() .threadGroupSize(10) .build()); - var serverAddress = serverNetwork.start(); - var clientCount = 100; - var packetsPerClient = 1000; + InetSocketAddress serverAddress = serverNetwork.start(); + + int clientCount = 100; + int packetsPerClient = 1000; + int minMessageLength = 10; + int maxMessageLength = (int) (DEFAULT_SERVER.readBufferSize() * 1.5); + var counter = new CountDownLatch(clientCount * packetsPerClient); - var minMessageLength = 10; - var maxMessageLength = (int) (DEFAULT_SERVER.getReadBufferSize() * 1.5); var sentPacketsToServer = new AtomicInteger(); var receivedPacketsOnServer = new AtomicInteger(); var receivedPacketsOnClients = new AtomicInteger(); @@ -323,17 +359,17 @@ void testServerWithMultiplyClientsUsingOldApi() { connectedClients.countDown(); }); - var clients = IntStream + List> clients = IntStream .range(0, clientCount) - .mapToObj(value -> newStringDataClientNetwork()) + .mapToObj(value -> NetworkFactory.stringDataClientNetwork()) .peek(client -> client.connect(serverAddress)) - .collect(toList()); + .toList(); connectedClients.await(); clients .stream() - .map(ClientNetwork::getCurrentConnection) + .map(ClientNetwork::currentConnection) .filter(Objects::nonNull) .peek(connection -> connection.onReceive((con, packet) -> { receivedPacketsOnClients.incrementAndGet(); @@ -359,24 +395,22 @@ void shouldGetAllPacketWithFeedback() { int packetCount = 200; - try (var testNetwork = buildStringNetwork()) { - - var bufferSize = testNetwork.serverNetworkConfig.getReadBufferSize() / 3; - + try (TestNetwork testNetwork = buildStringNetwork()) { + int bufferSize = testNetwork.serverNetworkConfig.readBufferSize() / 3; var random = ThreadLocalRandom.current(); - var clientToServer = testNetwork.clientToServer; - var serverToClient = testNetwork.serverToClient; + StringDataConnection clientToServer = testNetwork.clientToServer; + StringDataConnection serverToClient = testNetwork.serverToClient; var pendingPacketsOnServer = serverToClient .receivedPackets() .buffer(packetCount); - var asyncResults = IntStream + List> asyncResults = IntStream .range(0, packetCount) .mapToObj(value -> StringUtils.generate(random.nextInt(0, bufferSize))) - .map(message -> clientToServer.sendWithFeedback(new StringWritablePacket(message))) - .collect(toList()); + .map(message -> clientToServer.sendWithFeedback(new StringWritableNetworkPacket(message))) + .toList(); CompletableFuture .allOf(asyncResults.toArray(CompletableFuture[]::new)) @@ -394,11 +428,11 @@ void shouldGetAllPacketWithFeedback() { // so all packets are already sent, we should not wait for long time to get result var receivedPackets = ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofMillis(100))); - Assertions.assertEquals(receivedPackets.size(), packetCount, "Didn't receive all packets"); + Assertions.assertEquals(packetCount, receivedPackets.size(), "Didn't receive all packets"); } } - private static StringWritablePacket newMessage(int minMessageLength, int maxMessageLength) { - return new StringWritablePacket(StringUtils.generate(minMessageLength, maxMessageLength)); + private static StringWritableNetworkPacket newMessage(int minMessageLength, int maxMessageLength) { + return new StringWritableNetworkPacket(StringUtils.generate(minMessageLength, maxMessageLength)); } } diff --git a/rlib-network/src/test/java/javasabr/rlib/network/StringSSLNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/StringSSLNetworkTest.java deleted file mode 100644 index aace14f0..00000000 --- a/rlib-network/src/test/java/javasabr/rlib/network/StringSSLNetworkTest.java +++ /dev/null @@ -1,321 +0,0 @@ -package javasabr.rlib.network; - -import static java.util.stream.Collectors.toList; -import static javasabr.rlib.network.NetworkFactory.newStringDataSSLClientNetwork; -import static javasabr.rlib.network.NetworkFactory.newStringDataSSLServerNetwork; - -import java.io.PrintWriter; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.time.Duration; -import java.util.Scanner; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; -import javasabr.rlib.common.util.ObjectUtils; -import javasabr.rlib.common.util.StringUtils; -import javasabr.rlib.common.util.Utils; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerLevel; -import javasabr.rlib.logger.api.LoggerManager; -import javasabr.rlib.network.impl.DefaultBufferAllocator; -import javasabr.rlib.network.packet.impl.AbstractSSLPacketReader; -import javasabr.rlib.network.packet.impl.AbstractSSLPacketWriter; -import javasabr.rlib.network.packet.impl.StringReadablePacket; -import javasabr.rlib.network.packet.impl.StringWritablePacket; -import javasabr.rlib.network.util.NetworkUtils; -import javax.net.ssl.SSLSocket; -import lombok.SneakyThrows; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -/** - * The tests of string based network. - * - * @author JavaSaBr - */ -public class StringSSLNetworkTest extends BaseNetworkTest { - - private static final Logger LOGGER = LoggerManager.getLogger(StringSSLNetworkTest.class); - - @Test - @SneakyThrows - void certificatesTest() { - - var keystoreFile = StringSSLNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); - var sslContext = NetworkUtils.createSslContext(keystoreFile, "test"); - var clientSSLContext = NetworkUtils.createAllTrustedClientSslContext(); - - var serverPort = NetworkUtils.getAvailablePort(10000); - - var serverSocketFactory = sslContext.getServerSocketFactory(); - var serverSocket = serverSocketFactory.createServerSocket(serverPort); - - var clientSocketFactory = clientSSLContext.getSocketFactory(); - var clientSocket = (SSLSocket) clientSocketFactory.createSocket("localhost", serverPort); - - var clientSocketOnServer = serverSocket.accept(); - - new Thread(() -> Utils.unchecked(() -> { - var clientOutStream = new PrintWriter(clientSocket.getOutputStream()); - clientOutStream.println("Hello SSL"); - clientOutStream.flush(); - })).start(); - - var serverIn = new Scanner(clientSocketOnServer.getInputStream()); - var receivedOnServer = serverIn.next() + " " + serverIn.next(); - - Assertions.assertEquals("Hello SSL", receivedOnServer); - } - - @Test - @SneakyThrows - void serverSSLNetworkTest() { - - var keystoreFile = StringSSLNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); - var sslContext = NetworkUtils.createSslContext(keystoreFile, "test"); - - var serverNetwork = newStringDataSSLServerNetwork( - ServerNetworkConfig.DEFAULT_SERVER, - new DefaultBufferAllocator(ServerNetworkConfig.DEFAULT_CLIENT), - sslContext); - - var serverAddress = serverNetwork.start(); - - serverNetwork - .accepted() - .flatMap(Connection::receivedEvents) - .subscribe(event -> { - var message = event.packet.getData(); - LOGGER.info("Received from client: " + message); - event.connection.send(new StringWritablePacket("Echo: " + message)); - }); - - var clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); - var sslSocketFactory = clientSslContext.getSocketFactory(); - var sslSocket = (SSLSocket) sslSocketFactory.createSocket(serverAddress.getHostName(), serverAddress.getPort()); - - var buffer = ByteBuffer.allocate(1024); - buffer.position(2); - - new StringWritablePacket("Hello SSL").write(buffer); - - buffer.putShort(0, (short) buffer.position()); - buffer.flip(); - - var out = sslSocket.getOutputStream(); - out.write(buffer.array(), 0, buffer.limit()); - out.flush(); - - buffer.clear(); - - var in = sslSocket.getInputStream(); - var readBytes = in.read(buffer.array()); - - buffer - .position(readBytes) - .flip(); - var packetLength = buffer.getShort(); - - var response = new StringReadablePacket(); - response.read(null, buffer, packetLength - 2); - - LOGGER.info("Response: " + response.getData()); - - serverNetwork.shutdown(); - } - - @Test - @SneakyThrows - void clientSSLNetworkTest() { - - System.setProperty("javax.net.debug", "all"); - //LoggerManager.enable(AbstractSSLPacketWriter.class, LoggerLevel.DEBUG); - //LoggerManager.enable(AbstractSSLPacketReader.class, LoggerLevel.DEBUG); - - var keystoreFile = StringSSLNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); - var sslContext = NetworkUtils.createSslContext(keystoreFile, "test"); - - var serverPort = NetworkUtils.getAvailablePort(1000); - - var serverSocketFactory = sslContext.getServerSocketFactory(); - var serverSocket = serverSocketFactory.createServerSocket(serverPort); - var counter = new CountDownLatch(1); - - var clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); - var clientNetwork = newStringDataSSLClientNetwork( - NetworkConfig.DEFAULT_CLIENT, - new DefaultBufferAllocator(NetworkConfig.DEFAULT_CLIENT), - clientSslContext); - - clientNetwork - .connected(new InetSocketAddress("localhost", serverPort)) - .doOnNext(connection -> connection.send(new StringWritablePacket("Hello SSL"))) - .doOnError(Throwable::printStackTrace) - .flatMapMany(Connection::receivedEvents) - .subscribe(event -> { - LOGGER.info("Received from server: " + event.packet.getData()); - counter.countDown(); - }); - - var acceptedClientSocket = serverSocket.accept(); - - var buffer = ByteBuffer.allocate(512); - - var clientIn = acceptedClientSocket.getInputStream(); - var readBytes = clientIn.read(buffer.array()); - - buffer - .position(readBytes) - .flip(); - - var dataLength = buffer.getShort(); - - var receivedPacket = new StringReadablePacket(); - receivedPacket.read(null, buffer, dataLength); - - Assertions.assertEquals("Hello SSL", receivedPacket.getData()); - - LOGGER.info("Received from client: " + receivedPacket.getData()); - - buffer.clear(); - buffer.position(2); - - new StringWritablePacket("Echo: Hello SSL").write(buffer); - - buffer.putShort(0, (short) buffer.position()); - buffer.flip(); - - var out = acceptedClientSocket.getOutputStream(); - out.write(buffer.array(), 0, buffer.limit()); - out.flush(); - - buffer.clear(); - - Assertions.assertTrue( - counter.await(100_000, TimeUnit.MILLISECONDS), - "Still wait for " + counter.getCount() + " packets..."); - - clientNetwork.shutdown(); - serverSocket.close(); - } - - @Test - @SneakyThrows - void echoNetworkTest() { - - //System.setProperty("javax.net.debug", "all"); - //LoggerManager.enable(AbstractPacketWriter.class, LoggerLevel.DEBUG); - //LoggerManager.enable(AbstractSSLPacketWriter.class, LoggerLevel.DEBUG); - //LoggerManager.enable(AbstractSSLPacketReader.class, LoggerLevel.DEBUG); - - var keystoreFile = StringSSLNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); - var serverSSLContext = NetworkUtils.createSslContext(keystoreFile, "test"); - - var serverNetwork = newStringDataSSLServerNetwork( - ServerNetworkConfig.DEFAULT_SERVER, - new DefaultBufferAllocator(ServerNetworkConfig.DEFAULT_CLIENT), - serverSSLContext); - - var expectedReceivedPackets = 90; - var serverAddress = serverNetwork.start(); - var counter = new CountDownLatch(expectedReceivedPackets); - - serverNetwork - .accepted() - .flatMap(Connection::receivedEvents) - .subscribe(event -> { - var message = event.packet.getData(); - LOGGER.info("Received from client: " + message); - event.connection.send(new StringWritablePacket("Echo: " + message)); - }); - - var clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); - var clientNetwork = newStringDataSSLClientNetwork( - NetworkConfig.DEFAULT_CLIENT, - new DefaultBufferAllocator(NetworkConfig.DEFAULT_CLIENT), - clientSslContext); - - clientNetwork - .connected(serverAddress) - .doOnNext(connection -> IntStream - .range(10, expectedReceivedPackets + 10) - .forEach(length -> connection.send(newMessage(9, length)))) - .doOnError(Throwable::printStackTrace) - .flatMapMany(Connection::receivedEvents) - .subscribe(event -> { - LOGGER.info("Received from server: " + event.packet.getData()); - counter.countDown(); - }); - - Assertions.assertTrue( - counter.await(1000, TimeUnit.MILLISECONDS), - "Still wait for " + counter.getCount() + " packets..."); - - serverNetwork.shutdown(); - clientNetwork.shutdown(); - - //LoggerManager.disable(AbstractSSLPacketWriter.class, LoggerLevel.DEBUG); - //LoggerManager.disable(AbstractSSLPacketReader.class, LoggerLevel.DEBUG); - //LoggerManager.disable(AbstractPacketWriter.class, LoggerLevel.DEBUG); - } - - @Test - void shouldReceiveManyPacketsFromSmallToBigSize() { - - //System.setProperty("javax.net.debug", "all"); - //LoggerManager.enable(AbstractPacketReader.class, LoggerLevel.DEBUG); - //LoggerManager.enable(AbstractPacketWriter.class, LoggerLevel.DEBUG); - LoggerManager.enable(AbstractSSLPacketWriter.class, LoggerLevel.DEBUG); - LoggerManager.enable(AbstractSSLPacketReader.class, LoggerLevel.DEBUG); - - var keystoreFile = StringSSLNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); - var serverSSLContext = NetworkUtils.createSslContext(keystoreFile, "test"); - var clientSSLContext = NetworkUtils.createAllTrustedClientSslContext(); - - int packetCount = 10; - - try (var testNetwork = buildStringSSLNetwork(serverSSLContext, clientSSLContext)) { - - var bufferSize = testNetwork.serverNetworkConfig.getReadBufferSize(); - - var random = ThreadLocalRandom.current(); - - var clientToServer = testNetwork.clientToServer; - var serverToClient = testNetwork.serverToClient; - - var pendingPacketsOnServer = serverToClient - .receivedPackets() - .doOnNext(packet -> LOGGER.info("Received from client: " + packet.getData())) - .buffer(packetCount); - - var messages = IntStream - .range(0, packetCount) - .mapToObj(value -> { - var length = value % 3 == 0 ? bufferSize : random.nextInt(0, bufferSize / 2 - 1); - return StringUtils.generate(length); - }) - .peek(message -> clientToServer.send(new StringWritablePacket(message))) - .collect(toList()); - - var receivedPackets = ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5000))); - - Assertions.assertEquals(receivedPackets.size(), packetCount, "Didn't receive all packets"); - - var wrongPacket = receivedPackets - .stream() - .filter(packet -> messages - .stream() - .noneMatch(message -> message.equals(packet.getData()))) - .findFirst() - .orElse(null); - - Assertions.assertNull(wrongPacket, () -> "Wrong received packet: " + wrongPacket); - } - } - - private static StringWritablePacket newMessage(int minMessageLength, int maxMessageLength) { - return new StringWritablePacket(StringUtils.generate(minMessageLength, maxMessageLength)); - } -} diff --git a/rlib-network/src/test/java/javasabr/rlib/network/StringSslNetworkTest.java b/rlib-network/src/test/java/javasabr/rlib/network/StringSslNetworkTest.java new file mode 100644 index 00000000..da87ee0e --- /dev/null +++ b/rlib-network/src/test/java/javasabr/rlib/network/StringSslNetworkTest.java @@ -0,0 +1,333 @@ +package javasabr.rlib.network; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.List; +import java.util.Scanner; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; +import javasabr.rlib.common.util.ObjectUtils; +import javasabr.rlib.common.util.StringUtils; +import javasabr.rlib.common.util.ThreadUtils; +import javasabr.rlib.common.util.Utils; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.api.LoggerManager; +import javasabr.rlib.network.client.ClientNetwork; +import javasabr.rlib.network.impl.DefaultBufferAllocator; +import javasabr.rlib.network.impl.StringDataSslConnection; +import javasabr.rlib.network.packet.impl.AbstractSslNetworkPacketReader; +import javasabr.rlib.network.packet.impl.AbstractSslNetworkPacketWriter; +import javasabr.rlib.network.packet.impl.StringReadablePacket; +import javasabr.rlib.network.packet.impl.StringWritableNetworkPacket; +import javasabr.rlib.network.server.ServerNetwork; +import javasabr.rlib.network.util.NetworkUtils; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import lombok.CustomLog; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * The tests of string based network. + * + * @author JavaSaBr + */ +@CustomLog +public class StringSslNetworkTest extends BaseNetworkTest { + + @Test + @SneakyThrows + void certificatesTest() { + + InputStream keystoreFile = StringSslNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); + SSLContext sslContext = NetworkUtils.createSslContext(keystoreFile, "test"); + SSLContext clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); + + int serverPort = NetworkUtils.getAvailablePort(10000); + + SSLServerSocketFactory serverSocketFactory = sslContext.getServerSocketFactory(); + ServerSocket serverSocket = serverSocketFactory.createServerSocket(serverPort); + + SSLSocketFactory clientSocketFactory = clientSslContext.getSocketFactory(); + SSLSocket clientSocket = (SSLSocket) clientSocketFactory.createSocket("localhost", serverPort); + + var clientSocketOnServer = serverSocket.accept(); + + new Thread(() -> Utils.unchecked(() -> { + var clientOutStream = new PrintWriter(clientSocket.getOutputStream()); + clientOutStream.println("Hello SSL"); + clientOutStream.flush(); + })).start(); + + Scanner serverIn = new Scanner(clientSocketOnServer.getInputStream()); + String receivedOnServer = serverIn.next() + " " + serverIn.next(); + + Assertions.assertEquals("Hello SSL", receivedOnServer); + } + + @Test + @SneakyThrows + void serverSslNetworkTest() { + //LoggerManager.enable(StringSslNetworkTest.class, LoggerLevel.INFO); + + InputStream keystoreFile = StringSslNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); + SSLContext sslContext = NetworkUtils.createSslContext(keystoreFile, "test"); + + ServerNetwork serverNetwork = NetworkFactory.stringDataSslServerNetwork( + ServerNetworkConfig.DEFAULT_SERVER, + new DefaultBufferAllocator(ServerNetworkConfig.DEFAULT_CLIENT), + sslContext); + + InetSocketAddress serverAddress = serverNetwork.start(); + + serverNetwork + .accepted() + .flatMap(Connection::receivedEvents) + .subscribe(event -> { + var message = event.packet().data(); + log.info(message, "Received from client:[%s]"::formatted); + event.connection().send(new StringWritableNetworkPacket("Echo: " + message)); + }); + + SSLContext clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); + SSLSocketFactory sslSocketFactory = clientSslContext.getSocketFactory(); + SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(serverAddress.getHostName(), serverAddress.getPort()); + + StringWritableNetworkPacket writableNetworkPacket = new StringWritableNetworkPacket("Hello SSL"); + + var buffer = ByteBuffer.allocate(1024); + buffer.position(2); + writableNetworkPacket.write(buffer); + buffer.putShort(0, (short) buffer.position()); + buffer.flip(); + + var out = sslSocket.getOutputStream(); + out.write(buffer.array(), 0, buffer.limit()); + out.flush(); + + buffer.clear(); + + InputStream in = sslSocket.getInputStream(); + int readBytes = in.read(buffer.array()); + + buffer + .position(readBytes) + .flip(); + short packetLength = buffer.getShort(); + + StringReadablePacket response = new StringReadablePacket(); + response.read(buffer, packetLength - 2); + + log.info(response.data(), "Response:[%s]"::formatted); + + serverNetwork.shutdown(); + } + + @Test + @SneakyThrows + void clientSslNetworkTest() { + //System.setProperty("javax.net.debug", "all"); + //LoggerManager.enable(StringSslNetworkTest.class, LoggerLevel.INFO); + + InputStream keystoreFile = StringSslNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); + SSLContext sslContext = NetworkUtils.createSslContext(keystoreFile, "test"); + + int serverPort = NetworkUtils.getAvailablePort(1000); + + SSLServerSocketFactory serverSocketFactory = sslContext.getServerSocketFactory(); + ServerSocket serverSocket = serverSocketFactory.createServerSocket(serverPort); + CountDownLatch counter = new CountDownLatch(1); + + SSLContext clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); + ClientNetwork clientNetwork = NetworkFactory.stringDataSslClientNetwork( + NetworkConfig.DEFAULT_CLIENT, + new DefaultBufferAllocator(NetworkConfig.DEFAULT_CLIENT), + clientSslContext); + + clientNetwork + .connectReactive(new InetSocketAddress("localhost", serverPort)) + .doOnNext(connection -> connection.send(new StringWritableNetworkPacket("Hello SSL"))) + .doOnError(Throwable::printStackTrace) + .flatMapMany(Connection::receivedEvents) + .subscribe(event -> { + log.info(event.packet().data(), "Received from server:[%s]"::formatted); + counter.countDown(); + }); + + Socket acceptedClientSocket = serverSocket.accept(); + + var buffer = ByteBuffer.allocate(512); + + InputStream clientIn = acceptedClientSocket.getInputStream(); + int readBytes = clientIn.read(buffer.array()); + + buffer + .position(readBytes) + .flip(); + + var dataLength = buffer.getShort(); + + var receivedPacket = new StringReadablePacket(); + receivedPacket.read(buffer, dataLength); + + Assertions.assertEquals("Hello SSL", receivedPacket.data()); + + log.info(receivedPacket.data(), "Received from client:[%s]"::formatted); + + StringWritableNetworkPacket writableNetworkPacket = new StringWritableNetworkPacket("Echo: Hello SSL"); + + buffer.clear(); + buffer.position(2); + writableNetworkPacket.write(buffer); + buffer.putShort(0, (short) buffer.position()); + buffer.flip(); + + OutputStream out = acceptedClientSocket.getOutputStream(); + out.write(buffer.array(), 0, buffer.limit()); + out.flush(); + + buffer.clear(); + + Assertions.assertTrue( + counter.await(100_000, TimeUnit.MILLISECONDS), + "Still wait for " + counter.getCount() + " packets..."); + + clientNetwork.shutdown(); + serverSocket.close(); + } + + @Test + @SneakyThrows + void echoNetworkTest() { + //System.setProperty("javax.net.debug", "all"); + //LoggerManager.enable(StringSslNetworkTest.class, LoggerLevel.INFO); + + InputStream keystoreFile = StringSslNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); + SSLContext serverSSLContext = NetworkUtils.createSslContext(keystoreFile, "test"); + ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + + ServerNetwork serverNetwork = NetworkFactory.stringDataSslServerNetwork( + ServerNetworkConfig.DEFAULT_SERVER, + new DefaultBufferAllocator(ServerNetworkConfig.DEFAULT_CLIENT), + serverSSLContext); + + InetSocketAddress serverAddress = serverNetwork.start(); + int expectedReceivedPackets = 90; + + var counter = new CountDownLatch(expectedReceivedPackets); + + serverNetwork + .accepted() + .flatMap(Connection::receivedEvents) + .subscribe(event -> { + var message = event.packet().data(); + log.info(message, "Received from client:[%s]"::formatted); + event.connection().send(new StringWritableNetworkPacket("Echo: " + message)); + }); + + SSLContext clientSslContext = NetworkUtils.createAllTrustedClientSslContext(); + ClientNetwork clientNetwork = NetworkFactory.stringDataSslClientNetwork( + NetworkConfig.DEFAULT_CLIENT, + new DefaultBufferAllocator(NetworkConfig.DEFAULT_CLIENT), + clientSslContext); + + clientNetwork + .connectReactive(serverAddress) + .doOnNext(connection -> IntStream + .range(10, expectedReceivedPackets + 10) + .forEach(length -> { + var packet = newMessage(9, length); + int delay = ThreadLocalRandom + .current() + .nextInt(2000); + executor.schedule( + () -> { + connection.send(packet); + log.info(packet.data().length(), "Send [%s] symbols to server"::formatted); + }, delay, TimeUnit.MILLISECONDS); + })) + .doOnError(Throwable::printStackTrace) + .flatMapMany(Connection::receivedEvents) + .subscribe(event -> { + log.info(event.packet().data(), "Received from server:[%s]"::formatted); + counter.countDown(); + }); + + Assertions.assertTrue( + counter.await(60, TimeUnit.SECONDS), + "Still wait for " + counter.getCount() + " packets..."); + + serverNetwork.shutdown(); + clientNetwork.shutdown(); + } + + @Test + void shouldReceiveManyPacketsFromSmallToBigSize() { + + //System.setProperty("javax.net.debug", "all"); + //LoggerManager.enable(AbstractPacketReader.class, LoggerLevel.DEBUG); + //LoggerManager.enable(AbstractPacketWriter.class, LoggerLevel.DEBUG); + //LoggerManager.enable(AbstractSslNetworkPacketWriter.class, LoggerLevel.DEBUG); + //LoggerManager.enable(AbstractSslNetworkPacketReader.class, LoggerLevel.DEBUG); + + InputStream keystoreFile = StringSslNetworkTest.class.getResourceAsStream("/ssl/rlib_test_cert.p12"); + SSLContext serverSSLContext = NetworkUtils.createSslContext(keystoreFile, "test"); + SSLContext clientSSLContext = NetworkUtils.createAllTrustedClientSslContext(); + + int packetCount = 10; + + try (TestNetwork testNetwork = buildStringSSLNetwork(serverSSLContext, clientSSLContext)) { + var bufferSize = testNetwork.serverNetworkConfig.readBufferSize(); + var random = ThreadLocalRandom.current(); + + StringDataSslConnection clientToServer = testNetwork.clientToServer; + StringDataSslConnection serverToClient = testNetwork.serverToClient; + + var pendingPacketsOnServer = serverToClient + .receivedPackets() + .doOnNext(packet -> log.info("Received from client: " + packet.data())) + .buffer(packetCount); + + var messages = IntStream + .range(0, packetCount) + .mapToObj(value -> { + var length = value % 3 == 0 ? bufferSize : random.nextInt(0, bufferSize / 2 - 1); + return StringUtils.generate(length); + }) + .peek(message -> clientToServer.send(new StringWritableNetworkPacket(message))) + .toList(); + + List receivedPackets = + ObjectUtils.notNull(pendingPacketsOnServer.blockFirst(Duration.ofSeconds(5000))); + + Assertions.assertEquals(packetCount, receivedPackets.size(), "Didn't receive all packets"); + + var wrongPacket = receivedPackets + .stream() + .filter(packet -> messages + .stream() + .noneMatch(message -> message.equals(packet.data()))) + .findFirst() + .orElse(null); + + Assertions.assertNull(wrongPacket, () -> "Wrong received packet: " + wrongPacket); + } + } + + private static StringWritableNetworkPacket newMessage(int minMessageLength, int maxMessageLength) { + return new StringWritableNetworkPacket(StringUtils.generate(minMessageLength, maxMessageLength)); + } +} diff --git a/rlib-network/src/test/java/javasabr/rlib/network/package-info.java b/rlib-network/src/test/java/javasabr/rlib/network/package-info.java new file mode 100644 index 00000000..53222c6a --- /dev/null +++ b/rlib-network/src/test/java/javasabr/rlib/network/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.network; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/rlib-plugin-system/build.gradle b/rlib-plugin-system/build.gradle index 126f9f5d..f233f472 100644 --- a/rlib-plugin-system/build.gradle +++ b/rlib-plugin-system/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { api projects.rlibCommon diff --git a/rlib-reference/build.gradle b/rlib-reference/build.gradle index 7a1f495a..0a074db2 100644 --- a/rlib-reference/build.gradle +++ b/rlib-reference/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { compileOnly projects.rlibReusable diff --git a/rlib-reusable/build.gradle b/rlib-reusable/build.gradle index db4c8ef6..a8b1acac 100644 --- a/rlib-reusable/build.gradle +++ b/rlib-reusable/build.gradle @@ -1,3 +1,7 @@ +plugins { + id("configure-java") + id("configure-publishing") +} dependencies { api projects.rlibCollections diff --git a/rlib-testcontainers/build.gradle b/rlib-testcontainers/build.gradle index c143c6a5..18df8e69 100644 --- a/rlib-testcontainers/build.gradle +++ b/rlib-testcontainers/build.gradle @@ -1,3 +1,8 @@ +plugins { + id("configure-java") + id("configure-publishing") +} + dependencies { api projects.rlibCommon api libs.testcontainers diff --git a/test-coverage/build.gradle b/test-coverage/build.gradle index 7b385d79..dfeca12a 100644 --- a/test-coverage/build.gradle +++ b/test-coverage/build.gradle @@ -1,5 +1,6 @@ plugins { - id 'jacoco-report-aggregation' + id("configure-java") + id("jacoco-report-aggregation") } dependencies { @@ -20,5 +21,3 @@ dependencies { jacocoAggregation projects.rlibReusable jacocoAggregation projects.rlibTestcontainers } - -publishing.publications.remove(publishing.publications.mavenJava) \ No newline at end of file