├── .dockerignore
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── bin
├── generate-ca.sh
└── generate-server-keystore.sh
├── build.sbt
├── example
├── Dockerfile
├── ca_cert.pem
├── influent-server.jks
├── plain
│ ├── docker-compose.yml
│ └── fluent.conf
└── secure
│ ├── docker-compose.yml
│ └── fluent.conf
├── influent-java-example
└── src
│ └── main
│ ├── java
│ └── example
│ │ ├── Counter.java
│ │ ├── Environment.java
│ │ ├── Print.java
│ │ ├── SecurityCounter.java
│ │ └── TLSCounter.java
│ └── resources
│ └── logback.xml
├── influent-java
└── src
│ ├── main
│ └── java
│ │ └── influent
│ │ ├── EventEntry.java
│ │ ├── EventStream.java
│ │ ├── Tag.java
│ │ ├── forward
│ │ ├── CheckPingResult.java
│ │ ├── ForwardCallback.java
│ │ ├── ForwardClient.java
│ │ ├── ForwardClientNode.java
│ │ ├── ForwardClientUser.java
│ │ ├── ForwardOption.java
│ │ ├── ForwardRequest.java
│ │ ├── ForwardSecurity.java
│ │ ├── ForwardServer.java
│ │ ├── MsgPackPingDecoder.java
│ │ ├── MsgpackForwardRequestDecoder.java
│ │ ├── NioForwardConnection.java
│ │ ├── NioForwardServer.java
│ │ └── NioUdpHeartbeatServer.java
│ │ └── internal
│ │ └── msgpack
│ │ ├── DecodeResult.java
│ │ ├── InfluentByteBuffer.java
│ │ ├── MsgpackIncrementalUnpacker.java
│ │ └── MsgpackStreamUnpacker.java
│ └── test
│ ├── resources
│ └── mockito-extensions
│ │ └── org.mockito.plugins.MockMaker
│ └── scala
│ └── influent
│ ├── forward
│ ├── MsgpackForwardRequestDecoderSpec.scala
│ ├── NioForwardConnectionSpec.scala
│ └── NioUdpHeartbeatServerSpec.scala
│ └── internal
│ └── msgpack
│ ├── MsgpackIncrementalUnpackerSpec.scala
│ ├── MsgpackStreamUnpackerSpec.scala
│ └── MsgpackUnpackerArbitrary.scala
├── influent-transport
└── src
│ ├── main
│ └── java
│ │ └── influent
│ │ ├── exception
│ │ └── InfluentIOException.java
│ │ └── internal
│ │ ├── nio
│ │ ├── NioAttachment.java
│ │ ├── NioChannelConfig.java
│ │ ├── NioEventLoop.java
│ │ ├── NioEventLoopPool.java
│ │ ├── NioEventLoopTask.java
│ │ ├── NioRoundRobinEventLoopPool.java
│ │ ├── NioSelectionKey.java
│ │ ├── NioServerSocketChannel.java
│ │ ├── NioSingleThreadEventLoopPool.java
│ │ ├── NioTcpAcceptor.java
│ │ ├── NioTcpChannel.java
│ │ ├── NioTcpConfig.java
│ │ ├── NioTcpPlaintextChannel.java
│ │ ├── NioTcpTlsChannel.java
│ │ ├── NioTlsEngine.java
│ │ └── NioUdpChannel.java
│ │ └── util
│ │ ├── Exceptions.java
│ │ ├── Futures.java
│ │ ├── Inet4Network.java
│ │ ├── Inet6Network.java
│ │ ├── InetNetwork.java
│ │ └── ThreadSafeQueue.java
│ └── test
│ ├── resources
│ └── mockito-extensions
│ │ └── org.mockito.plugins.MockMaker
│ └── scala
│ └── influent
│ └── internal
│ ├── nio
│ ├── NioEventLoopSpec.scala
│ ├── NioEventLoopTaskSpec.scala
│ ├── NioRoundRobinEventLoopPoolSpec.scala
│ ├── NioSelectionKeySpec.scala
│ ├── NioServerSocketChannelSpec.scala
│ ├── NioSingleThreadEventLoopPoolSpec.scala
│ ├── NioTcpAcceptorSpec.scala
│ ├── NioTcpChannelSpec.scala
│ ├── NioTcpConfigSpec.scala
│ ├── NioTcpPlaintextChannelSpec.scala
│ └── NioUdpChannelSpec.scala
│ └── util
│ ├── FuturesSpec.scala
│ ├── Inet4NetworkSpec.scala
│ ├── Inet6NetworkSpec.scala
│ ├── InetNetworkSpec.scala
│ └── ThreadSafeQueueSpec.scala
├── project
├── build.properties
├── formatting-java.xml
└── plugins.sbt
└── publish.sh
/.dockerignore:
--------------------------------------------------------------------------------
1 | *
2 | !influent-java/src
3 | !influent-java-example/src
4 | !influent-transport/src
5 | !project/build.properties
6 | !project/plugins.sbt
7 | !build.sbt
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | target
3 | *.log
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: scala
2 |
3 | dist: xenial
4 |
5 | script:
6 | - sbt -J-Xmx3784m test
7 |
8 | jdk:
9 | - openjdk8
10 | - openjdk11
11 |
12 | sudo: true
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Influent
2 |
3 | [](https://travis-ci.org/okumin/influent)
4 | [](https://maven-badges.herokuapp.com/maven-central/com.okumin/influent-java)
5 | [](http://javadoc-badge.appspot.com/com.okumin/influent-java/index.html)
6 |
7 | Influent is a library to implement a Fluentd's forward server on the JVM.
8 |
9 | ## Protocol
10 |
11 | `influent.forward.ForwardServer` is almost compatible with [Forward Protocol Specification v1](https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1).
12 |
13 | This is the protocol for Fluentd's forward plugin.
14 |
15 | * [forward Input Plugin](http://docs.fluentd.org/articles/in_forward)
16 | * [forward Output Plugin](http://docs.fluentd.org/articles/out_forward)
17 |
18 | Influent is a server implementation, so behaves as like `in_forward`.
19 |
20 | There are some features that Influent does not support now.
21 | See also the `TODO` section.
22 |
23 | ## Advantages over Fluentd
24 |
25 | There are some reasons why Influent is developed.
26 |
27 | ### Java integration
28 |
29 | Influent enables users to handle Fluentd's events by Java.
30 | This means that they can use directly their domain logic written in Java or Java client APIs for some middleware.
31 |
32 | ### High performance
33 |
34 | JVM has high performance and Java has good thread API and IO API.
35 | Influent makes it possible to upgrade performance for some applications.
36 |
37 | ## TODO
38 |
39 | * handshake phase implementation
40 | * CompressedPackedForward mode implementation
41 | * TLS support
42 | * load test and performance improvement
43 | * Scala API
44 |
45 | ## Usage
46 |
47 | ### Dependency
48 |
49 | #### Maven
50 |
51 | ```
52 |
53 | com.okumin
54 | influent-java
55 | 0.3.0
56 |
57 | ```
58 |
59 | ### How to use
60 |
61 | Give `ForwardServer` the callback function that receives `EventStream`.
62 | If you want to write `EventStreams` to stdout,
63 |
64 | ```java
65 | // The callback function
66 | ForwardCallback callback = ForwardCallback.ofSyncConsumer(
67 | stream -> System.out.println(stream),
68 | Executors.newFixedThreadPool(1)
69 | );
70 |
71 | // Constructs a new server
72 | int port = 24224;
73 | ForwardServer server = new ForwardServer
74 | .Builder(callback)
75 | .localAddress(port)
76 | .build();
77 |
78 | // Starts the server on a new thread
79 | server.start();
80 |
81 | Thread.sleep(60 * 1000);
82 |
83 | // ForwardServer#shutdown returns a CompletableFuture
84 | CompletableFuture stopping = server.shutdown();
85 | // The future will be completed when the server is terminated
86 | stopping.get();
87 | ```
88 |
89 | Execute the above code, and send a message by `fluent-cat` command.
90 |
91 | ```
92 | $ echo '{"foo": "bar", "scores": [33, 4]}' | fluent-cat mofu
93 | ```
94 |
95 | The received `EventStream` is written to stdout.
96 |
97 | ```
98 | EventStream(Tag(mofu), [EventEntry(2016-11-13T13:10:59Z,{"foo":"bar","scores":[33,4]})])
99 | ```
100 |
--------------------------------------------------------------------------------
/bin/generate-ca.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -eu
2 |
3 | mkdir -p out
4 | chmod 700 out
5 |
6 | while true; do
7 | echo -n "Enter private key password for CA:"
8 | read -s password
9 | if [ "${#password}" -ge 4 ]; then
10 | echo
11 | break;
12 | else
13 | echo
14 | echo "Password must be at least 4 characters"
15 | continue
16 | fi
17 | done
18 |
19 | while true; do
20 | echo -n "Enter Country Name for CA [US]:"
21 | read country_name
22 | if [ -z "${country_name}" ]; then
23 | country_name=US
24 | break;
25 | elif [ "${#country_name}" -eq 2 ]; then
26 | break;
27 | else
28 | echo "Country Name must be 2 characters"
29 | continue
30 | fi
31 | done
32 |
33 | echo -n "Enter State or Province Name for CA [CA]:"
34 | read state_name
35 | if [ -z "${state_name}" ]; then
36 | state_name=CA
37 | fi
38 |
39 | echo -n "Enter Locality Name (eg, city) for CA [Mountain View]:"
40 | read locality_name
41 | if [ -z "${locality_name}" ]; then
42 | locality_name="Mountain View"
43 | fi
44 |
45 | echo -n "Organization Name (eg, company) for CA [Influent]:"
46 | read organization_name
47 | if [ -z "${organization_name}" ]; then
48 | organization_name="Influent"
49 | fi
50 |
51 | echo -n "Organizational Unit Name (eg, section) for CA []:"
52 | read organization_unit_name
53 | if [ -z "${organization_unit_name}" ]; then
54 | organization_unit_name=""
55 | fi
56 |
57 | echo -n "Common Name (e.g. server FQDN or YOUR name) [Influent CA]:"
58 | read common_name
59 | if [ -z "${common_name}" ]; then
60 | common_name="Influent CA"
61 | fi
62 |
63 | echo -n "Certificate valid days [36500]:"
64 | read validity_days
65 | if [ -z "${validity_days}" ]; then
66 | validity_days=36500
67 | fi
68 |
69 | openssl req \
70 | -new \
71 | -x509 \
72 | -newkey rsa:4096 \
73 | -out out/ca_cert.pem \
74 | -keyout out/ca_key.pem \
75 | -days ${validity_days} \
76 | -passout pass:${password} \
77 | -subj "/C=${country_name}/ST=${state_name}/L=${locality_name}/O=${organization_name}/OU=${organization_unit_name}/CN=${common_name}"
78 |
79 | chmod 600 out/ca_key.pem
80 |
81 | echo "out/ca_key.pem and out/ca_cert.pem are generated."
82 | echo "You can check ca_key.pem by 'openssl rsa -text -in out/ca_key.pem'"
83 | echo "You can check ca_cert.pem by 'openssl x509 -text -in out/ca_cert.pem'"
84 |
--------------------------------------------------------------------------------
/bin/generate-server-keystore.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -eu
2 |
3 | mkdir -p out
4 | chmod 700 out
5 |
6 | echo -n "Enter the certificate filename of CA [out/ca_cert.pem]:"
7 | read ca_cert_filename
8 | if [ -z "${ca_cert_filename}" ]; then
9 | ca_cert_filename="out/ca_cert.pem"
10 | fi
11 |
12 | echo -n "Enter the private key filename of CA [out/ca_key.pem]:"
13 | read ca_key_filename
14 | if [ -z "${ca_key_filename}" ]; then
15 | ca_key_filename="out/ca_key.pem"
16 | fi
17 |
18 | while true; do
19 | echo -n "Enter private key password of existent CA:"
20 | read -s ca_key_password
21 | if [ "${#ca_key_password}" -ge 4 ]; then
22 | echo
23 | break;
24 | else
25 | echo
26 | echo "Password must be at least 4 characters"
27 | continue
28 | fi
29 | done
30 |
31 | while true; do
32 | echo -n "Enter key store password for server:"
33 | read -s key_store_password
34 | if [ "${#key_store_password}" -ge 6 ]; then
35 | echo
36 | break;
37 | else
38 | echo
39 | echo "Password must be at least 6 characters"
40 | continue
41 | fi
42 | done
43 |
44 | while true; do
45 | echo -n "Enter private key password for server:"
46 | read -s key_password
47 | if [ "${#key_password}" -ge 6 ]; then
48 | echo
49 | break;
50 | else
51 | echo
52 | echo "Password must be at least 6 characters"
53 | continue
54 | fi
55 | done
56 |
57 | echo -n "Enter a name of the cert name of CA [influent-ca]:"
58 | read ca_cert_name
59 | if [ -z "${ca_cert_name}" ]; then
60 | ca_cert_name=influent-ca
61 | fi
62 |
63 | echo -n "Enter a name of the keypair name for server [influent-server]:"
64 | read keypair_name
65 | if [ -z "${keypair_name}" ]; then
66 | keypair_name=influent-server
67 | fi
68 |
69 | while true; do
70 | echo -n "Enter Country Name for server [US]:"
71 | read country_name
72 | if [ -z "${country_name}" ]; then
73 | country_name=US
74 | break;
75 | elif [ "${#country_name}" -eq 2 ]; then
76 | break;
77 | else
78 | echo "Country Name must be 2 characters"
79 | continue
80 | fi
81 | done
82 |
83 | echo -n "Enter State or Province Name for server [CA]:"
84 | read state_name
85 | if [ -z "${state_name}" ]; then
86 | state_name=CA
87 | fi
88 |
89 | echo -n "Enter Locality Name (eg, city) for server [Mountain View]:"
90 | read locality_name
91 | if [ -z "${locality_name}" ]; then
92 | locality_name="Mountain View"
93 | fi
94 |
95 | echo -n "Organization Name (eg, company) for server [Influent]:"
96 | read organization_name
97 | if [ -z "${organization_name}" ]; then
98 | organization_name="Influent"
99 | fi
100 |
101 | echo -n "Organizational Unit Name (eg, section) for server []:"
102 | read organization_unit_name
103 | if [ -z "${organization_unit_name}" ]; then
104 | organization_unit_name=""
105 | fi
106 |
107 | echo -n "Common Name (e.g. server host name):"
108 | read common_name
109 | if [ -z "${common_name}" ]; then
110 | common_name="Influent Server"
111 | fi
112 |
113 | echo -n "Certificate valid days [36500]:"
114 | read validity_days
115 | if [ -z "${validity_days}" ]; then
116 | validity_days=36500
117 | fi
118 |
119 | keytool \
120 | -genkeypair \
121 | -alias ${keypair_name} \
122 | -keyalg RSA \
123 | -keysize 4096 \
124 | -dname "CN=${common_name}, OU=${organization_unit_name}, O=${organization_name}, L=${locality_name}, ST=${state_name}, C=${country_name}" \
125 | -validity ${validity_days} \
126 | -keypass ${key_password} \
127 | -keystore out/influent-server.jks \
128 | -storepass ${key_store_password}
129 |
130 | chmod 600 out/influent-server.jks
131 |
132 | keytool \
133 | -certreq \
134 | -alias ${keypair_name} \
135 | -file out/influent-server.csr \
136 | -keypass ${key_password} \
137 | -keystore out/influent-server.jks \
138 | -storepass ${key_store_password}
139 |
140 | openssl \
141 | x509 \
142 | -req \
143 | -in out/influent-server.csr \
144 | -out out/influent-server.crt \
145 | -passin pass:${ca_key_password} \
146 | -days ${validity_days} \
147 | -CA ${ca_cert_filename} \
148 | -CAkey ${ca_key_filename} \
149 | -CAcreateserial
150 |
151 | keytool \
152 | -import \
153 | -alias ${ca_cert_name} \
154 | -file ${ca_cert_filename} \
155 | -keystore out/influent-server.jks \
156 | -storepass ${key_store_password} << EOF
157 | y
158 | EOF
159 |
160 | keytool \
161 | -import \
162 | -alias ${keypair_name} \
163 | -file out/influent-server.crt \
164 | -keypass ${key_password} \
165 | -keystore out/influent-server.jks \
166 | -storepass ${key_store_password}
167 |
168 | rm out/*.srl
169 | rm out/influent-server.csr
170 | rm out/influent-server.crt
171 |
172 | echo "out/influent-server.jks are generated."
173 | echo "You can check influent-server.jks by 'keytool -v -list -keystore out/influent-server.jks'"
174 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 |
2 | lazy val root = (project in file(".")).aggregate(influentJava, influentTransport)
3 |
4 | lazy val influentJava = (project in file("influent-java"))
5 | .settings(commonSettings: _*)
6 | .settings(javaSettings: _*)
7 | .settings(publishSettings: _*)
8 | .settings(
9 | name := "influent-java",
10 | libraryDependencies ++= Seq(
11 | "org.msgpack" % "msgpack-core" % "0.8.16"
12 | )
13 | )
14 | .dependsOn(influentTransport)
15 | .enablePlugins(AutomateHeaderPlugin)
16 |
17 | lazy val influentTransport = (project in file("influent-transport"))
18 | .settings(commonSettings: _*)
19 | .settings(javaSettings: _*)
20 | .settings(publishSettings: _*)
21 | .settings(
22 | name := "influent-transport"
23 | )
24 | .enablePlugins(AutomateHeaderPlugin)
25 |
26 | lazy val influentJavaExample = (project in file("influent-java-example"))
27 | .settings(commonSettings: _*)
28 | .settings(javaSettings: _*)
29 | .settings(
30 | name := "influent-java-example",
31 | libraryDependencies ++= Seq(
32 | "ch.qos.logback" % "logback-classic" % "1.2.3"
33 | ),
34 | assemblyJarName in assembly := "influent-java-example.jar"
35 | ).dependsOn(influentJava)
36 | .enablePlugins(AutomateHeaderPlugin)
37 |
38 | lazy val commonSettings = Seq(
39 | organization := "com.okumin",
40 | version := "0.4.0-M1",
41 | scalaVersion := "2.12.6",
42 | fork in Test := true,
43 | javacOptions in (Compile, doc) ++= Seq("-locale", "en_US"),
44 | javacOptions in (Compile, compile) ++= Seq("-encoding", "UTF-8"),
45 | libraryDependencies ++= Seq(
46 | "org.slf4j" % "slf4j-api" % "1.7.25",
47 | "org.mockito" % "mockito-core" % "2.28.2" % "test",
48 | "org.scalatest" %% "scalatest" % "3.0.5" % "test",
49 | "org.scalacheck" %% "scalacheck" % "1.14.0" % "test"
50 | ),
51 | // sbt-header settings
52 | organizationName := "okumin",
53 | startYear := Some(2016),
54 | licenses += ("Apache-2.0", new URL("https://www.apache.org/licenses/LICENSE-2.0.txt"))
55 | )
56 |
57 | lazy val javaSettings = Seq(
58 | crossPaths := false,
59 | autoScalaLibrary := false
60 | )
61 |
62 | lazy val publishSettings = Seq(
63 | publishMavenStyle := true,
64 | publishArtifact in Test := false,
65 | pomIncludeRepository := { _ => false },
66 | publishTo := {
67 | val nexus = "https://oss.sonatype.org/"
68 | if (isSnapshot.value)
69 | Some("snapshots" at nexus + "content/repositories/snapshots")
70 | else
71 | Some("releases" at nexus + "service/local/staging/deploy/maven2")
72 | },
73 | pomExtra := {
74 | https://github.com/okumin/influent
75 |
76 | git@github.com:okumin/influent.git
77 | scm:git:git@github.com:okumin/influent.git
78 |
79 |
80 |
81 | okumin
82 | okumin
83 | http://okumin.com/
84 |
85 |
86 | }
87 | )
88 |
--------------------------------------------------------------------------------
/example/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8 AS builder
2 |
3 | WORKDIR /influent-build
4 | COPY ./build.sbt ./build.sbt
5 | COPY ./project ./project
6 |
7 | RUN apt-get update \
8 | && apt-get install apt-transport-https \
9 | && echo "deb https://dl.bintray.com/sbt/debian /" > /etc/apt/sources.list.d/sbt.list \
10 | && apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823 \
11 | && apt-get update \
12 | && apt-get install sbt \
13 | && sbt update
14 |
15 | COPY ./ ./
16 | RUN sbt "project influentJavaExample" "assembly"
17 |
18 | FROM openjdk:8
19 |
20 | WORKDIR /influent
21 | COPY --from=builder /influent-build/influent-java-example/target/influent-java-example.jar ./influent-java-example.jar
22 | ENTRYPOINT ["java", "-classpath", "./influent-java-example.jar", "example.Print"]
23 |
--------------------------------------------------------------------------------
/example/ca_cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFPjCCAyYCCQD8CZ7WWS81TTANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJV
3 | UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxETAPBgNVBAoM
4 | CEluZmx1ZW50MRkwFwYDVQQDDBBJbmZsdWVudCBUZXN0IENBMCAXDTE4MTAxODE2
5 | MTExOFoYDzIxMTgwOTI0MTYxMTE4WjBgMQswCQYDVQQGEwJVUzELMAkGA1UECAwC
6 | Q0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxETAPBgNVBAoMCEluZmx1ZW50MRkw
7 | FwYDVQQDDBBJbmZsdWVudCBUZXN0IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
8 | MIICCgKCAgEAqQ3PUqOBynX1NR9QAAb1reS2qcTrobOkPUV11+tqS0Bpq256dMNr
9 | u3BFgoPmyO8kzR407z3CH9IXPknyKtTGXtYVtPuKg4L1x7NS6xlONMxlmMUw/RKr
10 | 0NGpayZhx9OoKM34GOsQhOktvbBbKMt/ann9uFSBIcMg2PDygHVogmaWGvQ5IZ2U
11 | wTb+bM9+rMrNo37EVLrppLo1VFgrEetXoWFQfkQIedQIa8IEWeLTCqHpBv5HcJqj
12 | YwwdhJTRrmLbWmwexJxTL/wLO/EfgEFTFJ9uSEhjYbDon8H4YMl5JFZPGo77aJ64
13 | yLTnXG0/UNd8MZO37yttHHIphRMAECPb6N4CN6WQ3uUp5inJBunQSAKnIyhzQOyw
14 | P46yLPUUP2QDvTjoRWJSAs/j5SZ8MFCZkBG1dD78JYxg+SwlAjtCda2iw99oRCkO
15 | DKMHH4gU9HCt5nkY9x3v6EXSdtWGRh99FEAad14TXdhpHo3LDfLubyDRwsKlQkig
16 | o9+M4SGualnMTMxmg4sLNFp/9XglTRkF5YfSUJe2NVL9kvYdc8oNLfL8Hhakm0Na
17 | ue3VoZDYKwPIJ1sc76oJpmuMH1bQEey5qMmAwehlQ3s4+P319WQZV87bgQ3YjqD3
18 | vrDT+RfF/6y+j5kUNpd2X6SBVrEtndMIai9Ki0MSQRCBcHtFJXSQN1MCAwEAATAN
19 | BgkqhkiG9w0BAQsFAAOCAgEASXOr8m4kefY7A9KQWKJESb4uw3mu32WLZvKAQpQ0
20 | 0mfUIa0Pm74aFrWJaFKaFOpicx7T8OJLRXQpg6qIkvGiTfsVCspAPLtpGfbNxNUG
21 | yPrcsUKNtQ9eF/El2eGRb9HctYJXoJQdEf1O3A1LmbzxNNaWmYvlj5d6Vfb91b1t
22 | +vLNY4AX/tWhXdTwd1slLXpVVaMBsoUEHpFVbzlwcQVZHi2kn7DGYggo7zzrg2el
23 | j5xwPQpzOVL/ZKy/SATVaf/nbSjzuZxTGwTp2Zfm9sV3tSvwsqcZaHJ0rlV+6qAk
24 | AVwa1eYSREDhlHr5/7bwfJAguyKbMpPzBgIV4yxZ85N2eWaSLlGw9J99Sfm7Hd6S
25 | B20pqBTDEGMjn4SM1n2FSviPdW014GvXnJ9mBAGE8HS+dEwfjxW2wupAUEKH9guR
26 | gww4BkViR6Soq4k4ZJE1clXsUgEmhiGTNnfOUeLlbzezUeWIu6ac/xMxo7e/W9qA
27 | j+NWFQtfm5hoD1nA88A56avulJ5aVO/28BbQdt84rLU4T/jYsu96DMJAQq9kXj9Z
28 | kc7laKcK0pRUH2Miz28D6wkFdqAUKeP7SegUwjvt0IcUqI+695zPDC2FLmTL3lXH
29 | btfib+MEDcdVQ0i4IFArjXQQb5Pfmo6MGSAS6NOWWd/O+yYn9h2DKFJT4SyAhOWO
30 | CbQ=
31 | -----END CERTIFICATE-----
32 |
--------------------------------------------------------------------------------
/example/influent-server.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/okumin/influent/143d5215315ae550a4d2c554b92c55e587122bf3/example/influent-server.jks
--------------------------------------------------------------------------------
/example/plain/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | influent-server:
4 | build:
5 | context: ../..
6 | dockerfile: example/Dockerfile
7 | environment:
8 | DURATION_SECONDS: '60'
9 | INFLUENT_LOG_LEVEL: INFO
10 | fluentd-agent:
11 | image: fluent/fluentd
12 | volumes:
13 | - ./fluent.conf:/fluentd/etc/fluent.conf
14 |
--------------------------------------------------------------------------------
/example/plain/fluent.conf:
--------------------------------------------------------------------------------
1 |
2 | @type dummy
3 | dummy {"hello":"world"}
4 | auto_increment_key count
5 | tag dummy
6 |
7 |
8 | @type copy
9 |
10 | @type forward
11 | flush_interval 1s
12 | require_ack_response true
13 |
14 |
15 | host influent-server
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/example/secure/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | influent-server:
4 | build:
5 | context: ../..
6 | dockerfile: example/Dockerfile
7 | environment:
8 | DURATION_SECONDS: '60'
9 | TLS_ENABLED: 'true'
10 | HANDSHAKE_ENABLED: 'false'
11 | INFLUENT_LOG_LEVEL: INFO
12 | volumes:
13 | - ../influent-server.jks:/influent/influent-server.jks
14 | fluentd-agent:
15 | image: fluent/fluentd
16 | volumes:
17 | - ./fluent.conf:/fluentd/etc/fluent.conf
18 | - ../ca_cert.pem:/fluentd/ca_cert.pem
19 |
--------------------------------------------------------------------------------
/example/secure/fluent.conf:
--------------------------------------------------------------------------------
1 |
2 | @type dummy
3 | dummy {"hello":"world"}
4 | auto_increment_key count
5 | tag dummy
6 |
7 |
8 | @type copy
9 |
10 | @type forward
11 | flush_interval 1s
12 | require_ack_response true
13 |
14 | transport tls
15 | tls_cert_path /fluentd/ca_cert.pem
16 |
17 |
18 | host influent-server
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/influent-java-example/src/main/java/example/Counter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package example;
18 |
19 | import influent.forward.ForwardCallback;
20 | import influent.forward.ForwardServer;
21 | import java.util.concurrent.CompletableFuture;
22 | import java.util.concurrent.atomic.AtomicLong;
23 | import org.slf4j.Logger;
24 | import org.slf4j.LoggerFactory;
25 |
26 | public class Counter {
27 | private static final class Reporter implements Runnable {
28 | private final AtomicLong counter = new AtomicLong();
29 |
30 | void add(final int up) {
31 | counter.addAndGet(up);
32 | }
33 |
34 | @Override
35 | public void run() {
36 | long lastChecked = System.currentTimeMillis();
37 | while (true) {
38 | try {
39 | Thread.sleep(100);
40 | } catch (final InterruptedException e) {
41 | break;
42 | }
43 | final long now = System.currentTimeMillis();
44 | if (now - lastChecked >= 1000) {
45 | lastChecked = now;
46 | final long current = counter.getAndSet(0);
47 | logger.info("{} requests/sec", current);
48 | }
49 | }
50 | }
51 | }
52 |
53 | private static final Logger logger = LoggerFactory.getLogger(Counter.class);
54 |
55 | public static void main(final String[] args) {
56 | final int workerPoolSize = Integer.parseInt(args[0]);
57 |
58 | final Reporter reporter = new Reporter();
59 |
60 | final ForwardCallback callback = ForwardCallback.of(stream -> {
61 | reporter.add(stream.getEntries().size());
62 | return CompletableFuture.completedFuture(null);
63 | });
64 |
65 | final ForwardServer server = new ForwardServer
66 | .Builder(callback)
67 | .workerPoolSize(workerPoolSize)
68 | .build();
69 | server.start();
70 | new Thread(reporter).start();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/influent-java-example/src/main/java/example/Environment.java:
--------------------------------------------------------------------------------
1 | package example;
2 |
3 | class Environment {
4 | private Environment() {
5 | throw new AssertionError();
6 | }
7 |
8 | static String get(final String name, final String defaultValue) {
9 | final String value = System.getenv(name);
10 | return value != null ? value : defaultValue;
11 | }
12 |
13 | static long getLong(final String name, final long defaultValue) {
14 | final String value = System.getenv(name);
15 | return value != null ? Long.valueOf(value) : defaultValue;
16 | }
17 |
18 | static boolean getBoolean(final String name, final boolean defaultValue) {
19 | final String value = System.getenv(name);
20 | return value != null ? Boolean.valueOf(value) : defaultValue;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/influent-java-example/src/main/java/example/Print.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package example;
18 |
19 | import influent.forward.ForwardCallback;
20 | import influent.forward.ForwardSecurity;
21 | import influent.forward.ForwardServer;
22 | import java.util.concurrent.Executors;
23 | import org.slf4j.Logger;
24 | import org.slf4j.LoggerFactory;
25 |
26 | public final class Print {
27 | private static final Logger logger = LoggerFactory.getLogger(Print.class);
28 |
29 | public static void main(final String[] args) throws Exception {
30 | final long durationSeconds = Environment.getLong("DURATION_SECONDS", 60);
31 | final boolean tlsEnabled = Environment.getBoolean("TLS_ENABLED", false);
32 | final String tlsKeystorePath = Environment.get("TLS_KEYSTORE_PATH", "/influent/influent-server.jks");
33 | final String tlsKeystorePassword = Environment.get("TLS_KEYSTORE_PASSWORD", "password");
34 | final String tlsKeyPassword = Environment.get("TLS_KEY_PASSWORD", "password");
35 | final boolean handshakeEnabled = Environment.getBoolean("HANDSHAKE_ENABLED", false);
36 | final String handshakeHostname = Environment.get("HANDSHAKE_HOSTNAME", "influent-server");
37 | final String handshakeSharedKey = Environment.get("HANDSHAKE_SHARED_KEY", "shared_key");
38 |
39 | final ForwardCallback callback = ForwardCallback.ofSyncConsumer(
40 | stream -> logger.info(stream.toString()),
41 | Executors.newWorkStealingPool()
42 | );
43 |
44 | final ForwardServer.Builder builder = new ForwardServer.Builder(callback);
45 | if (tlsEnabled) {
46 | builder
47 | .sslEnabled(true)
48 | .keystorePath(tlsKeystorePath)
49 | .keystorePassword(tlsKeystorePassword)
50 | .keyPassword(tlsKeyPassword);
51 | }
52 | if (handshakeEnabled) {
53 | final ForwardSecurity security = new ForwardSecurity
54 | .Builder()
55 | .selfHostname(handshakeHostname)
56 | .sharedKey(handshakeSharedKey)
57 | .build();
58 | builder.security(security);
59 | }
60 |
61 | final ForwardServer server = builder.build();
62 | server.start();
63 |
64 | // ForwardServer#start returns immediately
65 | Thread.sleep(durationSeconds * 1000);
66 |
67 | server.shutdown().get();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/influent-java-example/src/main/java/example/SecurityCounter.java:
--------------------------------------------------------------------------------
1 | package example;
2 |
3 | import influent.forward.ForwardCallback;
4 | import influent.forward.ForwardSecurity;
5 | import influent.forward.ForwardServer;
6 | import java.util.concurrent.CompletableFuture;
7 | import java.util.concurrent.atomic.AtomicLong;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | public class SecurityCounter {
12 | private static final class Reporter implements Runnable {
13 | private final AtomicLong counter = new AtomicLong();
14 |
15 | void add(final int up) {
16 | counter.addAndGet(up);
17 | }
18 |
19 | @Override
20 | public void run() {
21 | long lastChecked = System.currentTimeMillis();
22 | while (true) {
23 | try {
24 | Thread.sleep(100);
25 | } catch (final InterruptedException e) {
26 | break;
27 | }
28 | final long now = System.currentTimeMillis();
29 | if (now - lastChecked >= 1000) {
30 | lastChecked = now;
31 | final long current = counter.getAndSet(0);
32 | logger.info("{} requests/sec", current);
33 | }
34 | }
35 | }
36 | }
37 |
38 | private static final Logger logger = LoggerFactory.getLogger(Counter.class);
39 |
40 | public static void main(final String[] args) {
41 | final int workerPoolSize = Integer.parseInt(args[0]);
42 |
43 | final Reporter reporter = new Reporter();
44 |
45 | final ForwardCallback callback = ForwardCallback.of(stream -> {
46 | reporter.add(stream.getEntries().size());
47 | return CompletableFuture.completedFuture(null);
48 | });
49 |
50 | final ForwardSecurity security = new ForwardSecurity
51 | .Builder()
52 | .selfHostname("input.testing.local")
53 | .sharedKey("secure_communication_is_awesome")
54 | .build();
55 | final ForwardServer server = new ForwardServer
56 | .Builder(callback)
57 | .workerPoolSize(workerPoolSize)
58 | .security(security)
59 | .build();
60 | server.start();
61 | new Thread(reporter).start();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/influent-java-example/src/main/java/example/TLSCounter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package example;
18 |
19 | import influent.forward.ForwardCallback;
20 | import influent.forward.ForwardServer;
21 | import java.util.concurrent.CompletableFuture;
22 | import java.util.concurrent.atomic.AtomicLong;
23 | import org.slf4j.Logger;
24 | import org.slf4j.LoggerFactory;
25 |
26 | public class TLSCounter {
27 | private static final class Reporter implements Runnable {
28 | private final AtomicLong counter = new AtomicLong();
29 |
30 | void add(final int up) {
31 | counter.addAndGet(up);
32 | }
33 |
34 | @Override
35 | public void run() {
36 | long lastChecked = System.currentTimeMillis();
37 | while (true) {
38 | try {
39 | Thread.sleep(100);
40 | } catch (final InterruptedException e) {
41 | break;
42 | }
43 | final long now = System.currentTimeMillis();
44 | if (now - lastChecked >= 1000) {
45 | lastChecked = now;
46 | final long current = counter.getAndSet(0);
47 | logger.info("{} requests/sec", current);
48 | }
49 | }
50 | }
51 | }
52 |
53 | private static final Logger logger = LoggerFactory.getLogger(TLSCounter.class);
54 |
55 | public static void main(final String[] args) {
56 | if (args.length != 4) {
57 | usage();
58 | return;
59 | }
60 | final int workerPoolSize = Integer.parseInt(args[0]);
61 | final String keystorePath = args[1];
62 | final String keystorePassword = args[2];
63 | final String keyPassword = args[3];
64 |
65 | final Reporter reporter = new Reporter();
66 |
67 | final ForwardCallback callback = ForwardCallback.of(stream -> {
68 | reporter.add(stream.getEntries().size());
69 | return CompletableFuture.completedFuture(null);
70 | });
71 |
72 | final ForwardServer server = new ForwardServer
73 | .Builder(callback)
74 | .workerPoolSize(workerPoolSize)
75 | .sslEnabled(true)
76 | .tlsVersions("TLSv1.2")
77 | .keystorePath(keystorePath)
78 | .keystorePassword(keystorePassword)
79 | .keyPassword(keyPassword)
80 | .build();
81 | server.start();
82 | new Thread(reporter).start();
83 | }
84 |
85 | private static void usage() {
86 | System.out.println("Must specify 4 arguments.");
87 | System.out.println();
88 | System.out.println("e.g. 4 /path/to/server.jks keystorePassowrd keyPassword");
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/influent-java-example/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/EventEntry.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent;
18 |
19 | import java.time.Instant;
20 | import java.util.Objects;
21 | import org.msgpack.value.ImmutableMapValue;
22 |
23 | /**
24 | * An event entry.
25 | *
26 | *
This is not immutable when the {@code record} has mutable {@code org.msgpack.value.Value}. But
27 | * instances of {@code EventEntry} that this library returns are guaranteed immutable.
28 | */
29 | public final class EventEntry {
30 | private final Instant time;
31 | private final ImmutableMapValue record;
32 |
33 | private EventEntry(final Instant time, final ImmutableMapValue record) {
34 | this.time = Objects.requireNonNull(time);
35 | this.record = Objects.requireNonNull(record);
36 | }
37 |
38 | /**
39 | * Creates an {@code EventEntry}.
40 | *
41 | * @param time the event time
42 | * @param record the record
43 | * @return the new {@code EventEntry}
44 | * @throws NullPointerException if the time or the record are null
45 | */
46 | public static EventEntry of(final Instant time, final ImmutableMapValue record) {
47 | return new EventEntry(time, record);
48 | }
49 |
50 | /** @return the event time */
51 | public Instant getTime() {
52 | return time;
53 | }
54 |
55 | /** @return the record */
56 | public ImmutableMapValue getRecord() {
57 | return record;
58 | }
59 |
60 | /** {@inheritDoc} */
61 | @Override
62 | public boolean equals(final Object o) {
63 | if (this == o) {
64 | return true;
65 | }
66 | if (o == null || getClass() != o.getClass()) {
67 | return false;
68 | }
69 | final EventEntry that = (EventEntry) o;
70 | return Objects.equals(getTime(), that.getTime())
71 | && Objects.equals(getRecord(), that.getRecord());
72 | }
73 |
74 | /** {@inheritDoc} */
75 | @Override
76 | public int hashCode() {
77 | return Objects.hash(getTime(), getRecord());
78 | }
79 |
80 | /** {@inheritDoc} */
81 | @Override
82 | public String toString() {
83 | return "EventEntry(" + getTime() + ',' + getRecord() + ')';
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/EventStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent;
18 |
19 | import java.util.Collections;
20 | import java.util.List;
21 | import java.util.Objects;
22 |
23 | /**
24 | * An event stream.
25 | *
26 | *
This is not immutable when the origin of {@code entries} is mutated. But instances of {@code
27 | * EventStream} that this library returns are guaranteed immutable.
28 | */
29 | public final class EventStream {
30 | private final Tag tag;
31 | private final List entries;
32 |
33 | private EventStream(final Tag tag, final List entries) {
34 | this.tag = Objects.requireNonNull(tag);
35 | this.entries = Collections.unmodifiableList(Objects.requireNonNull(entries));
36 | }
37 |
38 | /**
39 | * Creates an {@code EventStream}.
40 | *
41 | * @param tag the tag
42 | * @param entries the entries
43 | * @return the new {@code EventStream}
44 | * @throws NullPointerException if the tag or the entries are null
45 | */
46 | public static EventStream of(final Tag tag, final List entries) {
47 | return new EventStream(tag, entries);
48 | }
49 |
50 | /** @return the tag */
51 | public Tag getTag() {
52 | return tag;
53 | }
54 |
55 | /** @return the unmodifiable list of {@code EventEntry} */
56 | public List getEntries() {
57 | return entries;
58 | }
59 |
60 | /** {@inheritDoc} */
61 | @Override
62 | public boolean equals(final Object o) {
63 | if (this == o) {
64 | return true;
65 | }
66 | if (o == null || getClass() != o.getClass()) {
67 | return false;
68 | }
69 | final EventStream that = (EventStream) o;
70 | return Objects.equals(getTag(), that.getTag())
71 | && Objects.equals(getEntries(), that.getEntries());
72 | }
73 |
74 | /** {@inheritDoc} */
75 | @Override
76 | public int hashCode() {
77 | return Objects.hash(getTag(), getEntries());
78 | }
79 |
80 | /** {@inheritDoc} */
81 | @Override
82 | public String toString() {
83 | return "EventStream(" + tag + ", " + entries + ")";
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/Tag.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent;
18 |
19 | import java.util.Objects;
20 |
21 | /**
22 | * A tag of fluentd's event.
23 | *
24 | *
Instances of {@code Tag} are immutable.
25 | */
26 | public final class Tag implements Comparable {
27 | private final String name;
28 |
29 | private Tag(final String name) {
30 | this.name = Objects.requireNonNull(name);
31 | }
32 |
33 | /**
34 | * Creates a {@code Tag}.
35 | *
36 | * @param name the tag name
37 | * @return the tag with the given name
38 | * @throws NullPointerException if the name is null
39 | */
40 | public static Tag of(final String name) {
41 | return new Tag(name);
42 | }
43 |
44 | /** @return the tag name */
45 | public String getName() {
46 | return name;
47 | }
48 |
49 | /**
50 | * Compares two tags lexicographically.
51 | *
52 | * @param o the {@code Tag} to be compared
53 | * @return the value 0 if the name of this tag is equal to that of the argument a value less than
54 | * 0 if the name of this tag is lexicographically less than that of the argument a value
55 | * greater than 0 if the name of this tag is lexicographically greater than that of the
56 | * argument
57 | */
58 | @Override
59 | public int compareTo(final Tag o) {
60 | return getName().compareTo(o.getName());
61 | }
62 |
63 | /** {@inheritDoc} */
64 | @Override
65 | public boolean equals(final Object o) {
66 | if (this == o) {
67 | return true;
68 | }
69 | if (o == null || getClass() != o.getClass()) {
70 | return false;
71 | }
72 | final Tag tag = (Tag) o;
73 | return Objects.equals(getName(), tag.getName());
74 | }
75 |
76 | /** {@inheritDoc} */
77 | @Override
78 | public int hashCode() {
79 | return Objects.hash(getName());
80 | }
81 |
82 | /** {@inheritDoc} */
83 | @Override
84 | public String toString() {
85 | return "Tag(" + getName() + ')';
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/CheckPingResult.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | public class CheckPingResult {
20 | private final boolean succeeded;
21 | private final String sharedKeySalt;
22 | private final String sharedKey;
23 | private final String reason;
24 |
25 | public static CheckPingResult success(String sharedKeySalt, String sharedKey) {
26 | return new CheckPingResult(true, sharedKeySalt, sharedKey, null);
27 | }
28 |
29 | public static CheckPingResult failure(String reason) {
30 | return new CheckPingResult(false, null, null, reason);
31 | }
32 |
33 | public CheckPingResult(boolean succeeded, String sharedKeySalt, String sharedKey, String reason) {
34 | this.succeeded = succeeded;
35 | this.sharedKeySalt = sharedKeySalt;
36 | this.sharedKey = sharedKey;
37 | this.reason = reason;
38 | }
39 |
40 | public boolean isSucceeded() {
41 | return succeeded;
42 | }
43 |
44 | public String getSharedKeySalt() {
45 | return sharedKeySalt;
46 | }
47 |
48 | public String getSharedKey() {
49 | return sharedKey;
50 | }
51 |
52 | public String getReason() {
53 | return reason;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/ForwardCallback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import java.util.concurrent.CompletableFuture;
20 | import java.util.concurrent.Executor;
21 | import java.util.function.Consumer;
22 | import java.util.function.Function;
23 |
24 | import influent.EventStream;
25 |
26 | /** The callback function that consumes {@code EventStreams}. */
27 | @FunctionalInterface
28 | public interface ForwardCallback {
29 | /**
30 | * Creates the {@code ForwardCallback}. See also ForwardCallback#consume.
31 | *
32 | * @param consumer the callback function
33 | * @return the {@code ForwardCallback}
34 | */
35 | static ForwardCallback of(final Function> consumer) {
36 | return consumer::apply;
37 | }
38 |
39 | /**
40 | * Creates a {@code ForwardCallback} from the synchronous {@code Consumer} and the {@code
41 | * Executor}.
42 | *
43 | * @param consumer the synchronous {@code Consumer}
44 | * @param executor the {@code Executor} that executes {@code consumer}
45 | * @return the {@code ForwardCallback}
46 | */
47 | static ForwardCallback ofSyncConsumer(
48 | final Consumer consumer, final Executor executor) {
49 | return stream -> CompletableFuture.runAsync(() -> consumer.accept(stream), executor);
50 | }
51 |
52 | /**
53 | * Consumes an {@code EventStream}.
54 | *
55 | *
{@code ForwardCallback#consume} must not be blocked since it is invoked on an event loop
56 | * thread. If there are some IO operation or a CPU intensive processing, those must be executed on
57 | * the another thread.
58 | *
59 | *
This method receives an {@code EventStream} and returns a {@code CompletableFuture}. When
60 | * the {@code CompletableFuture} succeeds, Influent assumes that the {@code EventStream} is
61 | * completely consumed and may send an ack response to the client.
62 | *
63 | *
When the {@code CompletableFuture} succeeds, Influent never sends an ack.
64 | *
65 | * @param stream the {@code EventStream}
66 | * @return the result of this consumption
67 | */
68 | CompletableFuture consume(EventStream stream);
69 | }
70 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/ForwardClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Collections;
21 | import java.util.List;
22 |
23 | /** Client ip/network authentication & per_host shared key */
24 | public class ForwardClient {
25 | private String host = null;
26 | private String network = null;
27 | private String sharedKey = null;
28 | private List usernames = new ArrayList<>();
29 |
30 | public ForwardClient(String host, String network, String sharedKey, List usernames) {
31 | this.host = host;
32 | this.network = network;
33 | this.sharedKey = sharedKey;
34 | this.usernames = usernames;
35 | }
36 |
37 | public String getHost() {
38 | return host;
39 | }
40 |
41 | public String getNetwork() {
42 | return network;
43 | }
44 |
45 | public String getSharedKey() {
46 | return sharedKey;
47 | }
48 |
49 | public List getUsernames() {
50 | return usernames;
51 | }
52 |
53 | public static class Builder {
54 | private String host = null;
55 | private String network = null;
56 | private String sharedKey = null;
57 | private List usernames = new ArrayList<>();
58 |
59 | private Builder() {}
60 |
61 | /**
62 | * Create new ForwardClient.Builder with given host
63 | *
64 | * @param host The IP address or host name of the client
65 | * @return new builder
66 | */
67 | public static Builder ofHost(String host) {
68 | Builder builder = new Builder();
69 | builder.host = host;
70 | return builder;
71 | }
72 |
73 | /**
74 | * Create new ForwardClient.Builder with given network
75 | *
76 | * @param network Network address specification
77 | * @return new builder
78 | */
79 | public static Builder ofNetwork(String network) {
80 | Builder builder = new Builder();
81 | builder.network = network;
82 | return builder;
83 | }
84 |
85 | /**
86 | * Set shared key per client
87 | *
88 | * @param sharedKey Shared key per client
89 | * @return this builder
90 | */
91 | public Builder sharedKey(String sharedKey) {
92 | this.sharedKey = sharedKey;
93 | return this;
94 | }
95 |
96 | /**
97 | * Set usernames to authenticate client
98 | *
99 | * @param usernames Usernames to authenticate client
100 | * @return this builder
101 | */
102 | public Builder usernames(String... usernames) {
103 | Collections.addAll(this.usernames, usernames);
104 | return this;
105 | }
106 |
107 | public ForwardClient build() {
108 | return new ForwardClient(host, network, sharedKey, usernames);
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/ForwardClientNode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import influent.internal.util.InetNetwork;
20 | import java.net.InetAddress;
21 | import java.net.UnknownHostException;
22 | import java.util.List;
23 |
24 | public class ForwardClientNode {
25 | private InetAddress sourceAddress = null;
26 | private InetNetwork network = null;
27 | private String sharedKey = null;
28 | private List usernames = null;
29 |
30 | public ForwardClientNode(ForwardClient client, String globalSharedKey) {
31 | if (client.getHost() != null) {
32 | try {
33 | sourceAddress = InetAddress.getByName(client.getHost());
34 | } catch (UnknownHostException e) {
35 | e.printStackTrace();
36 | // TODO throw exception
37 | }
38 | }
39 | if (client.getNetwork() != null) {
40 | network = InetNetwork.getBySpec(client.getNetwork());
41 | }
42 | sharedKey = client.getSharedKey() == null ? globalSharedKey : client.getSharedKey();
43 | usernames = client.getUsernames();
44 | }
45 |
46 | public boolean isMatched(InetAddress remoteAddress) {
47 | if (sourceAddress != null) {
48 | return sourceAddress.equals(remoteAddress);
49 | } else {
50 | return network.contains(remoteAddress);
51 | }
52 | }
53 |
54 | public String getSharedKey() {
55 | return sharedKey;
56 | }
57 |
58 | public List getUsernames() {
59 | return usernames;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/ForwardClientUser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | public class ForwardClientUser {
20 | private final String username;
21 | private final String password;
22 |
23 | public ForwardClientUser(final String username, final String password) {
24 | this.username = username;
25 | this.password = password;
26 | }
27 |
28 | public String getUsername() {
29 | return username;
30 | }
31 |
32 | public String getPassword() {
33 | return password;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/ForwardOption.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import java.util.Objects;
20 | import java.util.Optional;
21 |
22 | final class ForwardOption {
23 | private static final ForwardOption EMPTY = new ForwardOption(null, null);
24 |
25 | private final String chunk;
26 | private final String compressed;
27 |
28 | private ForwardOption(final String chunk, final String compressed) {
29 | this.chunk = chunk;
30 | this.compressed = compressed;
31 | }
32 |
33 | static ForwardOption of(final String chunk, final String compressed) {
34 | return new ForwardOption(chunk, compressed);
35 | }
36 |
37 | static ForwardOption empty() {
38 | return EMPTY;
39 | }
40 |
41 | Optional getChunk() {
42 | return Optional.ofNullable(chunk);
43 | }
44 |
45 | Optional getCompressed() {
46 | return Optional.ofNullable(compressed);
47 | }
48 |
49 | @Override
50 | public boolean equals(final Object o) {
51 | if (this == o) {
52 | return true;
53 | }
54 | if (o == null || getClass() != o.getClass()) {
55 | return false;
56 | }
57 | final ForwardOption that = (ForwardOption) o;
58 | return Objects.equals(getChunk(), that.getChunk())
59 | && Objects.equals(getCompressed(), that.getCompressed());
60 | }
61 |
62 | @Override
63 | public int hashCode() {
64 | return Objects.hash(getChunk(), getCompressed());
65 | }
66 |
67 | @Override
68 | public String toString() {
69 | return "ForwardOption(" + getChunk() + "," + getCompressed() + ")";
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/ForwardRequest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import java.util.Objects;
20 | import influent.EventStream;
21 |
22 | final class ForwardRequest {
23 | private final EventStream stream;
24 | private final ForwardOption option;
25 |
26 | private ForwardRequest(final EventStream stream, final ForwardOption option) {
27 | this.stream = stream;
28 | this.option = option;
29 | }
30 |
31 | static ForwardRequest of(final EventStream stream, final ForwardOption option) {
32 | return new ForwardRequest(stream, option);
33 | }
34 |
35 | EventStream getStream() {
36 | return stream;
37 | }
38 |
39 | ForwardOption getOption() {
40 | return option;
41 | }
42 |
43 | @Override
44 | public boolean equals(final Object o) {
45 | if (this == o) {
46 | return true;
47 | }
48 | if (o == null || getClass() != o.getClass()) {
49 | return false;
50 | }
51 | final ForwardRequest that = (ForwardRequest) o;
52 | return Objects.equals(getStream(), that.getStream())
53 | && Objects.equals(getOption(), that.getOption());
54 | }
55 |
56 | @Override
57 | public int hashCode() {
58 | return Objects.hash(getStream(), getOption());
59 | }
60 |
61 | @Override
62 | public String toString() {
63 | return "ForwardRequest(" + getStream() + "," + getOption() + ")";
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/ForwardSecurity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import java.net.InetAddress;
20 | import java.util.ArrayList;
21 | import java.util.HashMap;
22 | import java.util.List;
23 | import java.util.Map;
24 | import java.util.Optional;
25 | import java.util.stream.Collectors;
26 |
27 | public class ForwardSecurity {
28 | private String selfHostname = null;
29 | private String sharedKey = null;
30 | private boolean userAuthEnabled = false;
31 | private boolean anonymousSourceAllowed = true;
32 | private boolean enabled;
33 |
34 | private List clients = new ArrayList<>();
35 | private List users = new ArrayList<>();
36 | private List nodes = new ArrayList<>();
37 |
38 | public ForwardSecurity() {
39 | this.enabled = false;
40 | }
41 |
42 | public ForwardSecurity(
43 | String selfHostname,
44 | String sharedKey,
45 | boolean userAuthEnabled,
46 | boolean anonymousSourceAllowed,
47 | List clients,
48 | List users) {
49 | this.selfHostname = selfHostname;
50 | this.sharedKey = sharedKey;
51 | this.userAuthEnabled = userAuthEnabled;
52 | this.anonymousSourceAllowed = anonymousSourceAllowed;
53 | this.clients.addAll(clients);
54 | this.users.addAll(users);
55 | this.enabled = true;
56 |
57 | for (ForwardClient client : this.clients) {
58 | nodes.add(new ForwardClientNode(client, this.sharedKey));
59 | }
60 | }
61 |
62 | public boolean isEnabled() {
63 | return enabled;
64 | }
65 |
66 | public boolean isAnonymousSourceAllowed() {
67 | return anonymousSourceAllowed;
68 | }
69 |
70 | public boolean isUserAuthEnabled() {
71 | return userAuthEnabled;
72 | }
73 |
74 | public Optional findNode(InetAddress remoteAddress) {
75 | return nodes.stream().filter(n -> n.isMatched(remoteAddress)).findFirst();
76 | }
77 |
78 | public String getSelfHostname() {
79 | return selfHostname;
80 | }
81 |
82 | public String getSharedKey() {
83 | return sharedKey;
84 | }
85 |
86 | public List findAuthenticateUsers(ForwardClientNode node, String username) {
87 | if (node == null || node.getUsernames().isEmpty()) {
88 | return users
89 | .stream()
90 | .filter(user -> user.getUsername().equals(username))
91 | .collect(Collectors.toList());
92 | } else {
93 | return users
94 | .stream()
95 | .filter(
96 | user -> node.getUsernames().contains(username) && user.getUsername().equals(username))
97 | .collect(Collectors.toList());
98 | }
99 | }
100 |
101 | public static class Builder {
102 | private String selfHostname = null;
103 | private String sharedKey = null;
104 | private boolean userAuthEnabled = false;
105 | private boolean anonymousSourceAllowed = true;
106 |
107 | private List clients = new ArrayList<>();
108 | private List users = new ArrayList<>();
109 |
110 | /**
111 | * Set the hostname
112 | *
113 | * @param selfHostname The hostname
114 | * @return this builder
115 | */
116 | public Builder selfHostname(String selfHostname) {
117 | this.selfHostname = selfHostname;
118 | return this;
119 | }
120 |
121 | /**
122 | * Set shared key for authentication
123 | *
124 | * @param sharedKey Shared key for authentication
125 | * @return this builder
126 | */
127 | public Builder sharedKey(String sharedKey) {
128 | this.sharedKey = sharedKey;
129 | return this;
130 | }
131 |
132 | /**
133 | * Use user base authentication or not
134 | *
135 | * @param userAuthEnabled If true, use user based authentication
136 | * @return this builder
137 | */
138 | public Builder enableUserAuth(boolean userAuthEnabled) {
139 | this.userAuthEnabled = userAuthEnabled;
140 | return this;
141 | }
142 |
143 | /**
144 | * Allow anonymous source or not. Clients are required if not allowed anonymous source.
145 | *
146 | * @param anonymousSourceAllowed If true, allow anonymous source. Otherwise clients are
147 | * required.
148 | * @return this builder
149 | */
150 | public Builder allowAnonymousSource(boolean anonymousSourceAllowed) {
151 | this.anonymousSourceAllowed = anonymousSourceAllowed;
152 | return this;
153 | }
154 |
155 | /**
156 | * Add username and passowrd for user based authentication
157 | *
158 | * @param username The username for authentication
159 | * @param password The password for authentication
160 | * @return this builder
161 | */
162 | public Builder addUser(final String username, final String password) {
163 | users.add(new ForwardClientUser(username, password));
164 | return this;
165 | }
166 |
167 | /**
168 | * Add client ip/network authentication & per_host shared key
169 | *
170 | * @param client The ForwardClient
171 | * @return this builder
172 | */
173 | public Builder addClient(ForwardClient client) {
174 | clients.add(client);
175 | return this;
176 | }
177 |
178 | public ForwardSecurity build() {
179 | // TODO Check required parameters
180 | return new ForwardSecurity(
181 | selfHostname, sharedKey, userAuthEnabled, anonymousSourceAllowed, clients, users);
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/MsgPackPingDecoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import org.msgpack.value.ImmutableArrayValue;
20 | import org.msgpack.value.ImmutableValue;
21 | import org.msgpack.value.Value;
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 | import java.security.MessageDigest;
25 | import java.security.NoSuchAlgorithmException;
26 |
27 | final class MsgPackPingDecoder {
28 | private static final Logger logger = LoggerFactory.getLogger(MsgPackPingDecoder.class);
29 |
30 | private final ForwardSecurity security;
31 | private final ForwardClientNode node;
32 | private final byte[] nonce;
33 | private final byte[] userAuth;
34 |
35 | public MsgPackPingDecoder(
36 | ForwardSecurity security, ForwardClientNode node, byte[] nonce, byte[] userAuth) {
37 | this.security = security;
38 | this.node = node;
39 | this.nonce = nonce;
40 | this.userAuth = userAuth;
41 | }
42 |
43 | public CheckPingResult decode(ImmutableValue value) {
44 | logger.debug("decoding ping {}", value);
45 | if (!value.isArrayValue()) {
46 | error("A ping message must be an array", value);
47 | }
48 | ImmutableArrayValue arrayValue = value.asArrayValue();
49 | if (arrayValue.size() != 6) {
50 | error("A ping message must have 6 elements", value);
51 | }
52 | String ping = decodeString(arrayValue.get(0));
53 | if (!ping.equals(("PING"))) {
54 | error("Invalid ping message", value);
55 | }
56 | String clientHostname = decodeString(arrayValue.get(1));
57 | String sharedKeySalt = decodeString(arrayValue.get(2)); // TODO Support both String and byte[]
58 | String sharedKeyHexDigest = decodeString(arrayValue.get(3));
59 | String username = decodeString(arrayValue.get(4));
60 | String passwordDigest = decodeString(arrayValue.get(5));
61 |
62 | if (node == null && !security.isAnonymousSourceAllowed()) {
63 | // FIXME add remote address to message
64 | String message = "Anonymous client disallowed.";
65 | logger.warn(message);
66 | return CheckPingResult.failure(message);
67 | }
68 |
69 | String sharedKey = null;
70 | try {
71 | sharedKey = node != null ? node.getSharedKey() : security.getSharedKey();
72 | MessageDigest md = MessageDigest.getInstance("SHA-512");
73 | md.update(sharedKeySalt.getBytes());
74 | md.update(clientHostname.getBytes());
75 | md.update(nonce);
76 | md.update(sharedKey.getBytes());
77 | String serverSideDigest = generateHexString(md.digest());
78 | if (!sharedKeyHexDigest.equals(serverSideDigest)) {
79 | // FIXME Add remote address to log
80 | logger.warn("Shared key mismatch: {}", clientHostname);
81 | return CheckPingResult.failure("Shared key mismatch");
82 | }
83 |
84 | if (security.isUserAuthEnabled()) {
85 | boolean userAuthenticationSucceeded =
86 | security
87 | .findAuthenticateUsers(node, username)
88 | .stream()
89 | .anyMatch(
90 | user -> {
91 | md.reset();
92 | md.update(userAuth);
93 | md.update(username.getBytes());
94 | md.update(user.getPassword().getBytes());
95 | String serverSidePasswordDigest = generateHexString(md.digest());
96 | return passwordDigest.equals(serverSidePasswordDigest);
97 | });
98 | if (!userAuthenticationSucceeded) {
99 | // FIXME Add remote address to log
100 | logger.info("Authentication failed: hostname={}, username={}", clientHostname, username);
101 | return CheckPingResult.failure("username/password mismatch");
102 | }
103 | }
104 | } catch (NoSuchAlgorithmException e) {
105 | error(e.getMessage(), value, e);
106 | }
107 |
108 | return CheckPingResult.success(sharedKeySalt, sharedKey);
109 | }
110 |
111 | private String decodeString(final Value value) {
112 | return value == null ? null : value.asStringValue().asString();
113 | }
114 |
115 | private byte[] decodeByteArray(final Value value) {
116 | return value == null ? null : value.asBinaryValue().asByteArray();
117 | }
118 |
119 | private String generateHexString(final byte[] digest) {
120 | StringBuilder sb = new StringBuilder();
121 | for (byte b : digest) {
122 | sb.append(String.format("%02x", b));
123 | }
124 | return sb.toString();
125 | }
126 |
127 | private IllegalArgumentException error(final String message, final Value value) {
128 | throw new IllegalArgumentException(message + " value = " + value);
129 | }
130 |
131 | private IllegalArgumentException error(
132 | final String message, final Value value, final Throwable cause) {
133 | throw new IllegalArgumentException(message + " value = " + value, cause);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/NioForwardServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import influent.internal.nio.NioChannelConfig;
20 | import influent.internal.nio.NioEventLoop;
21 | import influent.internal.nio.NioEventLoopPool;
22 | import influent.internal.nio.NioTcpAcceptor;
23 | import influent.internal.nio.NioTcpConfig;
24 | import influent.internal.nio.NioTcpPlaintextChannel;
25 | import influent.internal.nio.NioTcpTlsChannel;
26 | import influent.internal.nio.NioTlsEngine;
27 | import java.net.SocketAddress;
28 | import java.nio.channels.SocketChannel;
29 | import java.util.concurrent.CompletableFuture;
30 | import java.util.concurrent.ThreadFactory;
31 | import java.util.function.Consumer;
32 |
33 | /** A {@code ForwardServer} implemented by NIO. */
34 | final class NioForwardServer implements ForwardServer {
35 | private final NioEventLoop bossEventLoop;
36 | private final NioEventLoopPool workerEventLoopPool;
37 |
38 | /**
39 | * Creates a {@code NioForwardServer}.
40 | *
41 | * @param localAddress local address to be bound
42 | * @param callback invoked when a request arrives
43 | * @param chunkSizeLimit the allowable size of chunks
44 | * @param tcpConfig the TCP config
45 | * @param workerPoolSize the event loop pool size for workers
46 | * @param channelConfig the channel configuration
47 | * @throws IllegalArgumentException if any of parameter is invalid e.g. the local address is
48 | * already used
49 | * @throws influent.exception.InfluentIOException if some IO error occurs
50 | */
51 | NioForwardServer(
52 | final SocketAddress localAddress,
53 | final ForwardCallback callback,
54 | final long chunkSizeLimit,
55 | final NioTcpConfig tcpConfig,
56 | final int workerPoolSize,
57 | final NioChannelConfig channelConfig,
58 | final ForwardSecurity security) {
59 | bossEventLoop = NioEventLoop.open();
60 | workerEventLoopPool = NioEventLoopPool.open(workerPoolSize);
61 | final Consumer channelFactory;
62 | if (channelConfig.isSslEnabled()) {
63 | channelFactory =
64 | (socketChannel) ->
65 | new NioForwardConnection(
66 | NioTcpTlsChannel.open(
67 | socketChannel,
68 | workerEventLoopPool.next(),
69 | tcpConfig,
70 | NioTlsEngine.createServerEngine(channelConfig.createSSLEngine())),
71 | callback,
72 | chunkSizeLimit,
73 | security);
74 | } else {
75 | channelFactory =
76 | (socketChannel) ->
77 | new NioForwardConnection(
78 | NioTcpPlaintextChannel.open(socketChannel, workerEventLoopPool.next(), tcpConfig),
79 | callback,
80 | chunkSizeLimit,
81 | security);
82 | }
83 | NioTcpAcceptor.open(localAddress, bossEventLoop, channelFactory, tcpConfig);
84 | new NioUdpHeartbeatServer(localAddress, bossEventLoop);
85 | }
86 |
87 | /** {@inheritDoc} */
88 | @Override
89 | public void start(final ThreadFactory threadFactory) {
90 | workerEventLoopPool.start(threadFactory);
91 | threadFactory.newThread(bossEventLoop).start();
92 | }
93 |
94 | /** {@inheritDoc} */
95 | @Override
96 | public CompletableFuture shutdown() {
97 | return bossEventLoop.shutdown().thenCompose(x -> workerEventLoopPool.shutdown());
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/forward/NioUdpHeartbeatServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward;
18 |
19 | import influent.internal.nio.NioAttachment;
20 | import influent.internal.nio.NioEventLoop;
21 | import influent.internal.nio.NioUdpChannel;
22 | import influent.internal.nio.NioUdpChannel.Op;
23 | import influent.internal.util.ThreadSafeQueue;
24 | import java.net.SocketAddress;
25 | import java.nio.ByteBuffer;
26 | import java.util.EnumSet;
27 | import java.util.Optional;
28 | import org.slf4j.Logger;
29 | import org.slf4j.LoggerFactory;
30 |
31 | /**
32 | * A heartbeat server for forward protocol.
33 | *
34 | *
{@code NioUdpHeartbeatServer} is not thread-safe and expected to be executed on the event loop
35 | * thread.
36 | */
37 | final class NioUdpHeartbeatServer implements NioAttachment {
38 | private static final byte RESPONSE_BYTE = 0;
39 | private static final int SOCKET_BUFFER_SIZE = 8;
40 | private static final Logger logger = LoggerFactory.getLogger(NioUdpHeartbeatServer.class);
41 |
42 | private final ByteBuffer response = ByteBuffer.allocate(Byte.BYTES).put(RESPONSE_BYTE);
43 | final ThreadSafeQueue replyTo = new ThreadSafeQueue<>();
44 | private final ByteBuffer receiveBuffer = ByteBuffer.allocate(1);
45 |
46 | private final NioUdpChannel channel;
47 |
48 | NioUdpHeartbeatServer(final NioUdpChannel channel) {
49 | this.channel = channel;
50 | }
51 |
52 | /**
53 | * Constructs a new {@code NioUdpHeartbeatServer}.
54 | *
55 | * @param localAddress the local address
56 | * @throws IllegalArgumentException if the local address is invalid or already used
57 | * @throws influent.exception.InfluentIOException if some IO error occurs
58 | */
59 | NioUdpHeartbeatServer(final SocketAddress localAddress, final NioEventLoop eventLoop) {
60 | this(NioUdpChannel.open(eventLoop, localAddress, SOCKET_BUFFER_SIZE, SOCKET_BUFFER_SIZE));
61 | channel.register(EnumSet.of(Op.READ), this);
62 | }
63 |
64 | /** Sends heartbeat responses. {@code NioUdpHeartbeatServer#onWritable} never fails. */
65 | @Override
66 | public void onWritable() {
67 | if (sendResponses()) {
68 | channel.disable(Op.WRITE);
69 | }
70 | }
71 |
72 | private boolean sendResponses() {
73 | while (replyTo.nonEmpty()) {
74 | try {
75 | final SocketAddress target = replyTo.peek();
76 | response.rewind();
77 | if (channel.send(response, target)) {
78 | replyTo.dequeue();
79 | } else {
80 | return false; // unsent responses are remaining
81 | }
82 | } catch (final Exception e) {
83 | logger.error("Failed sending a response.", e);
84 | }
85 | }
86 | return true;
87 | }
88 |
89 | /** Receives heartbeat requests. {@code NioUdpHeartbeatServer#onReadable} never fails. */
90 | @Override
91 | public void onReadable() {
92 | while (true) {
93 | receiveBuffer.rewind();
94 | try {
95 | receiveBuffer.rewind();
96 | final Optional source = channel.receive(receiveBuffer);
97 | if (source.isPresent()) {
98 | logger.debug("Received a heartbeat request from {}.", source);
99 | replyTo.enqueue(source.get());
100 | } else {
101 | break;
102 | }
103 | } catch (final Exception e) {
104 | logger.error("Failed receiving a request.", e);
105 | }
106 | }
107 |
108 | channel.enable(Op.WRITE);
109 | }
110 |
111 | /** {@inheritDoc} */
112 | @Override
113 | public void close() {
114 | channel.close();
115 | logger.info("The heartbeat server bound with {} closed.", channel.getLocalAddress());
116 | }
117 |
118 | /** {@inheritDoc} */
119 | @Override
120 | public String toString() {
121 | return "NioUdpHeartbeatServer(" + channel.getLocalAddress() + ")";
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/internal/msgpack/DecodeResult.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.msgpack;
18 |
19 | import org.msgpack.value.ImmutableValue;
20 |
21 | abstract class DecodeResult {
22 | private static final class Complete extends DecodeResult {
23 | private final ImmutableValue value;
24 |
25 | Complete(final ImmutableValue value) {
26 | this.value = value;
27 | }
28 |
29 | @Override
30 | boolean isCompleted() {
31 | return true;
32 | }
33 |
34 | @Override
35 | MsgpackIncrementalUnpacker next() {
36 | throw new IllegalStateException();
37 | }
38 |
39 | @Override
40 | ImmutableValue value() {
41 | return value;
42 | }
43 | }
44 |
45 | private static final class Continue extends DecodeResult {
46 | private final MsgpackIncrementalUnpacker next;
47 |
48 | Continue(final MsgpackIncrementalUnpacker next) {
49 | this.next = next;
50 | }
51 |
52 | @Override
53 | boolean isCompleted() {
54 | return false;
55 | }
56 |
57 | @Override
58 | MsgpackIncrementalUnpacker next() {
59 | return next;
60 | }
61 |
62 | @Override
63 | ImmutableValue value() {
64 | throw new IllegalStateException();
65 | }
66 | }
67 |
68 | static DecodeResult complete(final ImmutableValue result) {
69 | return new Complete(result);
70 | }
71 |
72 | static DecodeResult next(final MsgpackIncrementalUnpacker next) {
73 | return new Continue(next);
74 | }
75 |
76 | abstract boolean isCompleted();
77 |
78 | abstract MsgpackIncrementalUnpacker next();
79 |
80 | abstract ImmutableValue value();
81 | }
82 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/internal/msgpack/InfluentByteBuffer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.msgpack;
18 |
19 | import java.nio.ByteBuffer;
20 | import java.util.Deque;
21 | import java.util.LinkedList;
22 | import java.util.function.Supplier;
23 | import influent.internal.nio.NioTcpChannel;
24 |
25 | final class InfluentByteBuffer {
26 | private final Deque buffers = new LinkedList<>();
27 | private long remaining = 0;
28 | private long bufferSizeLimit;
29 |
30 | InfluentByteBuffer(final long bufferSizeLimit) {
31 | this.bufferSizeLimit = bufferSizeLimit;
32 | }
33 |
34 | void push(final ByteBuffer buffer) {
35 | remaining += buffer.remaining();
36 | buffers.addLast(buffer.slice());
37 | }
38 |
39 | private ByteBuffer peek() {
40 | return buffers.getFirst();
41 | }
42 |
43 | private void trim() {
44 | if (!peek().hasRemaining()) {
45 | buffers.removeFirst();
46 | }
47 | }
48 |
49 | private void getFromHead(final ByteBuffer dst) {
50 | final ByteBuffer head = peek();
51 | if (head.remaining() <= dst.remaining()) {
52 | remaining -= head.remaining();
53 | dst.put(head);
54 | } else {
55 | final int length = dst.remaining();
56 | remaining -= length;
57 | dst.put(head.array(), head.arrayOffset() + head.position(), length);
58 | head.position(head.position() + length);
59 | }
60 |
61 | trim();
62 | }
63 |
64 | boolean feed(final Supplier supplier) {
65 | // TODO: optimization
66 | while (remaining < bufferSizeLimit) {
67 | final ByteBuffer buffer = supplier.get();
68 | if (buffer == null) {
69 | return false;
70 | }
71 |
72 | push(buffer);
73 | }
74 | return true;
75 | }
76 |
77 | boolean hasRemaining() {
78 | return remaining != 0;
79 | }
80 |
81 | long remaining() {
82 | return remaining;
83 | }
84 |
85 | void get(final ByteBuffer dst) {
86 | while (dst.hasRemaining() && hasRemaining()) {
87 | getFromHead(dst);
88 | }
89 | }
90 |
91 | byte getByte() {
92 | final byte head = peek().get();
93 | remaining -= 1;
94 | trim();
95 | return head;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/influent-java/src/main/java/influent/internal/msgpack/MsgpackStreamUnpacker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.msgpack;
18 |
19 | import java.nio.ByteBuffer;
20 | import java.util.LinkedList;
21 | import java.util.NoSuchElementException;
22 | import java.util.Queue;
23 | import java.util.function.Supplier;
24 | import org.msgpack.value.ImmutableValue;
25 | import influent.exception.InfluentIOException;
26 | import influent.internal.nio.NioTcpChannel;
27 |
28 | /**
29 | * An unpacker for a MessagePack stream.
30 | *
31 | *
This is expected to be used in only Influent project.
32 | *
33 | *
{@code MsgpackStreamUnpacker} is not thread-safe.
34 | */
35 | public final class MsgpackStreamUnpacker {
36 | private final InfluentByteBuffer buffer;
37 | private final long chunkSizeLimit;
38 |
39 | private final Queue unpackedValues = new LinkedList<>();
40 | private MsgpackIncrementalUnpacker currentUnpacker = FormatUnpacker.getInstance();
41 | private long currentChunkSize = 0;
42 |
43 | /**
44 | * Constructs a new {@code MsgpackStreamUnpacker}.
45 | *
46 | * @param chunkSizeLimit the allowable chunk size {@code feed} fails when the size of reading
47 | * chunk exceeds the limit
48 | */
49 | public MsgpackStreamUnpacker(final long chunkSizeLimit) {
50 | this.buffer = new InfluentByteBuffer(chunkSizeLimit);
51 | this.chunkSizeLimit = chunkSizeLimit;
52 | }
53 |
54 | /**
55 | * Reads buffers from a {@code ReadableByteChannel}.
56 | *
57 | * @param supplier supplier to produce ByteBuffer
58 | * @param channel channel
59 | * @throws InfluentIOException when it fails reading from the channel or the chunk size exceeds
60 | * the limit
61 | */
62 | public void feed(final Supplier supplier, final NioTcpChannel channel) {
63 | boolean toBeContinued = true;
64 | while (toBeContinued) {
65 | toBeContinued = buffer.feed(supplier);
66 | unpack(channel);
67 | }
68 | }
69 |
70 | // fails when the chunk size exceeds the limit
71 | private void unpack(final NioTcpChannel channel) {
72 | while (buffer.hasRemaining()) {
73 | try {
74 | currentChunkSize += buffer.remaining();
75 | final DecodeResult result = currentUnpacker.unpack(buffer);
76 | currentChunkSize -= buffer.remaining();
77 | if (result.isCompleted()) {
78 | unpackedValues.offer(result.value());
79 | currentUnpacker = FormatUnpacker.getInstance();
80 | currentChunkSize = 0;
81 | } else if (currentChunkSize >= chunkSizeLimit) {
82 | channel.close();
83 | throw new InfluentIOException(
84 | "The chunk size exceeds the limit. size = "
85 | + buffer.remaining()
86 | + ", limit = "
87 | + chunkSizeLimit);
88 | } else {
89 | currentUnpacker = result.next();
90 | break;
91 | }
92 | } catch (final InfluentIOException e) {
93 | throw e;
94 | } catch (final Exception e) {
95 | channel.close();
96 | throw new InfluentIOException("Failed unpacking.", e);
97 | }
98 | }
99 | }
100 |
101 | /** @return true if this {@code MsgpackStreamUnpacker} can return the next value */
102 | public boolean hasNext() {
103 | return !unpackedValues.isEmpty();
104 | }
105 |
106 | /**
107 | * @return the next {@code ImmutableValue}
108 | * @throws NoSuchElementException when no next value is found
109 | */
110 | public ImmutableValue next() {
111 | return unpackedValues.remove();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/influent-java/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:
--------------------------------------------------------------------------------
1 | mock-maker-inline
--------------------------------------------------------------------------------
/influent-java/src/test/scala/influent/forward/NioUdpHeartbeatServerSpec.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.forward
18 |
19 | import java.net.{InetSocketAddress, SocketAddress}
20 | import java.nio.ByteBuffer
21 | import java.util.Optional
22 |
23 | import influent.exception.InfluentIOException
24 | import influent.internal.nio.NioUdpChannel
25 | import influent.internal.nio.NioUdpChannel.Op
26 | import org.mockito.Mockito._
27 | import org.scalatest.WordSpec
28 | import org.scalatest.mockito.MockitoSugar
29 |
30 | class NioUdpHeartbeatServerSpec extends WordSpec with MockitoSugar {
31 | private[this] def response: ByteBuffer = {
32 | val buffer = ByteBuffer.allocate(1).put(0: Byte)
33 | buffer.flip()
34 | buffer
35 | }
36 |
37 | "onWritable" should {
38 | "flush the response buffer" in {
39 | val channel = mock[NioUdpChannel]
40 | val server = new NioUdpHeartbeatServer(channel)
41 | val targets = Seq(
42 | new InetSocketAddress(8001),
43 | new InetSocketAddress(8002),
44 | new InetSocketAddress(8003)
45 | )
46 | targets.foreach { target =>
47 | server.replyTo.enqueue(target)
48 | when(channel.send(response, target)).thenReturn(true)
49 | }
50 |
51 | assert(server.onWritable() === ())
52 |
53 | targets.foreach { target =>
54 | verify(channel).send(response, target)
55 | }
56 | verify(channel).disable(Op.WRITE)
57 | verifyNoMoreInteractions(channel)
58 | }
59 |
60 | "not disable OP_WRITE" when {
61 | "all responses are not flushed" in {
62 | val channel = mock[NioUdpChannel]
63 | val server = new NioUdpHeartbeatServer(channel)
64 | val targets = Seq(
65 | new InetSocketAddress(8001),
66 | new InetSocketAddress(8002),
67 | new InetSocketAddress(8003)
68 | )
69 | targets.foreach(server.replyTo.enqueue)
70 | when(channel.send(response, targets.head)).thenReturn(true)
71 | when(channel.send(response, targets(1))).thenReturn(false)
72 |
73 | assert(server.onWritable() === ())
74 |
75 | verify(channel).send(response, targets.head)
76 | verify(channel).send(response, targets(1))
77 | verifyNoMoreInteractions(channel)
78 | }
79 | }
80 |
81 | "not fail" when {
82 | "some IO error occurs" in {
83 | val channel = mock[NioUdpChannel]
84 | val server = new NioUdpHeartbeatServer(channel)
85 | val targets = Seq(
86 | new InetSocketAddress(8001),
87 | new InetSocketAddress(8002),
88 | new InetSocketAddress(8003)
89 | )
90 | targets.foreach(server.replyTo.enqueue)
91 | when(channel.send(response, targets.head)).thenReturn(true)
92 | when(channel.send(response, targets(1)))
93 | .thenThrow(new InfluentIOException()).thenReturn(true)
94 | when(channel.send(response, targets(2))).thenReturn(true)
95 |
96 | assert(server.onWritable() === ())
97 |
98 | verify(channel).send(response, targets.head)
99 | verify(channel, times(2)).send(response, targets(1))
100 | verify(channel).send(response, targets(2))
101 | verify(channel).disable(Op.WRITE)
102 | verifyNoMoreInteractions(channel)
103 | }
104 | }
105 | }
106 |
107 | "onReadable" should {
108 | "receives heartbeat requests" in {
109 | val channel = mock[NioUdpChannel]
110 |
111 | val server = new NioUdpHeartbeatServer(channel)
112 | val source1 = new InetSocketAddress(8001)
113 | val source2 = new InetSocketAddress(8002)
114 | when(channel.receive(ByteBuffer.allocate(1)))
115 | .thenReturn(Optional.of(source1), Optional.of(source2), Optional.empty())
116 |
117 | assert(server.onReadable() === ())
118 |
119 | verify(channel, times(3)).receive(ByteBuffer.allocate(1))
120 | assert(server.replyTo.dequeue() === source1)
121 | assert(server.replyTo.dequeue() === source2)
122 | assert(!server.replyTo.nonEmpty())
123 | verify(channel).enable(Op.WRITE)
124 | verifyNoMoreInteractions(channel)
125 | }
126 |
127 | "not fail" when {
128 | "some IO error occurs" in {
129 | val channel = mock[NioUdpChannel]
130 | val server = new NioUdpHeartbeatServer(channel)
131 | val source: SocketAddress = new InetSocketAddress(8000)
132 | when(channel.receive(ByteBuffer.allocate(1)))
133 | .thenThrow(new InfluentIOException())
134 | .thenReturn(Optional.of(source), Optional.empty())
135 |
136 | assert(server.onReadable() === ())
137 | verify(channel, times(3)).receive(ByteBuffer.allocate(1))
138 | assert(server.replyTo.dequeue() === source)
139 | assert(!server.replyTo.nonEmpty())
140 | }
141 | }
142 | }
143 |
144 | "close" should {
145 | "close the channel and inform the supervisor" in {
146 | val channel = mock[NioUdpChannel]
147 | val server = new NioUdpHeartbeatServer(channel)
148 | assert(server.close() === ())
149 | verify(channel).close()
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/influent-java/src/test/scala/influent/internal/msgpack/MsgpackIncrementalUnpackerSpec.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.msgpack
18 |
19 | import influent.internal.msgpack.MsgpackUnpackerArbitrary._
20 | import java.nio.ByteBuffer
21 | import org.msgpack.core.MessagePack
22 | import org.msgpack.value.ImmutableValue
23 | import org.scalacheck.Shrink
24 | import org.scalatest.WordSpec
25 | import org.scalatest.prop.GeneratorDrivenPropertyChecks
26 |
27 | class MsgpackIncrementalUnpackerSpec extends WordSpec with GeneratorDrivenPropertyChecks {
28 | "MsgpackIncrementalUnpacker" should {
29 | "decode msgpack values" in {
30 | implicit val shrinkValue: Shrink[ImmutableValue] = Shrink.shrinkAny
31 | implicit val shrinkInt: Shrink[Int] = Shrink.shrinkAny
32 |
33 | forAll { (value: ImmutableValue, groupSize: Int) =>
34 | whenever(groupSize > 0) {
35 | val packer = MessagePack.newDefaultBufferPacker()
36 | packer.packValue(value)
37 | val asBytes = packer.toByteArray
38 |
39 | var unpacker: MsgpackIncrementalUnpacker = FormatUnpacker.getInstance()
40 | val buffer = new InfluentByteBuffer(Int.MaxValue)
41 | val chunks = asBytes.grouped(groupSize).toList
42 | chunks.init.foreach { bytes =>
43 | val buf = ByteBuffer.allocate(1024)
44 | buf.put(bytes).flip()
45 | buffer.push(buf)
46 | val result = unpacker.unpack(buffer)
47 | assert(!result.isCompleted)
48 | unpacker = result.next()
49 | }
50 |
51 | val buf = ByteBuffer.allocate(1024)
52 | buf.put(chunks.last).flip()
53 | buffer.push(buf)
54 |
55 | val actual = unpacker.unpack(buffer)
56 | assert(actual.isCompleted)
57 | assert(actual.value() === value)
58 | }
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/influent-java/src/test/scala/influent/internal/msgpack/MsgpackUnpackerArbitrary.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.msgpack
18 |
19 | import org.msgpack.value._
20 | import org.scalacheck.Arbitrary.arbitrary
21 | import org.scalacheck.{Arbitrary, Gen}
22 | import scala.collection.JavaConverters._
23 |
24 | object MsgpackUnpackerArbitrary {
25 | implicit lazy val arbValue: Arbitrary[ImmutableValue] = Arbitrary(genValue(0))
26 |
27 | private[this] def genValue(level: Int): Gen[ImmutableValue] = {
28 | def genScalar: Gen[ImmutableValue] = Gen.oneOf(
29 | arbitrary[ImmutableBinaryValue],
30 | arbitrary[ImmutableBooleanValue],
31 | arbitrary[ImmutableIntegerValue],
32 | arbitrary[ImmutableFloatValue],
33 | arbitrary[ImmutableNilValue],
34 | arbitrary[ImmutableStringValue],
35 | arbitrary[ImmutableExtensionValue]
36 | )
37 | def genCollection(level: Int): Gen[ImmutableValue] = Gen.oneOf(
38 | genArray(level),
39 | genMap(level)
40 | )
41 | level match {
42 | case 2 => genScalar
43 | case x => Gen.frequency(50 -> genScalar, 1 -> genCollection(x + 1))
44 | }
45 | }
46 |
47 | implicit lazy val arbBinary: Arbitrary[ImmutableBinaryValue] = Arbitrary {
48 | Gen.listOf(Arbitrary.arbByte.arbitrary).map(_.toArray).map(ValueFactory.newBinary)
49 | }
50 |
51 | implicit lazy val arbBoolean: Arbitrary[ImmutableBooleanValue] = Arbitrary {
52 | arbitrary[Boolean].map(ValueFactory.newBoolean)
53 | }
54 |
55 | implicit lazy val arbInteger: Arbitrary[ImmutableIntegerValue] = Arbitrary(Gen.oneOf(
56 | arbitrary[Long].map(ValueFactory.newInteger),
57 | arbitrary[BigInt].filter { value =>
58 | value.bitLength <= 63 || value.bitLength == 64 && value.signum == 1
59 | }.map(_.bigInteger).map(ValueFactory.newInteger)
60 | ))
61 |
62 | implicit lazy val arbFloat: Arbitrary[ImmutableFloatValue] = Arbitrary(Gen.oneOf(
63 | arbitrary[Float].map(ValueFactory.newFloat),
64 | arbitrary[Double].map(ValueFactory.newFloat)
65 | ))
66 |
67 | implicit lazy val arbNil: Arbitrary[ImmutableNilValue] = Arbitrary(Gen.const(ValueFactory.newNil()))
68 |
69 | implicit lazy val arbString: Arbitrary[ImmutableStringValue] = Arbitrary {
70 | Gen.alphaStr.map(ValueFactory.newString)
71 | }
72 |
73 | implicit lazy val arbExtension: Arbitrary[ImmutableExtensionValue] = Arbitrary {
74 | for {
75 | extType <- Arbitrary.arbByte.arbitrary
76 | data <- Gen.listOf(Arbitrary.arbByte.arbitrary).map(_.toArray)
77 | } yield ValueFactory.newExtension(extType, data)
78 | }
79 |
80 | private[this] def genArray(level: Int): Gen[ImmutableArrayValue] = {
81 | Gen.listOf(genValue(level)).map(_.asJava).map(ValueFactory.newArray)
82 | }
83 |
84 | implicit lazy val arbArray: Arbitrary[ImmutableArrayValue] = Arbitrary(genArray(0))
85 |
86 | private[this] def genMap(level: Int): Gen[ImmutableMapValue] = {
87 | val genKV = for {
88 | k <- genValue(level)
89 | v <- genValue(level)
90 | } yield (k, v)
91 | Gen.mapOf(genKV).flatMap { kvs =>
92 | kvs.map { case (k, v) => (k: Value, v: Value) }
93 | }.map(_.asJava).map { x => ValueFactory.newMap(x) }
94 | }
95 |
96 | implicit lazy val arbMap: Arbitrary[ImmutableMapValue] = Arbitrary(genMap(0))
97 | }
98 |
--------------------------------------------------------------------------------
/influent-transport/src/main/java/influent/exception/InfluentIOException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.exception;
18 |
19 | /** IO error. */
20 | public final class InfluentIOException extends RuntimeException {
21 | /**
22 | * Constructs a new {@code InfluentIOException}.
23 | *
24 | * @param message the error message
25 | * @param cause the cause
26 | */
27 | public InfluentIOException(final String message, final Throwable cause) {
28 | super(message, cause);
29 | }
30 |
31 | /**
32 | * Constructs a new {@code InfluentIOException}.
33 | *
34 | * @param message the error message
35 | */
36 | public InfluentIOException(final String message) {
37 | super(message);
38 | }
39 |
40 | /** Constructs a new {@code InfluentIOException}. */
41 | public InfluentIOException() {
42 | super();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/influent-transport/src/main/java/influent/internal/nio/NioAttachment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.nio;
18 |
19 | import influent.exception.InfluentIOException;
20 |
21 | /** An attachment for new IO operations. */
22 | public interface NioAttachment extends AutoCloseable {
23 | /**
24 | * Handles a read event. {@code NioAttachment} is closed when {@code onReadable} throws an
25 | * exception.
26 | *
27 | * @throws InfluentIOException when some IO error occurs
28 | * @throws UnsupportedOperationException {@code onReadable} is not supported
29 | */
30 | default void onReadable() {
31 | throw new UnsupportedOperationException(this + " does not support onReadable");
32 | }
33 |
34 | /**
35 | * Handles a write event. {@code NioAttachment} is closed when {@code onWritable} throws an
36 | * exception.
37 | *
38 | * @throws InfluentIOException when some IO error occurs
39 | * @throws UnsupportedOperationException {@code onWritable} is not supported
40 | */
41 | default void onWritable() {
42 | throw new UnsupportedOperationException(this + " does not support onWritable");
43 | }
44 |
45 | /**
46 | * Handles an accept event. {@code NioAttachment} is closed when {@code onAcceptable} throws an
47 | * exception.
48 | *
49 | * @throws InfluentIOException when some IO error occurs
50 | * @throws UnsupportedOperationException {@code onAcceptable} is not supported
51 | */
52 | default void onAcceptable() {
53 | throw new UnsupportedOperationException(this + " does not support onAcceptable");
54 | }
55 |
56 | /**
57 | * Handles a connect event. {@code NioAttachment} is closed when {@code onConnectable} throws an
58 | * exception.
59 | *
60 | * @throws InfluentIOException when some IO error occurs
61 | * @throws UnsupportedOperationException {@code onConnectable} is not supported
62 | */
63 | default void onConnectable() {
64 | throw new UnsupportedOperationException(this + " does not support onConnectable");
65 | }
66 |
67 | /** Terminates this attachment. */
68 | void close();
69 | }
70 |
--------------------------------------------------------------------------------
/influent-transport/src/main/java/influent/internal/nio/NioChannelConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.nio;
18 |
19 | import java.io.FileInputStream;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 | import java.security.KeyManagementException;
23 | import java.security.KeyStore;
24 | import java.security.KeyStoreException;
25 | import java.security.NoSuchAlgorithmException;
26 | import java.security.SecureRandom;
27 | import java.security.UnrecoverableKeyException;
28 | import java.security.cert.CertificateException;
29 | import javax.net.ssl.KeyManager;
30 | import javax.net.ssl.KeyManagerFactory;
31 | import javax.net.ssl.SSLContext;
32 | import javax.net.ssl.SSLEngine;
33 | import javax.net.ssl.SSLException;
34 | import influent.exception.InfluentIOException;
35 |
36 | public class NioChannelConfig {
37 |
38 | private boolean sslEnabled = false;
39 | private String host;
40 | private int port;
41 | private String[] tlsVersions;
42 | private String[] ciphers;
43 | private SSLContext context;
44 |
45 | public NioChannelConfig() {
46 | sslEnabled = false;
47 | context = null;
48 | tlsVersions = null;
49 | }
50 |
51 | public NioChannelConfig(
52 | final String host,
53 | final int port,
54 | final boolean sslEnabled,
55 | final String[] tlsVersions,
56 | final String[] ciphers,
57 | final String keystorePath,
58 | final String keystorePassword,
59 | final String keyPassword) {
60 | this.host = host;
61 | this.port = port;
62 | this.sslEnabled = sslEnabled;
63 | this.tlsVersions = tlsVersions;
64 | this.ciphers = ciphers;
65 | try {
66 | if (isSslEnabled()) {
67 | context = SSLContext.getInstance("TLS");
68 | context.init(
69 | createKeyManagers(keystorePath, keystorePassword, keyPassword),
70 | null,
71 | new SecureRandom());
72 | }
73 | } catch (final NoSuchAlgorithmException e) {
74 | throw new AssertionError(e);
75 | } catch (final KeyManagementException e) {
76 | e.printStackTrace();
77 | }
78 | }
79 |
80 | public SSLEngine createSSLEngine() {
81 | final SSLEngine engine = context.createSSLEngine(host, port);
82 | engine.setUseClientMode(false);
83 | engine.setEnabledProtocols(tlsVersions);
84 | if (ciphers != null) {
85 | engine.setEnabledCipherSuites(ciphers);
86 | }
87 | try {
88 | engine.beginHandshake();
89 | } catch (final SSLException e) {
90 | throw new InfluentIOException("Failed beginning a handshake.", e);
91 | }
92 | // TODO configure engine
93 | return engine;
94 | }
95 |
96 | private KeyManager[] createKeyManagers(
97 | final String filepath, final String keystorePassword, final String keyPassword) {
98 | try {
99 | final KeyStore keyStore = KeyStore.getInstance("JKS");
100 | try (InputStream keyStoreIS = new FileInputStream(filepath)) {
101 | keyStore.load(keyStoreIS, keystorePassword.toCharArray());
102 | }
103 | final KeyManagerFactory kmf =
104 | KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
105 | kmf.init(keyStore, keyPassword.toCharArray());
106 | return kmf.getKeyManagers();
107 | } catch (final IOException e) {
108 | e.printStackTrace();
109 | } catch (final CertificateException
110 | | UnrecoverableKeyException
111 | | NoSuchAlgorithmException
112 | | KeyStoreException e) {
113 | e.printStackTrace();
114 | }
115 | return null;
116 | }
117 |
118 | public boolean isSslEnabled() {
119 | return sslEnabled;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/influent-transport/src/main/java/influent/internal/nio/NioEventLoop.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.nio;
18 |
19 | import influent.exception.InfluentIOException;
20 | import influent.internal.nio.NioEventLoopTask.Register;
21 | import influent.internal.nio.NioEventLoopTask.Select;
22 | import influent.internal.nio.NioEventLoopTask.UpdateInterestSet;
23 | import influent.internal.util.Exceptions;
24 | import influent.internal.util.Futures;
25 | import influent.internal.util.ThreadSafeQueue;
26 | import java.io.IOException;
27 | import java.nio.channels.ClosedSelectorException;
28 | import java.nio.channels.SelectableChannel;
29 | import java.nio.channels.SelectionKey;
30 | import java.nio.channels.Selector;
31 | import java.util.Set;
32 | import java.util.concurrent.CompletableFuture;
33 | import java.util.concurrent.atomic.AtomicReference;
34 | import org.slf4j.Logger;
35 | import org.slf4j.LoggerFactory;
36 |
37 | /**
38 | * An event loop for non-blocking IO.
39 | *
40 | *
{@code NioEventLoop} is unconditionally thread-safe. {@code NioEventLoop#run} is expected to
41 | * be executed on one exclusive thread.
42 | */
43 | public final class NioEventLoop implements Runnable {
44 | private enum State {
45 | IDLE,
46 | ACTIVE,
47 | STOPPING,
48 | TERMINATED
49 | }
50 |
51 | private static final Logger logger = LoggerFactory.getLogger(NioEventLoop.class);
52 |
53 | private final Selector selector;
54 | private final ThreadSafeQueue tasks = new ThreadSafeQueue<>();
55 | private final AtomicReference state = new AtomicReference<>(State.IDLE);
56 | private final CompletableFuture shutdownFuture = new CompletableFuture<>();
57 |
58 | NioEventLoop(final Selector selector) {
59 | this.selector = selector;
60 | }
61 |
62 | /**
63 | * Creates a new {@code NioEventLoop}.
64 | *
65 | * @return the {@code NioEventLoop}
66 | * @throws InfluentIOException if a selector cannot be created
67 | */
68 | public static NioEventLoop open() {
69 | try {
70 | return new NioEventLoop(Selector.open());
71 | } catch (final IOException e) {
72 | throw new InfluentIOException("A selector cannot be created.", e);
73 | }
74 | }
75 |
76 | /**
77 | * Executes this event loop.
78 | *
79 | * @throws IllegalStateException if this event loop has already started
80 | */
81 | @Override
82 | public void run() {
83 | if (!state.compareAndSet(State.IDLE, State.ACTIVE)) {
84 | throw new IllegalStateException("This NioEventLoop is " + state.get());
85 | }
86 |
87 | try {
88 | loop();
89 | } finally {
90 | cleanup();
91 | state.set(State.TERMINATED);
92 | shutdownFuture.complete(null);
93 | }
94 | }
95 |
96 | private void loop() {
97 | while (state.get() == State.ACTIVE) {
98 | while (tasks.nonEmpty()) {
99 | try {
100 | tasks.dequeue().run();
101 | } catch (final Exception e) {
102 | logger.error("NioEventLoopTask failed.", e);
103 | }
104 | }
105 | tasks.enqueue(Select.of(selector));
106 | }
107 | }
108 |
109 | /**
110 | * Registers a channel with this event loop.
111 | *
112 | *
Operations are done asynchronously.
113 | *
114 | * @param channel the channel
115 | * @param key the {@code NioSelectionKey}
116 | * @param ops the interest set
117 | * @param attachment the {@code NioAttachment}
118 | */
119 | void register(
120 | final SelectableChannel channel,
121 | final NioSelectionKey key,
122 | final int ops,
123 | final NioAttachment attachment) {
124 | addTask(Register.of(selector, channel, key, ops, attachment));
125 | }
126 |
127 | /**
128 | * Enables the given interest set on the given {@code NioSelectionKey}.
129 | *
130 | *
Operations are done asynchronously.
131 | *
132 | * @param key the {@code NioSelectionKey}
133 | * @param ops the interest set to be enabled
134 | */
135 | void enableInterestSet(final NioSelectionKey key, final int ops) {
136 | addTask(UpdateInterestSet.of(key, current -> current | ops));
137 | }
138 |
139 | /**
140 | * Disables the given interest set on the given {@code NioSelectionKey}.
141 | *
142 | *
Operations are done asynchronously.
143 | *
144 | * @param key the {@code NioSelectionKey}
145 | * @param ops the interest set to be disabled
146 | */
147 | void disableInterestSet(final NioSelectionKey key, final int ops) {
148 | addTask(UpdateInterestSet.of(key, current -> current & ~ops));
149 | }
150 |
151 | private void addTask(final NioEventLoopTask task) {
152 | tasks.enqueue(task);
153 | // TODO: optimization
154 | selector.wakeup();
155 | }
156 |
157 | private void cleanup() {
158 | try {
159 | final Set keys = selector.keys();
160 | keys.forEach(
161 | key -> {
162 | final NioAttachment attachment = (NioAttachment) key.attachment();
163 | Exceptions.ignore(attachment::close, "Failed closing the attachment. " + attachment);
164 | });
165 | Exceptions.ignore(selector::close, "Failed closing the selector");
166 | } catch (final ClosedSelectorException e) {
167 | throw new AssertionError(e);
168 | }
169 | }
170 |
171 | /**
172 | * Stops this event loop. Shutdown operations are executed asynchronously and {@code
173 | * NioEventLoop#shutdown} returns a {@code CompletedFuture}.
174 | *
175 | * @return {@code CompletableFuture} the future that will be completed when this event loop stops
176 | * @throws IllegalStateException when this event loop is not started
177 | */
178 | public CompletableFuture shutdown() {
179 | if (state.get() == State.IDLE) {
180 | throw new IllegalStateException("This NioEventLoop has not yet been started.");
181 | }
182 | if (state.compareAndSet(State.ACTIVE, State.STOPPING)) {
183 | selector.wakeup();
184 | }
185 | return Futures.followerOf(shutdownFuture);
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/influent-transport/src/main/java/influent/internal/nio/NioEventLoopPool.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 okumin
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package influent.internal.nio;
18 |
19 | import java.util.concurrent.CompletableFuture;
20 | import java.util.concurrent.ThreadFactory;
21 |
22 | /**
23 | * A pool of {@code NioEventLoop}.
24 | *
25 | *