This commit is contained in:
12
.gitattributes
vendored
Normal file
12
.gitattributes
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
#
|
||||
# Linux start script should use lf
|
||||
/gradlew text eol=lf
|
||||
|
||||
# These are Windows script files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
|
||||
# Binary files should be left untouched
|
||||
*.jar binary
|
||||
|
17
.gitea/workflows/build.yaml
Normal file
17
.gitea/workflows/build.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: hostinger
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
- name: Execute Gradle build
|
||||
env:
|
||||
PUBLISHER_TOKEN: ${{ secrets.PUBLISHER_TOKEN }}
|
||||
run: ./gradlew build publish
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Ignore Gradle project-specific cache directory
|
||||
.gradle
|
||||
|
||||
# Ignore Gradle build output directory
|
||||
build
|
107
build.gradle
Normal file
107
build.gradle
Normal file
@@ -0,0 +1,107 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
alias catalog.plugins.sambal
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = 'net.woggioni'
|
||||
version = project.currentTag.map { List<String> tags ->
|
||||
tags[0]
|
||||
}.getOrElse(getProperty('xmemcached.version') + '-SNAPSHOT')
|
||||
|
||||
pluginManager.withPlugin('java-library') {
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
modularity.inferModulePath = true
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
useJUnitPlatform()
|
||||
systemProperty('junit.jupiter.execution.parallel.enabled', 'true')
|
||||
systemProperty('junit.jupiter.execution.parallel.mode.classes.default', 'concurrent')
|
||||
systemProperty('junit.jupiter.execution.parallel.mode.default = concurrent', 'concurrent')
|
||||
// systemProperty('test.memcached.host', 'localhost')
|
||||
// systemProperty('test.memcached.port', '1211')
|
||||
// systemProperty('test.memcached.servers', 'localhost:1211')
|
||||
|
||||
filter {
|
||||
excludeTestsMatching "*IT"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.withPlugin(catalog.plugins.lombok.get().pluginId) {
|
||||
lombok {
|
||||
version = catalog.versions.lombok.get()
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.withPlugin('maven-publish') {
|
||||
|
||||
pluginManager.withPlugin('java-library') {
|
||||
java {
|
||||
// withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "Gitea"
|
||||
url = uri(getProperty('gitea.maven.url'))
|
||||
|
||||
credentials(HttpHeaderCredentials) {
|
||||
name = "Authorization"
|
||||
value = "token ${System.getenv()["PUBLISHER_TOKEN"]}"
|
||||
}
|
||||
|
||||
authentication {
|
||||
header(HttpHeaderAuthentication)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation catalog.slf4j.api
|
||||
// https://mvnrepository.com/artifact/org.springframework/spring-core
|
||||
compileOnly group: 'org.springframework', name: 'spring-context', version: getProperty('spring.version')
|
||||
// https://mvnrepository.com/artifact/org.springframework/spring-core
|
||||
compileOnly group: 'org.springframework', name: 'spring-beans', version: getProperty('spring.version')
|
||||
|
||||
// https://mvnrepository.com/artifact/org.easymock/easymock
|
||||
testImplementation group: 'org.easymock', name: 'easymock', version: getProperty('easymock.version')
|
||||
|
||||
testImplementation catalog.junit.jupiter.api
|
||||
testRuntimeOnly catalog.junit.jupiter.engine
|
||||
|
||||
// https://mvnrepository.com/artifact/org.springframework/spring-core
|
||||
testImplementation group: 'org.springframework', name: 'spring-context', version: getProperty('spring.version')
|
||||
// https://mvnrepository.com/artifact/org.springframework/spring-core
|
||||
testImplementation group: 'org.springframework', name: 'spring-beans', version: getProperty('spring.version')
|
||||
|
||||
// testImplementation platform(group: "org.testcontainers", name: "testcontainers-bom", version: "1.20.4")
|
||||
// testImplementation group: "org.testcontainers", name: 'testcontainers'
|
||||
// testImplementation group: 'org.testcontainers', name: 'junit-jupiter'
|
||||
|
||||
}
|
||||
|
12
gradle.properties
Normal file
12
gradle.properties
Normal file
@@ -0,0 +1,12 @@
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
|
||||
xmemcached.version = 2.4.9
|
||||
|
||||
lys.version = 2024.12.29
|
||||
|
||||
gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven
|
||||
|
||||
spring.version=6.2.1
|
||||
easymock.version=5.5.0
|
2
gradle/libs.versions.toml
Normal file
2
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file was generated by the Gradle 'init' task.
|
||||
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
251
gradlew
vendored
Executable file
251
gradlew
vendored
Executable file
@@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
94
gradlew.bat
vendored
Normal file
94
gradlew.bat
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
26
settings.gradle
Normal file
26
settings.gradle
Normal file
@@ -0,0 +1,26 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url = getProperty('gitea.maven.url')
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url = getProperty('gitea.maven.url')
|
||||
content {
|
||||
includeGroup 'com.lys'
|
||||
}
|
||||
}
|
||||
}
|
||||
versionCatalogs {
|
||||
catalog {
|
||||
from group: 'com.lys', name: 'lys-catalog', version: getProperty('lys.version')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'xmemcached'
|
47
src/assemble/distribution.xml
Normal file
47
src/assemble/distribution.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<assembly>
|
||||
<id>bin-with-dependencies</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>User_Guide_zh</directory>
|
||||
<outputDirectory>/User_Guide_zh</outputDirectory>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>User_Guide_en</directory>
|
||||
<outputDirectory>/User_Guide_en</outputDirectory>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}
|
||||
</directory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/lib</outputDirectory>
|
||||
<scope>runtime</scope>
|
||||
<excludes>
|
||||
<exclude>xmemcached*</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
<files>
|
||||
<file>
|
||||
<source>README.md</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>NOTICE.txt</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>LICENSE.txt</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
30
src/assemble/src.xml
Normal file
30
src/assemble/src.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<assembly>
|
||||
<id>src</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/src</directory>
|
||||
<outputDirectory>/src</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<files>
|
||||
<file>
|
||||
<source>README.md</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>pom.xml</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>NOTICE.txt</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>LICENSE.txt</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
2494
src/main/java/com/google/code/yanf4j/buffer/AbstractIoBuffer.java
Normal file
2494
src/main/java/com/google/code/yanf4j/buffer/AbstractIoBuffer.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work for additional information regarding
|
||||
* copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.code.yanf4j.buffer;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeException} which is thrown when the data the {@link IoBuffer} contains is
|
||||
* corrupt.
|
||||
*
|
||||
* @author The Apache MINA Project (dev@mina.apache.org)
|
||||
* @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (Thu, 26 Jun 2008) $
|
||||
*
|
||||
*/
|
||||
public class BufferDataException extends RuntimeException {
|
||||
private static final long serialVersionUID = -4138189188602563502L;
|
||||
|
||||
public BufferDataException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BufferDataException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BufferDataException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BufferDataException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work for additional information regarding
|
||||
* copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.code.yanf4j.buffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import com.google.code.yanf4j.util.CircularQueue;
|
||||
|
||||
/**
|
||||
* An {@link IoBufferAllocator} that caches the buffers which are likely to be reused during
|
||||
* auto-expansion of the buffers.
|
||||
* <p>
|
||||
* In {@link SimpleBufferAllocator}, the underlying {@link ByteBuffer} of the {@link IoBuffer} is
|
||||
* reallocated on its capacity change, which means the newly allocated bigger {@link ByteBuffer}
|
||||
* replaces the old small {@link ByteBuffer} . Consequently, the old {@link ByteBuffer} is marked
|
||||
* for garbage collection.
|
||||
* <p>
|
||||
* It's not a problem in most cases as long as the capacity change doesn't happen frequently.
|
||||
* However, once it happens too often, it burdens the VM and the cost of filling the newly allocated
|
||||
* {@link ByteBuffer} with {@code NUL} surpass the cost of accessing the cache. In 2 dual-core
|
||||
* Opteron Italy 270 processors, {@link CachedBufferAllocator} outperformed
|
||||
* {@link SimpleBufferAllocator} in the following situation:
|
||||
* <ul>
|
||||
* <li>when a 32 bytes buffer is expanded 4 or more times,</li>
|
||||
* <li>when a 64 bytes buffer is expanded 4 or more times,</li>
|
||||
* <li>when a 128 bytes buffer is expanded 2 or more times,</li>
|
||||
* <li>and when a 256 bytes or bigger buffer is expanded 1 or more times.</li>
|
||||
* </ul>
|
||||
* Please note the observation above is subject to change in a different environment.
|
||||
* <p>
|
||||
* {@link CachedBufferAllocator} uses {@link ThreadLocal} to store the cached buffer, allocates
|
||||
* buffers whose capacity is power of 2 only and provides performance advantage if
|
||||
* {@link IoBuffer#free()} is called properly.
|
||||
*
|
||||
* @author The Apache MINA Project (dev@mina.apache.org)
|
||||
* @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (Thu, 26 Jun 2008) $
|
||||
*/
|
||||
public class CachedBufferAllocator implements IoBufferAllocator {
|
||||
|
||||
private static final int DEFAULT_MAX_POOL_SIZE = 8;
|
||||
private static final int DEFAULT_MAX_CACHED_BUFFER_SIZE = 1 << 18; // 256KB
|
||||
|
||||
private final int maxPoolSize;
|
||||
private final int maxCachedBufferSize;
|
||||
|
||||
private final ThreadLocal<Map<Integer, Queue<CachedBuffer>>> heapBuffers;
|
||||
private final ThreadLocal<Map<Integer, Queue<CachedBuffer>>> directBuffers;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the default parameters ({@literal #DEFAULT_MAX_POOL_SIZE} and
|
||||
* {@literal #DEFAULT_MAX_CACHED_BUFFER_SIZE}).
|
||||
*/
|
||||
public CachedBufferAllocator() {
|
||||
this(DEFAULT_MAX_POOL_SIZE, DEFAULT_MAX_CACHED_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param maxPoolSize the maximum number of buffers with the same capacity per thread. <code>0</code>
|
||||
* disables this limitation.
|
||||
* @param maxCachedBufferSize the maximum capacity of a cached buffer. A buffer whose capacity is
|
||||
* bigger than this value is not pooled. <code>0</code> disables this limitation.
|
||||
*/
|
||||
public CachedBufferAllocator(int maxPoolSize, int maxCachedBufferSize) {
|
||||
if (maxPoolSize < 0) {
|
||||
throw new IllegalArgumentException("maxPoolSize: " + maxPoolSize);
|
||||
}
|
||||
if (maxCachedBufferSize < 0) {
|
||||
throw new IllegalArgumentException("maxCachedBufferSize: " + maxCachedBufferSize);
|
||||
}
|
||||
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
this.maxCachedBufferSize = maxCachedBufferSize;
|
||||
|
||||
this.heapBuffers = new ThreadLocal<Map<Integer, Queue<CachedBuffer>>>() {
|
||||
@Override
|
||||
protected Map<Integer, Queue<CachedBuffer>> initialValue() {
|
||||
return newPoolMap();
|
||||
}
|
||||
};
|
||||
this.directBuffers = new ThreadLocal<Map<Integer, Queue<CachedBuffer>>>() {
|
||||
@Override
|
||||
protected Map<Integer, Queue<CachedBuffer>> initialValue() {
|
||||
return newPoolMap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of buffers with the same capacity per thread. <code>0</code> means 'no
|
||||
* limitation'.
|
||||
*/
|
||||
public int getMaxPoolSize() {
|
||||
return this.maxPoolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum capacity of a cached buffer. A buffer whose capacity is bigger than this
|
||||
* value is not pooled. <code>0</code> means 'no limitation'.
|
||||
*/
|
||||
public int getMaxCachedBufferSize() {
|
||||
return this.maxCachedBufferSize;
|
||||
}
|
||||
|
||||
private Map<Integer, Queue<CachedBuffer>> newPoolMap() {
|
||||
Map<Integer, Queue<CachedBuffer>> poolMap = new HashMap<Integer, Queue<CachedBuffer>>();
|
||||
int poolSize = this.maxPoolSize == 0 ? DEFAULT_MAX_POOL_SIZE : this.maxPoolSize;
|
||||
for (int i = 0; i < 31; i++) {
|
||||
poolMap.put(1 << i, new CircularQueue<CachedBuffer>(poolSize));
|
||||
}
|
||||
poolMap.put(0, new CircularQueue<CachedBuffer>(poolSize));
|
||||
poolMap.put(Integer.MAX_VALUE, new CircularQueue<CachedBuffer>(poolSize));
|
||||
return poolMap;
|
||||
}
|
||||
|
||||
public IoBuffer allocate(int requestedCapacity, boolean direct) {
|
||||
int actualCapacity = IoBuffer.normalizeCapacity(requestedCapacity);
|
||||
IoBuffer buf;
|
||||
if (this.maxCachedBufferSize != 0 && actualCapacity > this.maxCachedBufferSize) {
|
||||
if (direct) {
|
||||
buf = wrap(ByteBuffer.allocateDirect(actualCapacity));
|
||||
} else {
|
||||
buf = wrap(ByteBuffer.allocate(actualCapacity));
|
||||
}
|
||||
} else {
|
||||
Queue<CachedBuffer> pool;
|
||||
if (direct) {
|
||||
pool = this.directBuffers.get().get(actualCapacity);
|
||||
} else {
|
||||
pool = this.heapBuffers.get().get(actualCapacity);
|
||||
}
|
||||
|
||||
// Recycle if possible.
|
||||
buf = pool.poll();
|
||||
if (buf != null) {
|
||||
buf.clear();
|
||||
buf.setAutoExpand(false);
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
} else {
|
||||
if (direct) {
|
||||
buf = wrap(ByteBuffer.allocateDirect(actualCapacity));
|
||||
} else {
|
||||
buf = wrap(ByteBuffer.allocate(actualCapacity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.limit(requestedCapacity);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public ByteBuffer allocateNioBuffer(int capacity, boolean direct) {
|
||||
return allocate(capacity, direct).buf();
|
||||
}
|
||||
|
||||
public IoBuffer wrap(ByteBuffer nioBuffer) {
|
||||
return new CachedBuffer(nioBuffer);
|
||||
}
|
||||
|
||||
public void dispose() {}
|
||||
|
||||
private class CachedBuffer extends AbstractIoBuffer {
|
||||
private final Thread ownerThread;
|
||||
private ByteBuffer buf;
|
||||
|
||||
protected CachedBuffer(ByteBuffer buf) {
|
||||
super(CachedBufferAllocator.this, buf.capacity());
|
||||
this.ownerThread = Thread.currentThread();
|
||||
this.buf = buf;
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
|
||||
protected CachedBuffer(CachedBuffer parent, ByteBuffer buf) {
|
||||
super(parent);
|
||||
this.ownerThread = Thread.currentThread();
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer buf() {
|
||||
if (this.buf == null) {
|
||||
throw new IllegalStateException("Buffer has been freed already.");
|
||||
}
|
||||
return this.buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buf(ByteBuffer buf) {
|
||||
ByteBuffer oldBuf = this.buf;
|
||||
this.buf = buf;
|
||||
free(oldBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IoBuffer duplicate0() {
|
||||
return new CachedBuffer(this, buf().duplicate());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IoBuffer slice0() {
|
||||
return new CachedBuffer(this, buf().slice());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IoBuffer asReadOnlyBuffer0() {
|
||||
return new CachedBuffer(this, buf().asReadOnlyBuffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] array() {
|
||||
return buf().array();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int arrayOffset() {
|
||||
return buf().arrayOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasArray() {
|
||||
return buf().hasArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
free(this.buf);
|
||||
this.buf = null;
|
||||
}
|
||||
|
||||
private void free(ByteBuffer oldBuf) {
|
||||
if (oldBuf == null || oldBuf.capacity() > CachedBufferAllocator.this.maxCachedBufferSize
|
||||
|| oldBuf.isReadOnly() || isDerived() || Thread.currentThread() != this.ownerThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to the cache.
|
||||
Queue<CachedBuffer> pool;
|
||||
if (oldBuf.isDirect()) {
|
||||
pool = CachedBufferAllocator.this.directBuffers.get().get(oldBuf.capacity());
|
||||
} else {
|
||||
pool = CachedBufferAllocator.this.heapBuffers.get().get(oldBuf.capacity());
|
||||
}
|
||||
|
||||
if (pool == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Restrict the size of the pool to prevent OOM.
|
||||
if (CachedBufferAllocator.this.maxPoolSize == 0
|
||||
|| pool.size() < CachedBufferAllocator.this.maxPoolSize) {
|
||||
pool.offer(new CachedBuffer(oldBuf));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1330
src/main/java/com/google/code/yanf4j/buffer/IoBuffer.java
Normal file
1330
src/main/java/com/google/code/yanf4j/buffer/IoBuffer.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work for additional information regarding
|
||||
* copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.code.yanf4j.buffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Allocates {@link IoBuffer}s and manages them. Please implement this interface if you need more
|
||||
* advanced memory management scheme.
|
||||
*
|
||||
* @author The Apache MINA Project (dev@mina.apache.org)
|
||||
* @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (Thu, 26 Jun 2008) $
|
||||
*/
|
||||
public interface IoBufferAllocator {
|
||||
/**
|
||||
* Returns the buffer which is capable of the specified size.
|
||||
*
|
||||
* @param capacity the capacity of the buffer
|
||||
* @param direct <code>true</code> to get a direct buffer, <code>false</code> to get a heap buffer.
|
||||
*/
|
||||
IoBuffer allocate(int capacity, boolean direct);
|
||||
|
||||
/**
|
||||
* Returns the NIO buffer which is capable of the specified size.
|
||||
*
|
||||
* @param capacity the capacity of the buffer
|
||||
* @param direct <code>true</code> to get a direct buffer, <code>false</code> to get a heap buffer.
|
||||
*/
|
||||
ByteBuffer allocateNioBuffer(int capacity, boolean direct);
|
||||
|
||||
/**
|
||||
* Wraps the specified NIO {@link ByteBuffer} into MINA buffer.
|
||||
*/
|
||||
IoBuffer wrap(ByteBuffer nioBuffer);
|
||||
|
||||
/**
|
||||
* Dispose of this allocator.
|
||||
*/
|
||||
void dispose();
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work for additional information regarding
|
||||
* copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.code.yanf4j.buffer;
|
||||
|
||||
/**
|
||||
* Provides utility methods to dump an {@link IoBuffer} into a hex formatted string.
|
||||
*
|
||||
* @author The Apache MINA Project (dev@mina.apache.org)
|
||||
* @version $Rev: 686598 $, $Date: 2008-08-17 12:58:23 +0200 (Sun, 17 Aug 2008) $
|
||||
*/
|
||||
class IoBufferHexDumper {
|
||||
|
||||
/**
|
||||
* The high digits lookup table.
|
||||
*/
|
||||
private static final byte[] highDigits;
|
||||
|
||||
/**
|
||||
* The low digits lookup table.
|
||||
*/
|
||||
private static final byte[] lowDigits;
|
||||
|
||||
/**
|
||||
* Initialize lookup tables.
|
||||
*/
|
||||
static {
|
||||
final byte[] digits =
|
||||
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
int i;
|
||||
byte[] high = new byte[256];
|
||||
byte[] low = new byte[256];
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
high[i] = digits[i >>> 4];
|
||||
low[i] = digits[i & 0x0F];
|
||||
}
|
||||
|
||||
highDigits = high;
|
||||
lowDigits = low;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps an {@link IoBuffer} to a hex formatted string.
|
||||
*
|
||||
* @param in the buffer to dump
|
||||
* @param lengthLimit the limit at which hex dumping will stop
|
||||
* @return a hex formatted string representation of the <i>in</i> {@link Iobuffer}.
|
||||
*/
|
||||
public static String getHexdump(IoBuffer in, int lengthLimit) {
|
||||
if (lengthLimit == 0) {
|
||||
throw new IllegalArgumentException("lengthLimit: " + lengthLimit + " (expected: 1+)");
|
||||
}
|
||||
|
||||
boolean truncate = in.remaining() > lengthLimit;
|
||||
int size;
|
||||
if (truncate) {
|
||||
size = lengthLimit;
|
||||
} else {
|
||||
size = in.remaining();
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return "empty";
|
||||
}
|
||||
|
||||
StringBuilder out = new StringBuilder(in.remaining() * 3 - 1);
|
||||
|
||||
int mark = in.position();
|
||||
|
||||
// fill the first
|
||||
int byteValue = in.get() & 0xFF;
|
||||
out.append((char) highDigits[byteValue]);
|
||||
out.append((char) lowDigits[byteValue]);
|
||||
size--;
|
||||
|
||||
// and the others, too
|
||||
for (; size > 0; size--) {
|
||||
out.append(' ');
|
||||
byteValue = in.get() & 0xFF;
|
||||
out.append((char) highDigits[byteValue]);
|
||||
out.append((char) lowDigits[byteValue]);
|
||||
}
|
||||
|
||||
in.position(mark);
|
||||
|
||||
if (truncate) {
|
||||
out.append("...");
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
}
|
891
src/main/java/com/google/code/yanf4j/buffer/IoBufferWrapper.java
Normal file
891
src/main/java/com/google/code/yanf4j/buffer/IoBufferWrapper.java
Normal file
@@ -0,0 +1,891 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work for additional information regarding
|
||||
* copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.code.yanf4j.buffer;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A {@link IoBuffer} that wraps a buffer and proxies any operations to it.
|
||||
* <p>
|
||||
* You can think this class like a {@link FilterOutputStream}. All operations are proxied by default
|
||||
* so that you can extend this class and override existing operations selectively. You can introduce
|
||||
* new operations, too.
|
||||
*
|
||||
* @author The Apache MINA Project (dev@mina.apache.org)
|
||||
* @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (Thu, 26 Jun 2008) $
|
||||
*/
|
||||
public class IoBufferWrapper extends IoBuffer {
|
||||
|
||||
/**
|
||||
* The buffer proxied by this proxy.
|
||||
*/
|
||||
private final IoBuffer buf;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param buf the buffer to be proxied
|
||||
*/
|
||||
protected IoBufferWrapper(IoBuffer buf) {
|
||||
if (buf == null) {
|
||||
throw new NullPointerException("buf");
|
||||
}
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent buffer that this buffer wrapped.
|
||||
*/
|
||||
public IoBuffer getParentBuffer() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirect() {
|
||||
return buf.isDirect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer buf() {
|
||||
return buf.buf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int capacity() {
|
||||
return buf.capacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int position() {
|
||||
return buf.position();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer position(int newPosition) {
|
||||
buf.position(newPosition);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int limit() {
|
||||
return buf.limit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer limit(int newLimit) {
|
||||
buf.limit(newLimit);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer mark() {
|
||||
buf.mark();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer reset() {
|
||||
buf.reset();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer clear() {
|
||||
buf.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer sweep() {
|
||||
buf.sweep();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer sweep(byte value) {
|
||||
buf.sweep(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer flip() {
|
||||
buf.flip();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer rewind() {
|
||||
buf.rewind();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int remaining() {
|
||||
return buf.remaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRemaining() {
|
||||
return buf.hasRemaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte get() {
|
||||
return buf.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getUnsigned() {
|
||||
return buf.getUnsigned();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer put(byte b) {
|
||||
buf.put(b);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte get(int index) {
|
||||
return buf.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getUnsigned(int index) {
|
||||
return buf.getUnsigned(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer put(int index, byte b) {
|
||||
buf.put(index, b);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer get(byte[] dst, int offset, int length) {
|
||||
buf.get(dst, offset, length);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer getSlice(int index, int length) {
|
||||
return buf.getSlice(index, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer getSlice(int length) {
|
||||
return buf.getSlice(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer get(byte[] dst) {
|
||||
buf.get(dst);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer put(IoBuffer src) {
|
||||
buf.put(src);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer put(ByteBuffer src) {
|
||||
buf.put(src);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer put(byte[] src, int offset, int length) {
|
||||
buf.put(src, offset, length);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer put(byte[] src) {
|
||||
buf.put(src);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer compact() {
|
||||
buf.compact();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return buf.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
return buf.equals(ob);
|
||||
}
|
||||
|
||||
public int compareTo(IoBuffer that) {
|
||||
return buf.compareTo(that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteOrder order() {
|
||||
return buf.order();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer order(ByteOrder bo) {
|
||||
buf.order(bo);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getChar() {
|
||||
return buf.getChar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putChar(char value) {
|
||||
buf.putChar(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getChar(int index) {
|
||||
return buf.getChar(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putChar(int index, char value) {
|
||||
buf.putChar(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharBuffer asCharBuffer() {
|
||||
return buf.asCharBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort() {
|
||||
return buf.getShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnsignedShort() {
|
||||
return buf.getUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putShort(short value) {
|
||||
buf.putShort(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int index) {
|
||||
return buf.getShort(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnsignedShort(int index) {
|
||||
return buf.getUnsignedShort(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putShort(int index, short value) {
|
||||
buf.putShort(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShortBuffer asShortBuffer() {
|
||||
return buf.asShortBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt() {
|
||||
return buf.getInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUnsignedInt() {
|
||||
return buf.getUnsignedInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putInt(int value) {
|
||||
buf.putInt(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int index) {
|
||||
return buf.getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUnsignedInt(int index) {
|
||||
return buf.getUnsignedInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putInt(int index, int value) {
|
||||
buf.putInt(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntBuffer asIntBuffer() {
|
||||
return buf.asIntBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong() {
|
||||
return buf.getLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putLong(long value) {
|
||||
buf.putLong(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int index) {
|
||||
return buf.getLong(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putLong(int index, long value) {
|
||||
buf.putLong(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongBuffer asLongBuffer() {
|
||||
return buf.asLongBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat() {
|
||||
return buf.getFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putFloat(float value) {
|
||||
buf.putFloat(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int index) {
|
||||
return buf.getFloat(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putFloat(int index, float value) {
|
||||
buf.putFloat(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatBuffer asFloatBuffer() {
|
||||
return buf.asFloatBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble() {
|
||||
return buf.getDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putDouble(double value) {
|
||||
buf.putDouble(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return buf.getDouble(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putDouble(int index, double value) {
|
||||
buf.putDouble(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleBuffer asDoubleBuffer() {
|
||||
return buf.asDoubleBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHexDump() {
|
||||
return buf.getHexDump();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int fieldSize, CharsetDecoder decoder) throws CharacterCodingException {
|
||||
return buf.getString(fieldSize, decoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(CharsetDecoder decoder) throws CharacterCodingException {
|
||||
return buf.getString(decoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefixedString(CharsetDecoder decoder) throws CharacterCodingException {
|
||||
return buf.getPrefixedString(decoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefixedString(int prefixLength, CharsetDecoder decoder)
|
||||
throws CharacterCodingException {
|
||||
return buf.getPrefixedString(prefixLength, decoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putString(CharSequence in, int fieldSize, CharsetEncoder encoder)
|
||||
throws CharacterCodingException {
|
||||
buf.putString(in, fieldSize, encoder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putString(CharSequence in, CharsetEncoder encoder)
|
||||
throws CharacterCodingException {
|
||||
buf.putString(in, encoder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putPrefixedString(CharSequence in, CharsetEncoder encoder)
|
||||
throws CharacterCodingException {
|
||||
buf.putPrefixedString(in, encoder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putPrefixedString(CharSequence in, int prefixLength, CharsetEncoder encoder)
|
||||
throws CharacterCodingException {
|
||||
buf.putPrefixedString(in, prefixLength, encoder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putPrefixedString(CharSequence in, int prefixLength, int padding,
|
||||
CharsetEncoder encoder) throws CharacterCodingException {
|
||||
buf.putPrefixedString(in, prefixLength, padding, encoder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putPrefixedString(CharSequence in, int prefixLength, int padding, byte padValue,
|
||||
CharsetEncoder encoder) throws CharacterCodingException {
|
||||
buf.putPrefixedString(in, prefixLength, padding, padValue, encoder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer skip(int size) {
|
||||
buf.skip(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer fill(byte value, int size) {
|
||||
buf.fill(value, size);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer fillAndReset(byte value, int size) {
|
||||
buf.fillAndReset(value, size);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer fill(int size) {
|
||||
buf.fill(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer fillAndReset(int size) {
|
||||
buf.fillAndReset(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoExpand() {
|
||||
return buf.isAutoExpand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer setAutoExpand(boolean autoExpand) {
|
||||
buf.setAutoExpand(autoExpand);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer expand(int pos, int expectedRemaining) {
|
||||
buf.expand(pos, expectedRemaining);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer expand(int expectedRemaining) {
|
||||
buf.expand(expectedRemaining);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject() throws ClassNotFoundException {
|
||||
return buf.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(ClassLoader classLoader) throws ClassNotFoundException {
|
||||
return buf.getObject(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putObject(Object o) {
|
||||
buf.putObject(o);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream asInputStream() {
|
||||
return buf.asInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream asOutputStream() {
|
||||
return buf.asOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer duplicate() {
|
||||
return buf.duplicate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer slice() {
|
||||
return buf.slice();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer asReadOnlyBuffer() {
|
||||
return buf.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] array() {
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int arrayOffset() {
|
||||
return buf.arrayOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minimumCapacity() {
|
||||
return buf.minimumCapacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer minimumCapacity(int minimumCapacity) {
|
||||
buf.minimumCapacity(minimumCapacity);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer capacity(int newCapacity) {
|
||||
buf.capacity(newCapacity);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return buf.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int markValue() {
|
||||
return buf.markValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasArray() {
|
||||
return buf.hasArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
buf.free();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDerived() {
|
||||
return buf.isDerived();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoShrink() {
|
||||
return buf.isAutoShrink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer setAutoShrink(boolean autoShrink) {
|
||||
buf.setAutoShrink(autoShrink);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer shrink() {
|
||||
buf.shrink();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMediumInt() {
|
||||
return buf.getMediumInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnsignedMediumInt() {
|
||||
return buf.getUnsignedMediumInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMediumInt(int index) {
|
||||
return buf.getMediumInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnsignedMediumInt(int index) {
|
||||
return buf.getUnsignedMediumInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putMediumInt(int value) {
|
||||
buf.putMediumInt(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putMediumInt(int index, int value) {
|
||||
buf.putMediumInt(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHexDump(int lengthLimit) {
|
||||
return buf.getHexDump(lengthLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prefixedDataAvailable(int prefixLength) {
|
||||
return buf.prefixedDataAvailable(prefixLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prefixedDataAvailable(int prefixLength, int maxDataLength) {
|
||||
return buf.prefixedDataAvailable(prefixLength, maxDataLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(byte b) {
|
||||
return buf.indexOf(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E getEnum(Class<E> enumClass) {
|
||||
return buf.getEnum(enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E getEnum(int index, Class<E> enumClass) {
|
||||
return buf.getEnum(index, enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E getEnumShort(Class<E> enumClass) {
|
||||
return buf.getEnumShort(enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E getEnumShort(int index, Class<E> enumClass) {
|
||||
return buf.getEnumShort(index, enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E getEnumInt(Class<E> enumClass) {
|
||||
return buf.getEnumInt(enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E getEnumInt(int index, Class<E> enumClass) {
|
||||
return buf.getEnumInt(index, enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putEnum(Enum<?> e) {
|
||||
buf.putEnum(e);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putEnum(int index, Enum<?> e) {
|
||||
buf.putEnum(index, e);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putEnumShort(Enum<?> e) {
|
||||
buf.putEnumShort(e);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putEnumShort(int index, Enum<?> e) {
|
||||
buf.putEnumShort(index, e);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putEnumInt(Enum<?> e) {
|
||||
buf.putEnumInt(e);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoBuffer putEnumInt(int index, Enum<?> e) {
|
||||
buf.putEnumInt(index, e);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> EnumSet<E> getEnumSet(Class<E> enumClass) {
|
||||
return buf.getEnumSet(enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> EnumSet<E> getEnumSet(int index, Class<E> enumClass) {
|
||||
return buf.getEnumSet(index, enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> EnumSet<E> getEnumSetShort(Class<E> enumClass) {
|
||||
return buf.getEnumSetShort(enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> EnumSet<E> getEnumSetShort(int index, Class<E> enumClass) {
|
||||
return buf.getEnumSetShort(index, enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> EnumSet<E> getEnumSetInt(Class<E> enumClass) {
|
||||
return buf.getEnumSetInt(enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> EnumSet<E> getEnumSetInt(int index, Class<E> enumClass) {
|
||||
return buf.getEnumSetInt(index, enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> EnumSet<E> getEnumSetLong(Class<E> enumClass) {
|
||||
return buf.getEnumSetLong(enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> EnumSet<E> getEnumSetLong(int index, Class<E> enumClass) {
|
||||
return buf.getEnumSetLong(index, enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> IoBuffer putEnumSet(Set<E> set) {
|
||||
buf.putEnumSet(set);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> IoBuffer putEnumSet(int index, Set<E> set) {
|
||||
buf.putEnumSet(index, set);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> IoBuffer putEnumSetShort(Set<E> set) {
|
||||
buf.putEnumSetShort(set);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> IoBuffer putEnumSetShort(int index, Set<E> set) {
|
||||
buf.putEnumSetShort(index, set);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> IoBuffer putEnumSetInt(Set<E> set) {
|
||||
buf.putEnumSetInt(set);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> IoBuffer putEnumSetInt(int index, Set<E> set) {
|
||||
buf.putEnumSetInt(index, set);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> IoBuffer putEnumSetLong(Set<E> set) {
|
||||
buf.putEnumSetLong(set);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> IoBuffer putEnumSetLong(int index, Set<E> set) {
|
||||
buf.putEnumSetLong(index, set);
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work for additional information regarding
|
||||
* copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.code.yanf4j.buffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* A simplistic {@link IoBufferAllocator} which simply allocates a new buffer every time.
|
||||
*
|
||||
* @author The Apache MINA Project (dev@mina.apache.org)
|
||||
* @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (Thu, 26 Jun 2008) $
|
||||
*/
|
||||
public class SimpleBufferAllocator implements IoBufferAllocator {
|
||||
|
||||
public IoBuffer allocate(int capacity, boolean direct) {
|
||||
return wrap(allocateNioBuffer(capacity, direct));
|
||||
}
|
||||
|
||||
public ByteBuffer allocateNioBuffer(int capacity, boolean direct) {
|
||||
ByteBuffer nioBuffer;
|
||||
if (direct) {
|
||||
nioBuffer = ByteBuffer.allocateDirect(capacity);
|
||||
} else {
|
||||
nioBuffer = ByteBuffer.allocate(capacity);
|
||||
}
|
||||
return nioBuffer;
|
||||
}
|
||||
|
||||
public IoBuffer wrap(ByteBuffer nioBuffer) {
|
||||
return new SimpleBuffer(nioBuffer);
|
||||
}
|
||||
|
||||
public void dispose() {}
|
||||
|
||||
private class SimpleBuffer extends AbstractIoBuffer {
|
||||
private ByteBuffer buf;
|
||||
|
||||
protected SimpleBuffer(ByteBuffer buf) {
|
||||
super(SimpleBufferAllocator.this, buf.capacity());
|
||||
this.buf = buf;
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
|
||||
protected SimpleBuffer(SimpleBuffer parent, ByteBuffer buf) {
|
||||
super(parent);
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer buf() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buf(ByteBuffer buf) {
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IoBuffer duplicate0() {
|
||||
return new SimpleBuffer(this, this.buf.duplicate());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IoBuffer slice0() {
|
||||
return new SimpleBuffer(this, this.buf.slice());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IoBuffer asReadOnlyBuffer0() {
|
||||
return new SimpleBuffer(this, this.buf.asReadOnlyBuffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] array() {
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int arrayOffset() {
|
||||
return buf.arrayOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasArray() {
|
||||
return buf.hasArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {}
|
||||
}
|
||||
}
|
10
src/main/java/com/google/code/yanf4j/buffer/package.html
Normal file
10
src/main/java/com/google/code/yanf4j/buffer/package.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!--?xml version="1.0" encoding="UTF-8"?--><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!-- Copyright (c) 2007 Dustin Sallings <dustin+html@spy.net> -->
|
||||
<html>
|
||||
<head>
|
||||
<title>IoBuffer</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>IoBuffer from mina</h1>
|
||||
</body>
|
||||
</html>
|
191
src/main/java/com/google/code/yanf4j/config/Configuration.java
Normal file
191
src/main/java/com/google/code/yanf4j/config/Configuration.java
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang] Licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
|
||||
* law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
|
||||
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.config;
|
||||
|
||||
import net.rubyeye.xmemcached.impl.ReconnectRequest;
|
||||
import java.util.concurrent.DelayQueue;
|
||||
import com.google.code.yanf4j.util.SystemUtils;
|
||||
|
||||
/**
|
||||
* Networking configuration
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class Configuration {
|
||||
|
||||
public static final String XMEMCACHED_SELECTOR_POOL_SIZE = "xmemcached.selector.pool.size";
|
||||
|
||||
/**
|
||||
* Read buffer size per connection
|
||||
*/
|
||||
private int sessionReadBufferSize = 32 * 1024;
|
||||
|
||||
/**
|
||||
* Socket SO_TIMEOUT option
|
||||
*/
|
||||
private int soTimeout = 0;
|
||||
|
||||
/**
|
||||
* Thread count for processing WRITABLE event
|
||||
*/
|
||||
private int writeThreadCount = 0;
|
||||
|
||||
/**
|
||||
* Whether to enable statistics
|
||||
*/
|
||||
private boolean statisticsServer = false;
|
||||
|
||||
/**
|
||||
* Whether to handle read write concurrently,default is true
|
||||
*/
|
||||
private boolean handleReadWriteConcurrently = true;
|
||||
|
||||
/**
|
||||
* Thread coount for processing message dispatching
|
||||
*/
|
||||
private int dispatchMessageThreadCount = 0;
|
||||
|
||||
/**
|
||||
* THread count for processing READABLE event
|
||||
*/
|
||||
private int readThreadCount = 1;
|
||||
|
||||
private int selectorPoolSize =
|
||||
System.getProperty(XMEMCACHED_SELECTOR_POOL_SIZE) == null ? SystemUtils.getSystemThreadCount()
|
||||
: Integer.parseInt(System.getProperty(XMEMCACHED_SELECTOR_POOL_SIZE));
|
||||
|
||||
/**
|
||||
* Increasing buffer size per time
|
||||
*/
|
||||
public static final int DEFAULT_INCREASE_BUFF_SIZE = 32 * 1024;
|
||||
|
||||
/**
|
||||
* Max read buffer size for connection
|
||||
*/
|
||||
public final static int MAX_READ_BUFFER_SIZE = 128 * 1024;
|
||||
|
||||
/**
|
||||
* check session idle interval
|
||||
*/
|
||||
private long checkSessionTimeoutInterval = 1000L;
|
||||
|
||||
public final int getWriteThreadCount() {
|
||||
return this.writeThreadCount;
|
||||
}
|
||||
|
||||
public final int getDispatchMessageThreadCount() {
|
||||
return this.dispatchMessageThreadCount;
|
||||
}
|
||||
|
||||
public final void setDispatchMessageThreadCount(int dispatchMessageThreadCount) {
|
||||
this.dispatchMessageThreadCount = dispatchMessageThreadCount;
|
||||
}
|
||||
|
||||
public final void setWriteThreadCount(int writeThreadCount) {
|
||||
this.writeThreadCount = writeThreadCount;
|
||||
}
|
||||
|
||||
private long sessionIdleTimeout = 5000L;
|
||||
|
||||
/**
|
||||
* @see setSessionIdleTimeout
|
||||
* @return
|
||||
*/
|
||||
public final long getSessionIdleTimeout() {
|
||||
return this.sessionIdleTimeout;
|
||||
}
|
||||
|
||||
public final void setSessionIdleTimeout(long sessionIdleTimeout) {
|
||||
this.sessionIdleTimeout = sessionIdleTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see setSessionReadBufferSize
|
||||
* @return
|
||||
*/
|
||||
public final int getSessionReadBufferSize() {
|
||||
return this.sessionReadBufferSize;
|
||||
}
|
||||
|
||||
public final boolean isHandleReadWriteConcurrently() {
|
||||
return this.handleReadWriteConcurrently;
|
||||
}
|
||||
|
||||
public final int getSoTimeout() {
|
||||
return this.soTimeout;
|
||||
}
|
||||
|
||||
protected long statisticsInterval = 5 * 60 * 1000L;
|
||||
|
||||
public final long getStatisticsInterval() {
|
||||
return this.statisticsInterval;
|
||||
}
|
||||
|
||||
public final void setStatisticsInterval(long statisticsInterval) {
|
||||
this.statisticsInterval = statisticsInterval;
|
||||
}
|
||||
|
||||
public final void setSoTimeout(int soTimeout) {
|
||||
if (soTimeout < 0) {
|
||||
throw new IllegalArgumentException("soTimeout<0");
|
||||
}
|
||||
this.soTimeout = soTimeout;
|
||||
}
|
||||
|
||||
public final void setHandleReadWriteConcurrently(boolean handleReadWriteConcurrently) {
|
||||
this.handleReadWriteConcurrently = handleReadWriteConcurrently;
|
||||
}
|
||||
|
||||
public final void setSessionReadBufferSize(int tcpHandlerReadBufferSize) {
|
||||
if (tcpHandlerReadBufferSize <= 0) {
|
||||
throw new IllegalArgumentException("tcpHandlerReadBufferSize<=0");
|
||||
}
|
||||
this.sessionReadBufferSize = tcpHandlerReadBufferSize;
|
||||
}
|
||||
|
||||
public final boolean isStatisticsServer() {
|
||||
return this.statisticsServer;
|
||||
}
|
||||
|
||||
public final void setStatisticsServer(boolean statisticsServer) {
|
||||
this.statisticsServer = statisticsServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see setReadThreadCount
|
||||
* @return
|
||||
*/
|
||||
public final int getReadThreadCount() {
|
||||
return this.readThreadCount;
|
||||
}
|
||||
|
||||
public final void setReadThreadCount(int readThreadCount) {
|
||||
if (readThreadCount < 0) {
|
||||
throw new IllegalArgumentException("readThreadCount<0");
|
||||
}
|
||||
this.readThreadCount = readThreadCount;
|
||||
}
|
||||
|
||||
public void setCheckSessionTimeoutInterval(long checkSessionTimeoutInterval) {
|
||||
this.checkSessionTimeoutInterval = checkSessionTimeoutInterval;
|
||||
}
|
||||
|
||||
public long getCheckSessionTimeoutInterval() {
|
||||
return this.checkSessionTimeoutInterval;
|
||||
}
|
||||
|
||||
public void setSelectorPoolSize(int selectorPoolSize) {
|
||||
this.selectorPoolSize = selectorPoolSize;
|
||||
}
|
||||
|
||||
public int getSelectorPoolSize() {
|
||||
return selectorPoolSize;
|
||||
}
|
||||
}
|
10
src/main/java/com/google/code/yanf4j/config/package.html
Normal file
10
src/main/java/com/google/code/yanf4j/config/package.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!--?xml version="1.0" encoding="UTF-8"?--><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!-- Copyright (c) 2007 Dustin Sallings <dustin+html@spy.net> -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Networking configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Networking configuration</h1>
|
||||
</body>
|
||||
</html>
|
42
src/main/java/com/google/code/yanf4j/core/CodecFactory.java
Normal file
42
src/main/java/com/google/code/yanf4j/core/CodecFactory.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Codec factory
|
||||
*
|
||||
* @author boyan
|
||||
*
|
||||
*/
|
||||
public interface CodecFactory {
|
||||
|
||||
public interface Encoder {
|
||||
public IoBuffer encode(Object message, Session session);
|
||||
}
|
||||
|
||||
public interface Decoder {
|
||||
public Object decode(IoBuffer buff, Session session);
|
||||
}
|
||||
|
||||
public Encoder getEncoder();
|
||||
|
||||
public Decoder getDecoder();
|
||||
}
|
96
src/main/java/com/google/code/yanf4j/core/Controller.java
Normal file
96
src/main/java/com/google/code/yanf4j/core/Controller.java
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import com.google.code.yanf4j.statistics.Statistics;
|
||||
|
||||
/**
|
||||
* Networking Controller
|
||||
*
|
||||
*
|
||||
* @author boyan
|
||||
*
|
||||
*/
|
||||
public interface Controller {
|
||||
|
||||
public abstract long getSessionTimeout();
|
||||
|
||||
public long getSessionIdleTimeout();
|
||||
|
||||
public void setSessionIdleTimeout(long sessionIdleTimeout);
|
||||
|
||||
public abstract void setSessionTimeout(long sessionTimeout);
|
||||
|
||||
public abstract int getSoTimeout();
|
||||
|
||||
public abstract void setSoTimeout(int timeout);
|
||||
|
||||
public abstract void addStateListener(ControllerStateListener listener);
|
||||
|
||||
public void removeStateListener(ControllerStateListener listener);
|
||||
|
||||
public abstract boolean isHandleReadWriteConcurrently();
|
||||
|
||||
public abstract void setHandleReadWriteConcurrently(boolean handleReadWriteConcurrently);
|
||||
|
||||
public abstract int getReadThreadCount();
|
||||
|
||||
public abstract void setReadThreadCount(int readThreadCount);
|
||||
|
||||
public abstract Handler getHandler();
|
||||
|
||||
public abstract void setHandler(Handler handler);
|
||||
|
||||
public abstract int getPort();
|
||||
|
||||
public abstract void start() throws IOException;
|
||||
|
||||
public abstract boolean isStarted();
|
||||
|
||||
public abstract Statistics getStatistics();
|
||||
|
||||
public abstract CodecFactory getCodecFactory();
|
||||
|
||||
public abstract void setCodecFactory(CodecFactory codecFactory);
|
||||
|
||||
public abstract void stop() throws IOException;
|
||||
|
||||
public void setReceiveThroughputLimit(double receivePacketRate);
|
||||
|
||||
public double getReceiveThroughputLimit();
|
||||
|
||||
public double getSendThroughputLimit();
|
||||
|
||||
public void setSendThroughputLimit(double sendThroughputLimit);
|
||||
|
||||
public InetSocketAddress getLocalSocketAddress();
|
||||
|
||||
public void setLocalSocketAddress(InetSocketAddress inetAddress);
|
||||
|
||||
public int getDispatchMessageThreadCount();
|
||||
|
||||
public void setDispatchMessageThreadCount(int dispatchMessageThreadPoolSize);
|
||||
|
||||
public int getWriteThreadCount();
|
||||
|
||||
public void setWriteThreadCount(int writeThreadCount);
|
||||
|
||||
public <T> void setSocketOption(SocketOption<T> socketOption, T value);
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
/**
|
||||
* Controller lifecycle mark interface
|
||||
*
|
||||
* @author boyan
|
||||
*
|
||||
*/
|
||||
|
||||
public interface ControllerLifeCycle {
|
||||
|
||||
public void notifyReady();
|
||||
|
||||
public void notifyStarted();
|
||||
|
||||
public void notifyAllSessionClosed();
|
||||
|
||||
public void notifyException(Throwable t);
|
||||
|
||||
public void notifyStopped();
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
/**
|
||||
*
|
||||
* Controller state listener
|
||||
*
|
||||
* @author boyan
|
||||
*
|
||||
* @since 1.0, 2009-12-16 <20><><EFBFBD><EFBFBD>05:59:44
|
||||
*/
|
||||
public interface ControllerStateListener {
|
||||
|
||||
/**
|
||||
* When controller is started
|
||||
*
|
||||
* @param controller
|
||||
*/
|
||||
public void onStarted(final Controller controller);
|
||||
|
||||
/**
|
||||
* When controller is ready
|
||||
*
|
||||
* @param controller
|
||||
*/
|
||||
public void onReady(final Controller controller);
|
||||
|
||||
/**
|
||||
* When all connections are closed
|
||||
*
|
||||
* @param controller
|
||||
*/
|
||||
public void onAllSessionClosed(final Controller controller);
|
||||
|
||||
/**
|
||||
* When controller has been stopped
|
||||
*
|
||||
* @param controller
|
||||
*/
|
||||
public void onStopped(final Controller controller);
|
||||
|
||||
public void onException(final Controller controller, Throwable t);
|
||||
}
|
29
src/main/java/com/google/code/yanf4j/core/Dispatcher.java
Normal file
29
src/main/java/com/google/code/yanf4j/core/Dispatcher.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
/**
|
||||
* Dispatcher
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface Dispatcher {
|
||||
public void dispatch(Runnable r);
|
||||
|
||||
public void stop();
|
||||
}
|
27
src/main/java/com/google/code/yanf4j/core/EventType.java
Normal file
27
src/main/java/com/google/code/yanf4j/core/EventType.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
/**
|
||||
* Event Type
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public enum EventType {
|
||||
REGISTER, READABLE, WRITEABLE, ENABLE_READ, ENABLE_WRITE, UNREGISTER, EXPIRED, IDLE, CONNECTED
|
||||
}
|
46
src/main/java/com/google/code/yanf4j/core/Handler.java
Normal file
46
src/main/java/com/google/code/yanf4j/core/Handler.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
/**
|
||||
*
|
||||
* IO Event handler
|
||||
*
|
||||
* @author boyan
|
||||
*
|
||||
*/
|
||||
public interface Handler {
|
||||
|
||||
public void onSessionCreated(Session session);
|
||||
|
||||
public void onSessionStarted(Session session);
|
||||
|
||||
public void onSessionClosed(Session session);
|
||||
|
||||
public void onMessageReceived(Session session, Object msg);
|
||||
|
||||
public void onMessageSent(Session session, Object msg);
|
||||
|
||||
public void onExceptionCaught(Session session, Throwable throwable);
|
||||
|
||||
public void onSessionExpired(Session session);
|
||||
|
||||
public void onSessionIdle(Session session);
|
||||
|
||||
public void onSessionConnected(Session session);
|
||||
|
||||
}
|
216
src/main/java/com/google/code/yanf4j/core/Session.java
Normal file
216
src/main/java/com/google/code/yanf4j/core/Session.java
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* Abstract connection
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface Session {
|
||||
|
||||
public enum SessionStatus {
|
||||
NULL, READING, WRITING, IDLE, INITIALIZE, CLOSING, CLOSED
|
||||
}
|
||||
|
||||
/**
|
||||
* Start session
|
||||
*/
|
||||
public void start();
|
||||
|
||||
/**
|
||||
* Write a message,if you don't care when the message is written
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
public void write(Object packet);
|
||||
|
||||
/**
|
||||
* Check if session is closed
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isClosed();
|
||||
|
||||
/**
|
||||
* Close session
|
||||
*/
|
||||
public void close();
|
||||
|
||||
/**
|
||||
* Return the remote end's InetSocketAddress
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public InetSocketAddress getRemoteSocketAddress();
|
||||
|
||||
public InetAddress getLocalAddress();
|
||||
|
||||
/**
|
||||
* Return true if using blocking write
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isUseBlockingWrite();
|
||||
|
||||
/**
|
||||
* Set if using blocking write
|
||||
*
|
||||
* @param useBlockingWrite
|
||||
*/
|
||||
public void setUseBlockingWrite(boolean useBlockingWrite);
|
||||
|
||||
/**
|
||||
* Return true if using blocking read
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isUseBlockingRead();
|
||||
|
||||
public void setUseBlockingRead(boolean useBlockingRead);
|
||||
|
||||
/**
|
||||
* Flush the write queue,this method may be no effect if OP_WRITE is running.
|
||||
*/
|
||||
public void flush();
|
||||
|
||||
/**
|
||||
* Return true if session is expired,session is expired beacause you set the sessionTimeout,if
|
||||
* since session's last operation form now is over this vlaue,isExpired return true,and
|
||||
* Handler.onExpired() will be invoked.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isExpired();
|
||||
|
||||
/**
|
||||
* Check if session is idle
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isIdle();
|
||||
|
||||
/**
|
||||
* Return current encoder
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public CodecFactory.Encoder getEncoder();
|
||||
|
||||
/**
|
||||
* Set encoder
|
||||
*
|
||||
* @param encoder
|
||||
*/
|
||||
public void setEncoder(CodecFactory.Encoder encoder);
|
||||
|
||||
/**
|
||||
* Return current decoder
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
public CodecFactory.Decoder getDecoder();
|
||||
|
||||
public void setDecoder(CodecFactory.Decoder decoder);
|
||||
|
||||
/**
|
||||
* Return true if allow handling read and write concurrently,default is true.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isHandleReadWriteConcurrently();
|
||||
|
||||
public void setHandleReadWriteConcurrently(boolean handleReadWriteConcurrently);
|
||||
|
||||
/**
|
||||
* Return the session read buffer's byte order,big end or little end.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ByteOrder getReadBufferByteOrder();
|
||||
|
||||
public void setReadBufferByteOrder(ByteOrder readBufferByteOrder);
|
||||
|
||||
/**
|
||||
* Set a attribute attched with this session
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setAttribute(String key, Object value);
|
||||
|
||||
/**
|
||||
* Remove attribute
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public void removeAttribute(String key);
|
||||
|
||||
/**
|
||||
* Return attribute associated with key
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Object getAttribute(String key);
|
||||
|
||||
/**
|
||||
* Clear attributes
|
||||
*/
|
||||
public void clearAttributes();
|
||||
|
||||
/**
|
||||
* Return the bytes in write queue,there bytes is in memory.Use this method to controll writing
|
||||
* speed.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getScheduleWritenBytes();
|
||||
|
||||
/**
|
||||
* Return last operation timestamp,operation include read,write,idle
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getLastOperationTimeStamp();
|
||||
|
||||
/**
|
||||
* return true if it is a loopback connection
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isLoopbackConnection();
|
||||
|
||||
public long getSessionIdleTimeout();
|
||||
|
||||
public void setSessionIdleTimeout(long sessionIdleTimeout);
|
||||
|
||||
public long getSessionTimeout();
|
||||
|
||||
public void setSessionTimeout(long sessionTimeout);
|
||||
|
||||
public Object setAttributeIfAbsent(String key, Object value);
|
||||
|
||||
public Handler getHandler();
|
||||
|
||||
}
|
51
src/main/java/com/google/code/yanf4j/core/SessionConfig.java
Normal file
51
src/main/java/com/google/code/yanf4j/core/SessionConfig.java
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
import java.util.Queue;
|
||||
import com.google.code.yanf4j.statistics.Statistics;
|
||||
|
||||
/**
|
||||
* Session configuration
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class SessionConfig {
|
||||
public final Handler handler;
|
||||
public final CodecFactory codecFactory;
|
||||
public final Statistics statistics;
|
||||
public final Queue<WriteMessage> queue;
|
||||
public final Dispatcher dispatchMessageDispatcher;
|
||||
public final boolean handleReadWriteConcurrently;
|
||||
public final long sessionTimeout;
|
||||
public final long sessionIdelTimeout;
|
||||
|
||||
public SessionConfig(Handler handler, CodecFactory codecFactory, Statistics statistics,
|
||||
Queue<WriteMessage> queue, Dispatcher dispatchMessageDispatcher,
|
||||
boolean handleReadWriteConcurrently, long sessionTimeout, long sessionIdelTimeout) {
|
||||
|
||||
this.handler = handler;
|
||||
this.codecFactory = codecFactory;
|
||||
this.statistics = statistics;
|
||||
this.queue = queue;
|
||||
this.dispatchMessageDispatcher = dispatchMessageDispatcher;
|
||||
this.handleReadWriteConcurrently = handleReadWriteConcurrently;
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
this.sessionIdelTimeout = sessionIdelTimeout;
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
/**
|
||||
* Session manager
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface SessionManager {
|
||||
/**
|
||||
* Register session to controller
|
||||
*
|
||||
* @param session
|
||||
*/
|
||||
public void registerSession(Session session);
|
||||
|
||||
/**
|
||||
* Unregister session
|
||||
*
|
||||
* @param session
|
||||
*/
|
||||
public void unregisterSession(Session session);
|
||||
}
|
79
src/main/java/com/google/code/yanf4j/core/SocketOption.java
Normal file
79
src/main/java/com/google/code/yanf4j/core/SocketOption.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
/**
|
||||
* Socket option
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class SocketOption<T> {
|
||||
|
||||
private final String name;
|
||||
private final Class<T> type;
|
||||
|
||||
public SocketOption(String name, Class<T> type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (this.name == null ? 0 : this.name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SocketOption other = (SocketOption) obj;
|
||||
if (this.name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Class<T> type() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
42
src/main/java/com/google/code/yanf4j/core/WriteMessage.java
Normal file
42
src/main/java/com/google/code/yanf4j/core/WriteMessage.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core;
|
||||
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
import com.google.code.yanf4j.core.impl.FutureImpl;
|
||||
|
||||
/**
|
||||
* Write message with a buffer
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface WriteMessage {
|
||||
|
||||
public void writing();
|
||||
|
||||
public boolean isWriting();
|
||||
|
||||
public IoBuffer getWriteBuffer();
|
||||
|
||||
public Object getMessage();
|
||||
|
||||
public void setWriteBuffer(IoBuffer buffers);
|
||||
|
||||
public FutureImpl<Boolean> getWriteFuture();
|
||||
|
||||
}
|
@@ -0,0 +1,541 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import net.rubyeye.xmemcached.utils.AddrUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.CodecFactory;
|
||||
import com.google.code.yanf4j.core.Controller;
|
||||
import com.google.code.yanf4j.core.ControllerLifeCycle;
|
||||
import com.google.code.yanf4j.core.ControllerStateListener;
|
||||
import com.google.code.yanf4j.core.Dispatcher;
|
||||
import com.google.code.yanf4j.core.Handler;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
import com.google.code.yanf4j.core.SocketOption;
|
||||
import com.google.code.yanf4j.core.WriteMessage;
|
||||
import com.google.code.yanf4j.statistics.Statistics;
|
||||
import com.google.code.yanf4j.statistics.impl.DefaultStatistics;
|
||||
import com.google.code.yanf4j.statistics.impl.SimpleStatistics;
|
||||
import com.google.code.yanf4j.util.ConcurrentHashSet;
|
||||
import com.google.code.yanf4j.util.DispatcherFactory;
|
||||
import com.google.code.yanf4j.util.LinkedTransferQueue;
|
||||
|
||||
/**
|
||||
* Base controller
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractController implements Controller, ControllerLifeCycle {
|
||||
|
||||
protected Statistics statistics = new DefaultStatistics();
|
||||
protected long statisticsInterval;
|
||||
|
||||
protected static final Logger log = LoggerFactory.getLogger(AbstractController.class);
|
||||
/**
|
||||
* controller state listener list
|
||||
*/
|
||||
protected CopyOnWriteArrayList<ControllerStateListener> stateListeners =
|
||||
new CopyOnWriteArrayList<ControllerStateListener>();
|
||||
/**
|
||||
* Event handler
|
||||
*/
|
||||
protected Handler handler;
|
||||
/**
|
||||
* Codec Factory
|
||||
*/
|
||||
|
||||
protected CodecFactory codecFactory;
|
||||
/**
|
||||
* Status
|
||||
*/
|
||||
protected volatile boolean started;
|
||||
/**
|
||||
* local bind address
|
||||
*/
|
||||
protected InetSocketAddress localSocketAddress;
|
||||
/**
|
||||
* Read event processing thread count
|
||||
*/
|
||||
protected int readThreadCount;
|
||||
protected int writeThreadCount;
|
||||
protected int dispatchMessageThreadCount;
|
||||
protected Configuration configuration;
|
||||
protected Dispatcher readEventDispatcher, dispatchMessageDispatcher, writeEventDispatcher;
|
||||
protected long sessionTimeout;
|
||||
protected boolean handleReadWriteConcurrently = true;
|
||||
|
||||
protected int soTimeout;
|
||||
|
||||
/**
|
||||
* Socket options
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<SocketOption, Object> socketOptions = new HashMap<SocketOption, Object>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setSocketOptions(Map<SocketOption, Object> socketOptions) {
|
||||
if (socketOptions == null) {
|
||||
throw new NullPointerException("Null socketOptions");
|
||||
}
|
||||
this.socketOptions = socketOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connected session set
|
||||
*/
|
||||
protected Set<Session> sessionSet = new ConcurrentHashSet<Session>();
|
||||
private Thread shutdownHookThread;
|
||||
private volatile boolean isHutdownHookCalled = false;
|
||||
|
||||
public final int getDispatchMessageThreadCount() {
|
||||
return dispatchMessageThreadCount;
|
||||
}
|
||||
|
||||
public final void setDispatchMessageThreadCount(int dispatchMessageThreadPoolSize) {
|
||||
if (started) {
|
||||
throw new IllegalStateException("Controller is started");
|
||||
}
|
||||
if (dispatchMessageThreadPoolSize < 0) {
|
||||
throw new IllegalArgumentException("dispatchMessageThreadPoolSize<0");
|
||||
}
|
||||
dispatchMessageThreadCount = dispatchMessageThreadPoolSize;
|
||||
}
|
||||
|
||||
public long getSessionIdleTimeout() {
|
||||
return configuration.getSessionIdleTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build write queue for session
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected Queue<WriteMessage> buildQueue() {
|
||||
return new LinkedTransferQueue<WriteMessage>();
|
||||
}
|
||||
|
||||
public void setSessionIdleTimeout(long sessionIdleTimeout) {
|
||||
configuration.setSessionIdleTimeout(sessionIdleTimeout);
|
||||
|
||||
}
|
||||
|
||||
public long getSessionTimeout() {
|
||||
return sessionTimeout;
|
||||
}
|
||||
|
||||
public void setSessionTimeout(long sessionTimeout) {
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
}
|
||||
|
||||
public int getSoTimeout() {
|
||||
return soTimeout;
|
||||
}
|
||||
|
||||
public void setSoTimeout(int timeout) {
|
||||
soTimeout = timeout;
|
||||
}
|
||||
|
||||
public AbstractController() {
|
||||
this(new Configuration(), null, null);
|
||||
}
|
||||
|
||||
public double getReceiveThroughputLimit() {
|
||||
return statistics.getReceiveThroughputLimit();
|
||||
}
|
||||
|
||||
public double getSendThroughputLimit() {
|
||||
return statistics.getSendThroughputLimit();
|
||||
}
|
||||
|
||||
public void setReceiveThroughputLimit(double receiveThroughputLimit) {
|
||||
statistics.setReceiveThroughputLimit(receiveThroughputLimit);
|
||||
|
||||
}
|
||||
|
||||
public void setSendThroughputLimit(double sendThroughputLimit) {
|
||||
statistics.setSendThroughputLimit(sendThroughputLimit);
|
||||
}
|
||||
|
||||
public AbstractController(Configuration configuration) {
|
||||
this(configuration, null, null);
|
||||
|
||||
}
|
||||
|
||||
public AbstractController(Configuration configuration, CodecFactory codecFactory) {
|
||||
this(configuration, null, codecFactory);
|
||||
}
|
||||
|
||||
public AbstractController(Configuration configuration, Handler handler,
|
||||
CodecFactory codecFactory) {
|
||||
init(configuration, handler, codecFactory);
|
||||
}
|
||||
|
||||
private synchronized void init(Configuration configuration, Handler handler,
|
||||
CodecFactory codecFactory) {
|
||||
setHandler(handler);
|
||||
setCodecFactory(codecFactory);
|
||||
setConfiguration(configuration);
|
||||
setReadThreadCount(configuration.getReadThreadCount());
|
||||
setWriteThreadCount(configuration.getWriteThreadCount());
|
||||
setDispatchMessageThreadCount(configuration.getDispatchMessageThreadCount());
|
||||
setHandleReadWriteConcurrently(configuration.isHandleReadWriteConcurrently());
|
||||
setSoTimeout(configuration.getSoTimeout());
|
||||
setStatisticsConfig(configuration);
|
||||
setReceiveThroughputLimit(-0.1d);
|
||||
setStarted(false);
|
||||
}
|
||||
|
||||
void setStarted(boolean started) {
|
||||
this.started = started;
|
||||
}
|
||||
|
||||
private void setStatisticsConfig(Configuration configuration) {
|
||||
if (configuration.isStatisticsServer()) {
|
||||
statistics = new SimpleStatistics();
|
||||
statisticsInterval = configuration.getStatisticsInterval();
|
||||
|
||||
} else {
|
||||
statistics = new DefaultStatistics();
|
||||
statisticsInterval = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
if (configuration == null) {
|
||||
throw new IllegalArgumentException("Null Configuration");
|
||||
}
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public InetSocketAddress getLocalSocketAddress() {
|
||||
return localSocketAddress;
|
||||
}
|
||||
|
||||
public void setLocalSocketAddress(InetSocketAddress inetSocketAddress) {
|
||||
localSocketAddress = inetSocketAddress;
|
||||
}
|
||||
|
||||
public void onAccept(SelectionKey sk) throws IOException {
|
||||
statistics.statisticsAccept();
|
||||
}
|
||||
|
||||
public void onConnect(SelectionKey key) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void addStateListener(ControllerStateListener listener) {
|
||||
stateListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeStateListener(ControllerStateListener listener) {
|
||||
stateListeners.remove(listener);
|
||||
}
|
||||
|
||||
public boolean isHandleReadWriteConcurrently() {
|
||||
return handleReadWriteConcurrently;
|
||||
}
|
||||
|
||||
public void setHandleReadWriteConcurrently(boolean handleReadWriteConcurrently) {
|
||||
this.handleReadWriteConcurrently = handleReadWriteConcurrently;
|
||||
}
|
||||
|
||||
public int getReadThreadCount() {
|
||||
return readThreadCount;
|
||||
}
|
||||
|
||||
public void setReadThreadCount(int readThreadCount) {
|
||||
if (started) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (readThreadCount < 0) {
|
||||
throw new IllegalArgumentException("readThreadCount<0");
|
||||
}
|
||||
this.readThreadCount = readThreadCount;
|
||||
}
|
||||
|
||||
public final int getWriteThreadCount() {
|
||||
return writeThreadCount;
|
||||
}
|
||||
|
||||
public final void setWriteThreadCount(int writeThreadCount) {
|
||||
if (started) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (writeThreadCount < 0) {
|
||||
throw new IllegalArgumentException("readThreadCount<0");
|
||||
}
|
||||
this.writeThreadCount = writeThreadCount;
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public void setHandler(Handler handler) {
|
||||
if (started) {
|
||||
throw new IllegalStateException("The Controller have started");
|
||||
}
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
if (localSocketAddress != null) {
|
||||
return localSocketAddress.getPort();
|
||||
}
|
||||
throw new NullPointerException("Controller is not binded");
|
||||
}
|
||||
|
||||
public synchronized void start() throws IOException {
|
||||
if (isStarted()) {
|
||||
return;
|
||||
}
|
||||
if (getHandler() == null) {
|
||||
throw new IOException("The handler is null");
|
||||
}
|
||||
if (getCodecFactory() == null) {
|
||||
setCodecFactory(new ByteBufferCodecFactory());
|
||||
}
|
||||
setStarted(true);
|
||||
setReadEventDispatcher(DispatcherFactory.newDispatcher(getReadThreadCount(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy(), "xmemcached-read-thread"));
|
||||
setWriteEventDispatcher(DispatcherFactory.newDispatcher(getWriteThreadCount(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy(), "xmemcached-write-thread"));
|
||||
setDispatchMessageDispatcher(DispatcherFactory.newDispatcher(getDispatchMessageThreadCount(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy(), "xmemcached-dispatch-thread"));
|
||||
startStatistics();
|
||||
start0();
|
||||
notifyStarted();
|
||||
if (AddrUtil.isEnableShutDownHook()) {
|
||||
shutdownHookThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
isHutdownHookCalled = true;
|
||||
AbstractController.this.stop();
|
||||
} catch (IOException e) {
|
||||
log.error("Stop controller fail", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHookThread);
|
||||
}
|
||||
log.info("The Controller started at " + localSocketAddress + " ...");
|
||||
}
|
||||
|
||||
protected abstract void start0() throws IOException;
|
||||
|
||||
void setDispatchMessageDispatcher(Dispatcher dispatcher) {
|
||||
Dispatcher oldDispatcher = dispatchMessageDispatcher;
|
||||
dispatchMessageDispatcher = dispatcher;
|
||||
if (oldDispatcher != null) {
|
||||
oldDispatcher.stop();
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher getReadEventDispatcher() {
|
||||
return readEventDispatcher;
|
||||
}
|
||||
|
||||
void setReadEventDispatcher(Dispatcher dispatcher) {
|
||||
Dispatcher oldDispatcher = readEventDispatcher;
|
||||
readEventDispatcher = dispatcher;
|
||||
if (oldDispatcher != null) {
|
||||
oldDispatcher.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void setWriteEventDispatcher(Dispatcher dispatcher) {
|
||||
Dispatcher oldDispatcher = writeEventDispatcher;
|
||||
writeEventDispatcher = dispatcher;
|
||||
if (oldDispatcher != null) {
|
||||
oldDispatcher.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private final void startStatistics() {
|
||||
statistics.start();
|
||||
}
|
||||
|
||||
public void notifyStarted() {
|
||||
for (ControllerStateListener stateListener : stateListeners) {
|
||||
stateListener.onStarted(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
public final Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
public final CodecFactory getCodecFactory() {
|
||||
return codecFactory;
|
||||
}
|
||||
|
||||
public final void setCodecFactory(CodecFactory codecFactory) {
|
||||
this.codecFactory = codecFactory;
|
||||
}
|
||||
|
||||
public void notifyReady() {
|
||||
for (ControllerStateListener stateListener : stateListeners) {
|
||||
stateListener.onReady(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final synchronized void unregisterSession(Session session) {
|
||||
sessionSet.remove(session);
|
||||
if (sessionSet.size() == 0) {
|
||||
notifyAllSessionClosed();
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void checkStatisticsForRestart() {
|
||||
if (statisticsInterval > 0
|
||||
&& System.currentTimeMillis() - statistics.getStartedTime() > statisticsInterval * 1000) {
|
||||
statistics.restart();
|
||||
}
|
||||
}
|
||||
|
||||
public final synchronized void registerSession(Session session) {
|
||||
if (started) {
|
||||
sessionSet.add(session);
|
||||
} else {
|
||||
session.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void stop() throws IOException {
|
||||
synchronized (this) {
|
||||
if (!isStarted()) {
|
||||
return;
|
||||
}
|
||||
setStarted(false);
|
||||
}
|
||||
for (Session session : sessionSet) {
|
||||
session.close();
|
||||
}
|
||||
stopStatistics();
|
||||
stopDispatcher();
|
||||
sessionSet.clear();
|
||||
notifyStopped();
|
||||
clearStateListeners();
|
||||
stop0();
|
||||
if (AddrUtil.isEnableShutDownHook() && shutdownHookThread != null && !isHutdownHookCalled) {
|
||||
Runtime.getRuntime().removeShutdownHook(shutdownHookThread);
|
||||
}
|
||||
log.info("Controller has been stopped.");
|
||||
}
|
||||
|
||||
protected abstract void stop0() throws IOException;
|
||||
|
||||
private final void stopDispatcher() {
|
||||
if (readEventDispatcher != null) {
|
||||
readEventDispatcher.stop();
|
||||
}
|
||||
if (dispatchMessageDispatcher != null) {
|
||||
dispatchMessageDispatcher.stop();
|
||||
}
|
||||
if (writeEventDispatcher != null) {
|
||||
writeEventDispatcher.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private final void stopStatistics() {
|
||||
statistics.stop();
|
||||
}
|
||||
|
||||
private final void clearStateListeners() {
|
||||
stateListeners.clear();
|
||||
}
|
||||
|
||||
public final void notifyException(Throwable t) {
|
||||
for (ControllerStateListener stateListener : stateListeners) {
|
||||
stateListener.onException(this, t);
|
||||
}
|
||||
}
|
||||
|
||||
public final void notifyStopped() {
|
||||
for (ControllerStateListener stateListener : stateListeners) {
|
||||
stateListener.onStopped(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final void notifyAllSessionClosed() {
|
||||
for (ControllerStateListener stateListener : stateListeners) {
|
||||
stateListener.onAllSessionClosed(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Session> getSessionSet() {
|
||||
return Collections.unmodifiableSet(sessionSet);
|
||||
}
|
||||
|
||||
public <T> void setSocketOption(SocketOption<T> socketOption, T value) {
|
||||
if (socketOption == null) {
|
||||
throw new NullPointerException("Null socketOption");
|
||||
}
|
||||
if (value == null) {
|
||||
throw new NullPointerException("Null value");
|
||||
}
|
||||
if (!socketOption.type().equals(value.getClass())) {
|
||||
throw new IllegalArgumentException("Expected " + socketOption.type().getSimpleName()
|
||||
+ " value,but givend " + value.getClass().getSimpleName());
|
||||
}
|
||||
socketOptions.put(socketOption, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getSocketOption(SocketOption<T> socketOption) {
|
||||
return (T) socketOptions.get(socketOption);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind localhost address
|
||||
*
|
||||
* @param inetSocketAddress
|
||||
* @throws IOException
|
||||
*/
|
||||
public void bind(InetSocketAddress inetSocketAddress) throws IOException {
|
||||
if (inetSocketAddress == null) {
|
||||
throw new IllegalArgumentException("Null inetSocketAddress");
|
||||
}
|
||||
setLocalSocketAddress(inetSocketAddress);
|
||||
start();
|
||||
}
|
||||
}
|
@@ -0,0 +1,439 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
import com.google.code.yanf4j.core.CodecFactory;
|
||||
import com.google.code.yanf4j.core.Dispatcher;
|
||||
import com.google.code.yanf4j.core.Handler;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
import com.google.code.yanf4j.core.SessionConfig;
|
||||
import com.google.code.yanf4j.core.WriteMessage;
|
||||
import com.google.code.yanf4j.statistics.Statistics;
|
||||
import com.google.code.yanf4j.util.LinkedTransferQueue;
|
||||
|
||||
/**
|
||||
* Base connection
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractSession implements Session {
|
||||
|
||||
protected IoBuffer readBuffer;
|
||||
protected static final Logger log = LoggerFactory.getLogger(AbstractSession.class);
|
||||
|
||||
protected final ConcurrentHashMap<String, Object> attributes =
|
||||
new ConcurrentHashMap<String, Object>();
|
||||
|
||||
protected Queue<WriteMessage> writeQueue;
|
||||
|
||||
protected long sessionIdleTimeout;
|
||||
|
||||
protected long sessionTimeout;
|
||||
|
||||
public long getSessionIdleTimeout() {
|
||||
return sessionIdleTimeout;
|
||||
}
|
||||
|
||||
public void setSessionIdleTimeout(long sessionIdleTimeout) {
|
||||
this.sessionIdleTimeout = sessionIdleTimeout;
|
||||
}
|
||||
|
||||
public long getSessionTimeout() {
|
||||
return sessionTimeout;
|
||||
}
|
||||
|
||||
public void setSessionTimeout(long sessionTimeout) {
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
}
|
||||
|
||||
public Queue<WriteMessage> getWriteQueue() {
|
||||
return writeQueue;
|
||||
}
|
||||
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public Dispatcher getDispatchMessageDispatcher() {
|
||||
return dispatchMessageDispatcher;
|
||||
}
|
||||
|
||||
public ReentrantLock getWriteLock() {
|
||||
return writeLock;
|
||||
}
|
||||
|
||||
protected CodecFactory.Encoder encoder;
|
||||
protected CodecFactory.Decoder decoder;
|
||||
|
||||
protected volatile boolean closed;
|
||||
|
||||
protected Statistics statistics;
|
||||
|
||||
protected Handler handler;
|
||||
|
||||
protected boolean loopback;
|
||||
|
||||
public AtomicLong lastOperationTimeStamp = new AtomicLong(0);
|
||||
|
||||
protected AtomicLong scheduleWritenBytes = new AtomicLong(0);
|
||||
|
||||
protected final Dispatcher dispatchMessageDispatcher;
|
||||
protected boolean useBlockingWrite = false;
|
||||
protected boolean useBlockingRead = true;
|
||||
protected boolean handleReadWriteConcurrently = true;
|
||||
|
||||
public abstract void decode();
|
||||
|
||||
public void updateTimeStamp() {
|
||||
lastOperationTimeStamp.set(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public long getLastOperationTimeStamp() {
|
||||
return lastOperationTimeStamp.get();
|
||||
}
|
||||
|
||||
public final boolean isHandleReadWriteConcurrently() {
|
||||
return handleReadWriteConcurrently;
|
||||
}
|
||||
|
||||
public final void setHandleReadWriteConcurrently(boolean handleReadWriteConcurrently) {
|
||||
this.handleReadWriteConcurrently = handleReadWriteConcurrently;
|
||||
}
|
||||
|
||||
public long getScheduleWritenBytes() {
|
||||
return scheduleWritenBytes.get();
|
||||
}
|
||||
|
||||
public CodecFactory.Encoder getEncoder() {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
public void setEncoder(CodecFactory.Encoder encoder) {
|
||||
this.encoder = encoder;
|
||||
}
|
||||
|
||||
public CodecFactory.Decoder getDecoder() {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
public IoBuffer getReadBuffer() {
|
||||
return readBuffer;
|
||||
}
|
||||
|
||||
public void setReadBuffer(IoBuffer readBuffer) {
|
||||
this.readBuffer = readBuffer;
|
||||
}
|
||||
|
||||
public void setDecoder(CodecFactory.Decoder decoder) {
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
public final ByteOrder getReadBufferByteOrder() {
|
||||
if (readBuffer == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return readBuffer.order();
|
||||
}
|
||||
|
||||
public final void setReadBufferByteOrder(ByteOrder readBufferByteOrder) {
|
||||
if (readBuffer == null) {
|
||||
throw new NullPointerException("Null ReadBuffer");
|
||||
}
|
||||
readBuffer.order(readBufferByteOrder);
|
||||
}
|
||||
|
||||
// synchronized,prevent reactors invoking this method concurrently.
|
||||
protected synchronized void onIdle() {
|
||||
try {
|
||||
// check twice
|
||||
if (isIdle()) {
|
||||
updateTimeStamp();
|
||||
handler.onSessionIdle(this);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
onException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onConnected() {
|
||||
try {
|
||||
handler.onSessionConnected(this);
|
||||
} catch (Throwable e) {
|
||||
onException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onExpired() {
|
||||
try {
|
||||
if (isExpired()) {
|
||||
handler.onSessionExpired(this);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
onException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract WriteMessage wrapMessage(Object msg, Future<Boolean> writeFuture);
|
||||
|
||||
/**
|
||||
* Pre-Process WriteMessage before writing to channel
|
||||
*
|
||||
* @param writeMessage
|
||||
* @return
|
||||
*/
|
||||
protected WriteMessage preprocessWriteMessage(WriteMessage writeMessage) {
|
||||
return writeMessage;
|
||||
}
|
||||
|
||||
protected void dispatchReceivedMessage(final Object message) {
|
||||
if (dispatchMessageDispatcher == null) {
|
||||
long start = -1;
|
||||
if (statistics != null && statistics.isStatistics()) {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
onMessage(message, this);
|
||||
if (start != -1) {
|
||||
statistics.statisticsProcess(System.currentTimeMillis() - start);
|
||||
}
|
||||
} else {
|
||||
|
||||
dispatchMessageDispatcher.dispatch(new Runnable() {
|
||||
public void run() {
|
||||
long start = -1;
|
||||
if (statistics != null && statistics.isStatistics()) {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
onMessage(message, AbstractSession.this);
|
||||
if (start != -1) {
|
||||
statistics.statisticsProcess(System.currentTimeMillis() - start);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onMessage(final Object message, Session session) {
|
||||
try {
|
||||
handler.onMessageReceived(session, message);
|
||||
} catch (Throwable e) {
|
||||
onException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public final void setClosed(boolean closed) {
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
synchronized (this) {
|
||||
if (isClosed()) {
|
||||
return;
|
||||
}
|
||||
setClosed(true);
|
||||
}
|
||||
try {
|
||||
closeChannel();
|
||||
clearAttributes();
|
||||
log.debug("session closed");
|
||||
} catch (IOException e) {
|
||||
onException(e);
|
||||
log.error("Close session error", e);
|
||||
} finally {
|
||||
onClosed();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void closeChannel() throws IOException;
|
||||
|
||||
public void onException(Throwable e) {
|
||||
handler.onExceptionCaught(this, e);
|
||||
}
|
||||
|
||||
protected void onClosed() {
|
||||
try {
|
||||
handler.onSessionClosed(this);
|
||||
} catch (Throwable e) {
|
||||
onException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAttribute(String key, Object value) {
|
||||
attributes.put(key, value);
|
||||
}
|
||||
|
||||
public Object setAttributeIfAbsent(String key, Object value) {
|
||||
return attributes.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String key) {
|
||||
attributes.remove(key);
|
||||
}
|
||||
|
||||
public Object getAttribute(String key) {
|
||||
return attributes.get(key);
|
||||
}
|
||||
|
||||
public void clearAttributes() {
|
||||
attributes.clear();
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
log.debug("session started");
|
||||
onStarted();
|
||||
start0();
|
||||
}
|
||||
|
||||
protected abstract void start0();
|
||||
|
||||
protected void onStarted() {
|
||||
try {
|
||||
handler.onSessionStarted(this);
|
||||
} catch (Throwable e) {
|
||||
onException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected ReentrantLock writeLock = new ReentrantLock();
|
||||
|
||||
protected AtomicReference<WriteMessage> currentMessage =
|
||||
new LinkedTransferQueue.PaddedAtomicReference<WriteMessage>(null);
|
||||
|
||||
static final class FailFuture implements Future<Boolean> {
|
||||
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
public Boolean get() throws InterruptedException, ExecutionException {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
public Boolean get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void write(Object packet) {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
WriteMessage message = wrapMessage(packet, null);
|
||||
scheduleWritenBytes.addAndGet(message.getWriteBuffer().remaining());
|
||||
writeFromUserCode(message);
|
||||
}
|
||||
|
||||
public abstract void writeFromUserCode(WriteMessage message);
|
||||
|
||||
public final boolean isLoopbackConnection() {
|
||||
return loopback;
|
||||
}
|
||||
|
||||
public boolean isUseBlockingWrite() {
|
||||
return useBlockingWrite;
|
||||
}
|
||||
|
||||
public void setUseBlockingWrite(boolean useBlockingWrite) {
|
||||
this.useBlockingWrite = useBlockingWrite;
|
||||
}
|
||||
|
||||
public boolean isUseBlockingRead() {
|
||||
return useBlockingRead;
|
||||
}
|
||||
|
||||
public void setUseBlockingRead(boolean useBlockingRead) {
|
||||
this.useBlockingRead = useBlockingRead;
|
||||
}
|
||||
|
||||
public void clearWriteQueue() {
|
||||
writeQueue.clear();
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isIdle() {
|
||||
long lastOpTimestamp = getLastOperationTimeStamp();
|
||||
return lastOpTimestamp > 0 && System.currentTimeMillis() - lastOpTimestamp > sessionIdleTimeout;
|
||||
}
|
||||
|
||||
public AbstractSession(SessionConfig sessionConfig) {
|
||||
super();
|
||||
lastOperationTimeStamp.set(System.currentTimeMillis());
|
||||
statistics = sessionConfig.statistics;
|
||||
handler = sessionConfig.handler;
|
||||
writeQueue = sessionConfig.queue;
|
||||
encoder = sessionConfig.codecFactory.getEncoder();
|
||||
decoder = sessionConfig.codecFactory.getDecoder();
|
||||
dispatchMessageDispatcher = sessionConfig.dispatchMessageDispatcher;
|
||||
handleReadWriteConcurrently = sessionConfig.handleReadWriteConcurrently;
|
||||
sessionTimeout = sessionConfig.sessionTimeout;
|
||||
sessionIdleTimeout = sessionConfig.sessionIdelTimeout;
|
||||
}
|
||||
|
||||
public long transferTo(long position, long count, FileChannel target) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public long transferFrom(long position, long count, FileChannel source) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected void onCreated() {
|
||||
try {
|
||||
handler.onSessionCreated(this);
|
||||
} catch (Throwable e) {
|
||||
onException(e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
import com.google.code.yanf4j.core.CodecFactory;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
|
||||
/**
|
||||
* Default codec factory
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class ByteBufferCodecFactory implements CodecFactory {
|
||||
static final IoBuffer EMPTY_BUFFER = IoBuffer.allocate(0);
|
||||
|
||||
private boolean direct;
|
||||
|
||||
public ByteBufferCodecFactory() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public ByteBufferCodecFactory(boolean direct) {
|
||||
super();
|
||||
this.direct = direct;
|
||||
this.encoder = new ByteBufferEncoder();
|
||||
this.decoder = new ByteBufferDecoder();
|
||||
}
|
||||
|
||||
public class ByteBufferDecoder implements Decoder {
|
||||
|
||||
public Object decode(IoBuffer buff, Session session) {
|
||||
if (buff == null) {
|
||||
return null;
|
||||
}
|
||||
if (buff.remaining() == 0) {
|
||||
return EMPTY_BUFFER;
|
||||
}
|
||||
byte[] bytes = new byte[buff.remaining()];
|
||||
buff.get(bytes);
|
||||
IoBuffer result = IoBuffer.allocate(bytes.length, ByteBufferCodecFactory.this.direct);
|
||||
result.put(bytes);
|
||||
result.flip();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Decoder decoder;
|
||||
|
||||
public Decoder getDecoder() {
|
||||
return this.decoder;
|
||||
}
|
||||
|
||||
public class ByteBufferEncoder implements Encoder {
|
||||
|
||||
public IoBuffer encode(Object message, Session session) {
|
||||
final IoBuffer msgBuffer = (IoBuffer) message;
|
||||
if (msgBuffer == null) {
|
||||
return null;
|
||||
}
|
||||
if (msgBuffer.remaining() == 0) {
|
||||
return EMPTY_BUFFER;
|
||||
}
|
||||
byte[] bytes = new byte[msgBuffer.remaining()];
|
||||
msgBuffer.get(bytes);
|
||||
IoBuffer result = IoBuffer.allocate(bytes.length, ByteBufferCodecFactory.this.direct);
|
||||
result.put(bytes);
|
||||
result.flip();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Encoder encoder;
|
||||
|
||||
public Encoder getEncoder() {
|
||||
return this.encoder;
|
||||
}
|
||||
|
||||
}
|
170
src/main/java/com/google/code/yanf4j/core/impl/FutureImpl.java
Normal file
170
src/main/java/com/google/code/yanf4j/core/impl/FutureImpl.java
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* Simple {@link Future} implementation, which uses synchronization {@link Object} to synchronize
|
||||
* during the lifecycle.
|
||||
*
|
||||
* @see Future
|
||||
*
|
||||
* @author Alexey Stashok
|
||||
*/
|
||||
public class FutureImpl<R> implements Future<R> {
|
||||
|
||||
private final Object sync;
|
||||
|
||||
private boolean isDone;
|
||||
|
||||
private boolean isCancelled;
|
||||
private Throwable failure;
|
||||
|
||||
protected R result;
|
||||
|
||||
public FutureImpl() {
|
||||
this(new Object());
|
||||
}
|
||||
|
||||
public FutureImpl(Object sync) {
|
||||
this.sync = sync;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current result value without any blocking.
|
||||
*
|
||||
* @return current result value without any blocking.
|
||||
*/
|
||||
public R getResult() {
|
||||
synchronized (this.sync) {
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the result value and notify about operation completion.
|
||||
*
|
||||
* @param result the result value
|
||||
*/
|
||||
public void setResult(R result) {
|
||||
synchronized (this.sync) {
|
||||
this.result = result;
|
||||
notifyHaveResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
synchronized (this.sync) {
|
||||
this.isCancelled = true;
|
||||
notifyHaveResult();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isCancelled() {
|
||||
synchronized (this.sync) {
|
||||
return this.isCancelled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isDone() {
|
||||
synchronized (this.sync) {
|
||||
return this.isDone;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public R get() throws InterruptedException, ExecutionException {
|
||||
synchronized (this.sync) {
|
||||
for (;;) {
|
||||
if (this.isDone) {
|
||||
if (this.isCancelled) {
|
||||
throw new CancellationException();
|
||||
} else if (this.failure != null) {
|
||||
throw new ExecutionException(this.failure);
|
||||
} else if (this.result != null) {
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
|
||||
this.sync.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public R get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
long startTime = System.currentTimeMillis();
|
||||
long timeoutMillis = TimeUnit.MILLISECONDS.convert(timeout, unit);
|
||||
synchronized (this.sync) {
|
||||
for (;;) {
|
||||
if (this.isDone) {
|
||||
if (this.isCancelled) {
|
||||
throw new CancellationException();
|
||||
} else if (this.failure != null) {
|
||||
throw new ExecutionException(this.failure);
|
||||
} else if (this.result != null) {
|
||||
return this.result;
|
||||
}
|
||||
} else if (System.currentTimeMillis() - startTime > timeoutMillis) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
this.sync.wait(timeoutMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify about the failure, occured during asynchronous operation execution.
|
||||
*
|
||||
* @param failure
|
||||
*/
|
||||
public void failure(Throwable failure) {
|
||||
synchronized (this.sync) {
|
||||
this.failure = failure;
|
||||
notifyHaveResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify blocked listeners threads about operation completion.
|
||||
*/
|
||||
protected void notifyHaveResult() {
|
||||
this.isDone = true;
|
||||
this.sync.notifyAll();
|
||||
}
|
||||
}
|
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
||||
*
|
||||
* Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* The contents of this file are subject to the terms of either the GNU General Public License
|
||||
* Version 2 only ("GPL") or the Common Development and Distribution License("CDDL") (collectively,
|
||||
* the "License"). You may not use this file except in compliance with the License. You can obtain a
|
||||
* copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html or
|
||||
* glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*
|
||||
* When distributing the software, include this License Header Notice in each file and include the
|
||||
* License file at glassfish/bootstrap/legal/LICENSE.txt. Sun designates this particular file as
|
||||
* subject to the "Classpath" exception as provided by Sun in the GPL Version 2 section of the
|
||||
* License file that accompanied this code. If applicable, add the following below the License
|
||||
* Header, with the fields enclosed by brackets [] replaced by your own identifying information:
|
||||
* "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* If you wish your version of this file to be governed by only the CDDL or only the GPL Version 2,
|
||||
* indicate your decision by adding "[Contributor] elects to include this software in this
|
||||
* distribution under the [CDDL or GPL Version 2] license." If you don't indicate a single choice of
|
||||
* license, a recipient has the option to distribute your version of this file under either the
|
||||
* CDDL, the GPL Version 2 or to extend the choice of license to its licensees as provided above.
|
||||
* However, if you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then the
|
||||
* option applies only if the new code is made subject to such option by the copyright holder.
|
||||
*/
|
||||
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Simple {@link Future} implementation, which uses {@link ReentrantLock} to synchronize during the
|
||||
* lifecycle.
|
||||
*
|
||||
* @see Future
|
||||
* @see ReentrantLock
|
||||
*
|
||||
* @author Alexey Stashok
|
||||
*/
|
||||
public class FutureLockImpl<R> implements Future<R> {
|
||||
|
||||
private final ReentrantLock lock;
|
||||
|
||||
private boolean isDone;
|
||||
|
||||
private CountDownLatch latch;
|
||||
|
||||
private boolean isCancelled;
|
||||
private Throwable failure;
|
||||
|
||||
protected R result;
|
||||
|
||||
public FutureLockImpl() {
|
||||
this(new ReentrantLock());
|
||||
}
|
||||
|
||||
public FutureLockImpl(ReentrantLock lock) {
|
||||
this.lock = lock;
|
||||
latch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current result value without any blocking.
|
||||
*
|
||||
* @return current result value without any blocking.
|
||||
*/
|
||||
public R getResult() {
|
||||
try {
|
||||
lock.lock();
|
||||
return result;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the result value and notify about operation completion.
|
||||
*
|
||||
* @param result the result value
|
||||
*/
|
||||
public void setResult(R result) {
|
||||
try {
|
||||
lock.lock();
|
||||
this.result = result;
|
||||
notifyHaveResult();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
try {
|
||||
lock.lock();
|
||||
isCancelled = true;
|
||||
notifyHaveResult();
|
||||
return true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isCancelled() {
|
||||
try {
|
||||
lock.lock();
|
||||
return isCancelled;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isDone() {
|
||||
try {
|
||||
lock.lock();
|
||||
return isDone;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public R get() throws InterruptedException, ExecutionException {
|
||||
latch.await();
|
||||
|
||||
try {
|
||||
lock.lock();
|
||||
if (isCancelled) {
|
||||
throw new CancellationException();
|
||||
} else if (failure != null) {
|
||||
throw new ExecutionException(failure);
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public R get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
boolean isTimeOut = !latch.await(timeout, unit);
|
||||
try {
|
||||
lock.lock();
|
||||
if (!isTimeOut) {
|
||||
if (isCancelled) {
|
||||
throw new CancellationException();
|
||||
} else if (failure != null) {
|
||||
throw new ExecutionException(failure);
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify about the failure, occured during asynchronous operation execution.
|
||||
*
|
||||
* @param failure
|
||||
*/
|
||||
public void failure(Throwable failure) {
|
||||
try {
|
||||
lock.lock();
|
||||
this.failure = failure;
|
||||
notifyHaveResult();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify blocked listeners threads about operation completion.
|
||||
*/
|
||||
protected void notifyHaveResult() {
|
||||
isDone = true;
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.code.yanf4j.core.Handler;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
|
||||
/**
|
||||
* IO Handler adapter
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author boyan
|
||||
*
|
||||
*/
|
||||
public class HandlerAdapter implements Handler {
|
||||
private static final Logger log = LoggerFactory.getLogger(HandlerAdapter.class);
|
||||
|
||||
public void onExceptionCaught(Session session, Throwable throwable) {
|
||||
|
||||
}
|
||||
|
||||
public void onMessageSent(Session session, Object message) {
|
||||
|
||||
}
|
||||
|
||||
public void onSessionConnected(Session session) {
|
||||
|
||||
}
|
||||
|
||||
public void onSessionStarted(Session session) {
|
||||
|
||||
}
|
||||
|
||||
public void onSessionCreated(Session session) {
|
||||
|
||||
}
|
||||
|
||||
public void onSessionClosed(Session session) {
|
||||
|
||||
}
|
||||
|
||||
public void onMessageReceived(Session session, Object message) {
|
||||
|
||||
}
|
||||
|
||||
public void onSessionIdle(Session session) {
|
||||
|
||||
}
|
||||
|
||||
public void onSessionExpired(Session session) {
|
||||
log.warn("Session(" + session.getRemoteSocketAddress() + ") is expired.");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.google.code.yanf4j.core.Dispatcher;
|
||||
import com.google.code.yanf4j.util.WorkerThreadFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
* Pool dispatcher,wrap a threadpool.
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class PoolDispatcher implements Dispatcher {
|
||||
public static final int DEFAULT_POOL_QUEUE_SIZE_FACTOR = 1000;
|
||||
public static final float DEFAULT_MAX_POOL_SIZE_FACTOR = 1.25f;
|
||||
private ThreadPoolExecutor threadPool;
|
||||
|
||||
public PoolDispatcher(int poolSize) {
|
||||
this(poolSize, 60, TimeUnit.SECONDS, new ThreadPoolExecutor.AbortPolicy(), "pool-dispatcher");
|
||||
}
|
||||
|
||||
public PoolDispatcher(int poolSize, long keepAliveTime, TimeUnit unit,
|
||||
RejectedExecutionHandler rejectedExecutionHandler, String prefix) {
|
||||
this(poolSize, DEFAULT_POOL_QUEUE_SIZE_FACTOR, DEFAULT_MAX_POOL_SIZE_FACTOR, keepAliveTime,
|
||||
unit, rejectedExecutionHandler, prefix);
|
||||
}
|
||||
|
||||
public PoolDispatcher(int poolSize, int poolQueueSizeFactor, float maxPoolSizeFactor,
|
||||
long keepAliveTime, TimeUnit unit, RejectedExecutionHandler rejectedExecutionHandler,
|
||||
String prefix) {
|
||||
this.threadPool = new ThreadPoolExecutor(poolSize, (int) (maxPoolSizeFactor * poolSize),
|
||||
keepAliveTime, unit, new ArrayBlockingQueue<Runnable>(poolSize * poolQueueSizeFactor),
|
||||
new WorkerThreadFactory(prefix));
|
||||
this.threadPool.setRejectedExecutionHandler(rejectedExecutionHandler);
|
||||
}
|
||||
|
||||
public final void dispatch(Runnable r) {
|
||||
if (!this.threadPool.isShutdown()) {
|
||||
this.threadPool.execute(r);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
this.threadPool.shutdown();
|
||||
try {
|
||||
this.threadPool.awaitTermination(1000, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import com.google.code.yanf4j.core.SocketOption;
|
||||
|
||||
/**
|
||||
* Standard socket options
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class StandardSocketOption {
|
||||
|
||||
/**
|
||||
* Keep connection alive.
|
||||
*
|
||||
* <p>
|
||||
* The value of this socket option is a {@code Boolean} that represents whether the option is
|
||||
* enabled or disabled. When the {@code SO_KEEPALIVE} option is enabled the operating system may
|
||||
* use a <em>keep-alive</em> mechanism to periodically probe the other end of a connection when
|
||||
* the connection is otherwise idle. The exact semantics of the keep alive mechanism is system
|
||||
* dependent and therefore unspecified.
|
||||
*
|
||||
* <p>
|
||||
* The initial value of this socket option is {@code FALSE}. The socket option may be enabled or
|
||||
* disabled at any time.
|
||||
*
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122 * Requirements for Internet
|
||||
* Hosts -- Communication Layers< /a>
|
||||
* @see Socket#setKeepAlive
|
||||
*/
|
||||
public static final SocketOption<Boolean> SO_KEEPALIVE =
|
||||
new SocketOption<Boolean>("SO_KEEPALIVE", Boolean.class);
|
||||
/**
|
||||
* The size of the socket send buffer.
|
||||
*
|
||||
* <p>
|
||||
* The value of this socket option is an {@code Integer} that is the size of the socket send
|
||||
* buffer in bytes. The socket send buffer is an output buffer used by the networking
|
||||
* implementation. It may need to be increased for high-volume connections. The value of the
|
||||
* socket option is a <em>hint</em> to the implementation to size the buffer and the actual size
|
||||
* may differ. The socket option can be queried to retrieve the actual size.
|
||||
*
|
||||
* <p>
|
||||
* For datagram-oriented sockets, the size of the send buffer may limit the size of the datagrams
|
||||
* that may be sent by the socket. Whether datagrams larger than the buffer size are sent or
|
||||
* discarded is system dependent.
|
||||
*
|
||||
* <p>
|
||||
* The initial/default size of the socket send buffer and the range of allowable values is system
|
||||
* dependent although a negative size is not allowed. An attempt to set the socket send buffer to
|
||||
* larger than its maximum size causes it to be set to its maximum size.
|
||||
*
|
||||
* <p>
|
||||
* An implementation allows this socket option to be set before the socket is bound or connected.
|
||||
* Whether an implementation allows the socket send buffer to be changed after the socket is bound
|
||||
* is system dependent.
|
||||
*
|
||||
* @see Socket#setSendBufferSize
|
||||
*/
|
||||
public static final SocketOption<Integer> SO_SNDBUF =
|
||||
new SocketOption<Integer>("SO_SNDBUF", Integer.class);
|
||||
/**
|
||||
* The size of the socket receive buffer.
|
||||
*
|
||||
* <p>
|
||||
* The value of this socket option is an {@code Integer} that is the size of the socket receive
|
||||
* buffer in bytes. The socket receive buffer is an input buffer used by the networking
|
||||
* implementation. It may need to be increased for high-volume connections or decreased to limit
|
||||
* the possible backlog of incoming data. The value of the socket option is a <em>hint</em> to the
|
||||
* implementation to size the buffer and the actual size may differ.
|
||||
*
|
||||
* <p>
|
||||
* For datagram-oriented sockets, the size of the receive buffer may limit the size of the
|
||||
* datagrams that can be received. Whether datagrams larger than the buffer size can be received
|
||||
* is system dependent. Increasing the socket receive buffer may be important for cases where
|
||||
* datagrams arrive in bursts faster than they can be processed.
|
||||
*
|
||||
* <p>
|
||||
* In the case of stream-oriented sockets and the TCP/IP protocol, the size of the socket receive
|
||||
* buffer may be used when advertising the size of the TCP receive window to the remote peer.
|
||||
*
|
||||
* <p>
|
||||
* The initial/default size of the socket receive buffer and the range of allowable values is
|
||||
* system dependent although a negative size is not allowed. An attempt to set the socket receive
|
||||
* buffer to larger than its maximum size causes it to be set to its maximum size.
|
||||
*
|
||||
* <p>
|
||||
* An implementation allows this socket option to be set before the socket is bound or connected.
|
||||
* Whether an implementation allows the socket receive buffer to be changed after the socket is
|
||||
* bound is system dependent.
|
||||
*
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc1323.txt">RFC 1323: TCP * Extensions for High
|
||||
* Performance< /a>
|
||||
* @see Socket#setReceiveBufferSize
|
||||
* @see ServerSocket#setReceiveBufferSize
|
||||
*/
|
||||
public static final SocketOption<Integer> SO_RCVBUF =
|
||||
new SocketOption<Integer>("SO_RCVBUF", Integer.class);
|
||||
/**
|
||||
* Re-use address.
|
||||
*
|
||||
* <p>
|
||||
* The value of this socket option is a {@code Boolean} that represents whether the option is
|
||||
* enabled or disabled. The exact semantics of this socket option are socket type and system
|
||||
* dependent.
|
||||
*
|
||||
* <p>
|
||||
* In the case of stream-oriented sockets, this socket option will usually determine whether the
|
||||
* socket can be bound to a socket address when a previous connection involving that socket
|
||||
* address is in the <em>TIME_WAIT</em> state. On implementations where the semantics differ, and
|
||||
* the socket option is not required to be enabled in order to bind the socket when a previous
|
||||
* connection is in this state, then the implementation may choose to ignore this option.
|
||||
*
|
||||
* <p>
|
||||
* For datagram-oriented sockets the socket option is used to allow multiple programs bind to the
|
||||
* same address. This option should be enabled when the socket is to be used for Internet Protocol
|
||||
* (IP) multicasting.
|
||||
*
|
||||
* <p>
|
||||
* An implementation allows this socket option to be set before the socket is bound or connected.
|
||||
* Changing the value of this socket option after the socket is bound has no effect. The default
|
||||
* value of this socket option is system dependent.
|
||||
*
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc793.txt">RFC 793: * Transmission Control
|
||||
* Protocol< /a>
|
||||
* @see ServerSocket#setReuseAddress
|
||||
*/
|
||||
public static final SocketOption<Boolean> SO_REUSEADDR =
|
||||
new SocketOption<Boolean>("SO_REUSEADDR", Boolean.class);
|
||||
/**
|
||||
* Linger on close if data is present.
|
||||
*
|
||||
* <p>
|
||||
* The value of this socket option is an {@code Integer} that controls the action taken when
|
||||
* unsent data is queued on the socket and a method to close the socket is invoked. If the value
|
||||
* of the socket option is zero or greater, then it represents a timeout value, in seconds, known
|
||||
* as the <em>linger interval</em>. The linger interval is the timeout for the {@code close}
|
||||
* method to block while the operating system attempts to transmit the unsent data or it decides
|
||||
* that it is unable to transmit the data. If the value of the socket option is less than zero
|
||||
* then the option is disabled. In that case the {@code close} method does not wait until unsent
|
||||
* data is transmitted; if possible the operating system will transmit any unsent data before the
|
||||
* connection is closed.
|
||||
*
|
||||
* <p>
|
||||
* This socket option is intended for use with sockets that are configured in
|
||||
* {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode only. The behavior of
|
||||
* the {@code close} method when this option is enabled on a non-blocking socket is not defined.
|
||||
*
|
||||
* <p>
|
||||
* The initial value of this socket option is a negative value, meaning that the option is
|
||||
* disabled. The option may be enabled, or the linger interval changed, at any time. The maximum
|
||||
* value of the linger interval is system dependent. Setting the linger interval to a value that
|
||||
* is greater than its maximum value causes the linger interval to be set to its maximum value.
|
||||
*
|
||||
* @see Socket#setSoLinger
|
||||
*/
|
||||
public static final SocketOption<Integer> SO_LINGER =
|
||||
new SocketOption<Integer>("SO_LINGER", Integer.class);
|
||||
/**
|
||||
* Disable the Nagle algorithm.
|
||||
*
|
||||
* <p>
|
||||
* The value of this socket option is a {@code Boolean} that represents whether the option is
|
||||
* enabled or disabled. The socket option is specific to stream-oriented sockets using the TCP/IP
|
||||
* protocol. TCP/IP uses an algorithm known as <em>The Nagle Algorithm</em> to coalesce short
|
||||
* segments and improve network efficiency.
|
||||
*
|
||||
* <p>
|
||||
* The default value of this socket option is {@code FALSE}. The socket option should only be
|
||||
* enabled in cases where it is known that the coalescing impacts performance. The socket option
|
||||
* may be enabled at any time. In other words, the Nagle Algorithm can be disabled. Once the
|
||||
* option is enabled, it is system dependent whether it can be subsequently disabled. If it
|
||||
* cannot, then invoking the {@code setOption} method to disable the option has no effect.
|
||||
*
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122: * Requirements for Internet
|
||||
* Hosts -- Communication Layers< /a>
|
||||
* @see Socket#setTcpNoDelay
|
||||
*/
|
||||
public static final SocketOption<Boolean> TCP_NODELAY =
|
||||
new SocketOption<Boolean>("TCP_NODELAY", Boolean.class);
|
||||
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
import com.google.code.yanf4j.core.CodecFactory;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
import com.google.code.yanf4j.util.ByteBufferMatcher;
|
||||
import com.google.code.yanf4j.util.ShiftAndByteBufferMatcher;
|
||||
|
||||
/**
|
||||
* Text line codec factory
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class TextLineCodecFactory implements CodecFactory {
|
||||
|
||||
public static final IoBuffer SPLIT = IoBuffer.wrap("\r\n".getBytes());
|
||||
|
||||
private static final ByteBufferMatcher SPLIT_PATTERN = new ShiftAndByteBufferMatcher(SPLIT);
|
||||
|
||||
public static final String DEFAULT_CHARSET_NAME = "utf-8";
|
||||
|
||||
private Charset charset;
|
||||
|
||||
public TextLineCodecFactory() {
|
||||
this.charset = Charset.forName(DEFAULT_CHARSET_NAME);
|
||||
}
|
||||
|
||||
public TextLineCodecFactory(String charsetName) {
|
||||
this.charset = Charset.forName(charsetName);
|
||||
}
|
||||
|
||||
class StringDecoder implements Decoder {
|
||||
public Object decode(IoBuffer buffer, Session session) {
|
||||
String result = null;
|
||||
int index = SPLIT_PATTERN.matchFirst(buffer);
|
||||
if (index >= 0) {
|
||||
int limit = buffer.limit();
|
||||
buffer.limit(index);
|
||||
CharBuffer charBuffer = TextLineCodecFactory.this.charset.decode(buffer.buf());
|
||||
result = charBuffer.toString();
|
||||
buffer.limit(limit);
|
||||
buffer.position(index + SPLIT.remaining());
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private Decoder decoder = new StringDecoder();
|
||||
|
||||
public Decoder getDecoder() {
|
||||
return this.decoder;
|
||||
|
||||
}
|
||||
|
||||
class StringEncoder implements Encoder {
|
||||
public IoBuffer encode(Object msg, Session session) {
|
||||
if (msg == null) {
|
||||
return null;
|
||||
}
|
||||
String message = (String) msg;
|
||||
ByteBuffer buff = TextLineCodecFactory.this.charset.encode(message);
|
||||
byte[] bs = new byte[buff.remaining() + SPLIT.remaining()];
|
||||
int len = buff.remaining();
|
||||
System.arraycopy(buff.array(), buff.position(), bs, 0, len);
|
||||
bs[len] = 13; // \r
|
||||
bs[len + 1] = 10; // \n
|
||||
IoBuffer resultBuffer = IoBuffer.wrap(bs);
|
||||
|
||||
return resultBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
private Encoder encoder = new StringEncoder();
|
||||
|
||||
public Encoder getEncoder() {
|
||||
return this.encoder;
|
||||
}
|
||||
|
||||
// public static void main(String args[]) {
|
||||
// TextLineCodecFactory codecFactory = new TextLineCodecFactory();
|
||||
// Encoder encoder = codecFactory.getEncoder();
|
||||
// long sum = 0;
|
||||
// for (int i = 0; i < 100000; i++) {
|
||||
// sum += encoder.encode("hello", null).remaining();
|
||||
// }
|
||||
//
|
||||
// long start = System.currentTimeMillis();
|
||||
//
|
||||
// for (int i = 0; i < 10000000; i++) {
|
||||
// sum += encoder.encode("hello", null).remaining();
|
||||
// }
|
||||
// long cost = System.currentTimeMillis() - start;
|
||||
// System.out.println("sum=" + sum + ",cost = " + cost + " ms.");
|
||||
// }
|
||||
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.core.impl;
|
||||
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
import com.google.code.yanf4j.core.WriteMessage;
|
||||
|
||||
/**
|
||||
* Write message implementation with a buffer
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class WriteMessageImpl implements WriteMessage {
|
||||
|
||||
protected Object message;
|
||||
|
||||
protected IoBuffer buffer;
|
||||
|
||||
protected FutureImpl<Boolean> writeFuture;
|
||||
|
||||
protected boolean writing;
|
||||
|
||||
public final void writing() {
|
||||
this.writing = true;
|
||||
}
|
||||
|
||||
public final boolean isWriting() {
|
||||
return this.writing;
|
||||
}
|
||||
|
||||
public WriteMessageImpl(Object message, FutureImpl<Boolean> writeFuture) {
|
||||
this.message = message;
|
||||
this.writeFuture = writeFuture;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.google.code.yanf4j.nio.IWriteMessage#getBuffers()
|
||||
*/
|
||||
public synchronized final IoBuffer getWriteBuffer() {
|
||||
return this.buffer;
|
||||
}
|
||||
|
||||
public synchronized final void setWriteBuffer(IoBuffer buffers) {
|
||||
this.buffer = buffers;
|
||||
|
||||
}
|
||||
|
||||
public final FutureImpl<Boolean> getWriteFuture() {
|
||||
return this.writeFuture;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.google.code.yanf4j.nio.IWriteMessage#getMessage()
|
||||
*/
|
||||
public final Object getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
10
src/main/java/com/google/code/yanf4j/core/package.html
Normal file
10
src/main/java/com/google/code/yanf4j/core/package.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!--?xml version="1.0" encoding="UTF-8"?--><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!-- Copyright (c) 2007 Dustin Sallings <dustin+html@spy.net> -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Networking core package</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Networking core package</h1>
|
||||
</body>
|
||||
</html>
|
52
src/main/java/com/google/code/yanf4j/nio/NioSession.java
Normal file
52
src/main/java/com/google/code/yanf4j/nio/NioSession.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang] Licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
|
||||
* law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
|
||||
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.nio;
|
||||
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.Selector;
|
||||
import com.google.code.yanf4j.core.EventType;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
|
||||
/**
|
||||
* Nio connection
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface NioSession extends Session {
|
||||
/**
|
||||
* When io event occured
|
||||
*
|
||||
* @param event
|
||||
* @param selector
|
||||
*/
|
||||
public void onEvent(EventType event, Selector selector);
|
||||
|
||||
/**
|
||||
* Enable read event
|
||||
*
|
||||
* @param selector
|
||||
*/
|
||||
public void enableRead(Selector selector);
|
||||
|
||||
/**
|
||||
* Enable write event
|
||||
*
|
||||
* @param selector
|
||||
*/
|
||||
public void enableWrite(Selector selector);
|
||||
|
||||
/**
|
||||
* return the channel for this connection
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
public SelectableChannel channel();
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.nio;
|
||||
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.util.Queue;
|
||||
import com.google.code.yanf4j.core.CodecFactory;
|
||||
import com.google.code.yanf4j.core.Dispatcher;
|
||||
import com.google.code.yanf4j.core.Handler;
|
||||
import com.google.code.yanf4j.core.SessionConfig;
|
||||
import com.google.code.yanf4j.core.WriteMessage;
|
||||
import com.google.code.yanf4j.nio.impl.SelectorManager;
|
||||
import com.google.code.yanf4j.statistics.Statistics;
|
||||
|
||||
/**
|
||||
* Nio session configuration
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class NioSessionConfig extends SessionConfig {
|
||||
|
||||
public final SelectableChannel selectableChannel;
|
||||
public final SelectorManager selectorManager;
|
||||
|
||||
public NioSessionConfig(SelectableChannel sc, Handler handler, SelectorManager reactor,
|
||||
CodecFactory codecFactory, Statistics statistics, Queue<WriteMessage> queue,
|
||||
Dispatcher dispatchMessageDispatcher, boolean handleReadWriteConcurrently,
|
||||
long sessionTimeout, long sessionIdleTimeout) {
|
||||
super(handler, codecFactory, statistics, queue, dispatchMessageDispatcher,
|
||||
handleReadWriteConcurrently, sessionTimeout, sessionIdleTimeout);
|
||||
this.selectableChannel = sc;
|
||||
this.selectorManager = reactor;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package com.google.code.yanf4j.nio;
|
||||
|
||||
/**
|
||||
* Copyright [2008-2009] [dennis zhuang] Licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
|
||||
* law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
|
||||
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
|
||||
/**
|
||||
* SelectionKey handler
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface SelectionKeyHandler {
|
||||
public void onAccept(SelectionKey sk) throws IOException;
|
||||
|
||||
public void closeSelectionKey(SelectionKey key);
|
||||
|
||||
public void onWrite(SelectionKey key);
|
||||
|
||||
public void onRead(SelectionKey key);
|
||||
|
||||
public void onConnect(SelectionKey key) throws IOException;
|
||||
|
||||
public void closeChannel(Selector selector) throws IOException;
|
||||
}
|
165
src/main/java/com/google/code/yanf4j/nio/TCPController.java
Normal file
165
src/main/java/com/google/code/yanf4j/nio/TCPController.java
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Copyright [2008-2009] [dennis zhuang] Licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
|
||||
* law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
|
||||
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
|
||||
package com.google.code.yanf4j.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.CodecFactory;
|
||||
import com.google.code.yanf4j.core.EventType;
|
||||
import com.google.code.yanf4j.core.Handler;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
import com.google.code.yanf4j.core.impl.StandardSocketOption;
|
||||
import com.google.code.yanf4j.nio.impl.SocketChannelController;
|
||||
|
||||
/**
|
||||
* Controller for tcp server
|
||||
*
|
||||
* @author dennis
|
||||
*/
|
||||
public class TCPController extends SocketChannelController {
|
||||
|
||||
private ServerSocketChannel serverSocketChannel;
|
||||
|
||||
/**
|
||||
* Accept backlog queue size
|
||||
*/
|
||||
private int backlog = 500; // default 500
|
||||
|
||||
public int getBacklog() {
|
||||
return this.backlog;
|
||||
}
|
||||
|
||||
public void setBacklog(int backlog) {
|
||||
if (isStarted()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (backlog < 0) {
|
||||
throw new IllegalArgumentException("backlog<0");
|
||||
}
|
||||
this.backlog = backlog;
|
||||
}
|
||||
|
||||
public TCPController() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TCPController(Configuration configuration) {
|
||||
super(configuration, null, null);
|
||||
|
||||
}
|
||||
|
||||
public TCPController(Configuration configuration, CodecFactory codecFactory) {
|
||||
super(configuration, null, codecFactory);
|
||||
}
|
||||
|
||||
public TCPController(Configuration configuration, Handler handler, CodecFactory codecFactory) {
|
||||
super(configuration, handler, codecFactory);
|
||||
}
|
||||
|
||||
private int connectionTime, latency, bandwidth;
|
||||
|
||||
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
|
||||
this.connectionTime = connectionTime;
|
||||
this.latency = latency;
|
||||
this.bandwidth = bandwidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws IOException {
|
||||
this.serverSocketChannel = ServerSocketChannel.open();
|
||||
this.serverSocketChannel.socket().setSoTimeout(this.soTimeout);
|
||||
if (this.connectionTime != 0 || this.latency != 0 || this.bandwidth != 0) {
|
||||
this.serverSocketChannel.socket().setPerformancePreferences(this.connectionTime, this.latency,
|
||||
this.bandwidth);
|
||||
}
|
||||
this.serverSocketChannel.configureBlocking(false);
|
||||
|
||||
if (this.socketOptions.get(StandardSocketOption.SO_REUSEADDR) != null) {
|
||||
this.serverSocketChannel.socket().setReuseAddress(StandardSocketOption.SO_REUSEADDR.type()
|
||||
.cast(this.socketOptions.get(StandardSocketOption.SO_REUSEADDR)));
|
||||
}
|
||||
if (this.socketOptions.get(StandardSocketOption.SO_RCVBUF) != null) {
|
||||
this.serverSocketChannel.socket().setReceiveBufferSize(StandardSocketOption.SO_RCVBUF.type()
|
||||
.cast(this.socketOptions.get(StandardSocketOption.SO_RCVBUF)));
|
||||
|
||||
}
|
||||
if (this.localSocketAddress != null) {
|
||||
this.serverSocketChannel.socket().bind(this.localSocketAddress, this.backlog);
|
||||
} else {
|
||||
this.serverSocketChannel.socket().bind(new InetSocketAddress("localhost", 0), this.backlog);
|
||||
}
|
||||
setLocalSocketAddress(
|
||||
(InetSocketAddress) this.serverSocketChannel.socket().getLocalSocketAddress());
|
||||
this.selectorManager.registerChannel(this.serverSocketChannel, SelectionKey.OP_ACCEPT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccept(SelectionKey selectionKey) throws IOException {
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>رգ<D8B1>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
if (!this.serverSocketChannel.isOpen()) {
|
||||
selectionKey.cancel();
|
||||
return;
|
||||
}
|
||||
SocketChannel sc = null;
|
||||
try {
|
||||
sc = this.serverSocketChannel.accept();
|
||||
if (sc != null) {
|
||||
configureSocketChannel(sc);
|
||||
Session session = buildSession(sc);
|
||||
// enable read
|
||||
this.selectorManager.registerSession(session, EventType.ENABLE_READ);
|
||||
session.start();
|
||||
super.onAccept(selectionKey); // for statistics
|
||||
} else {
|
||||
log.debug("Accept fail");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
closeAcceptChannel(selectionKey, sc);
|
||||
log.error("Accept connection error", e);
|
||||
notifyException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sk
|
||||
* @param sc
|
||||
* @throws IOException
|
||||
* @throws SocketException
|
||||
*/
|
||||
private void closeAcceptChannel(SelectionKey sk, SocketChannel sc)
|
||||
throws IOException, SocketException {
|
||||
if (sk != null) {
|
||||
sk.cancel();
|
||||
}
|
||||
if (sc != null) {
|
||||
sc.socket().setSoLinger(true, 0); // await TIME_WAIT status
|
||||
sc.socket().shutdownOutput();
|
||||
sc.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void closeChannel(Selector selector) throws IOException {
|
||||
if (this.serverSocketChannel != null) {
|
||||
this.serverSocketChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void unbind() throws IOException {
|
||||
stop();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,343 @@
|
||||
package com.google.code.yanf4j.nio.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
import com.google.code.yanf4j.core.EventType;
|
||||
import com.google.code.yanf4j.core.WriteMessage;
|
||||
import com.google.code.yanf4j.core.impl.AbstractSession;
|
||||
import com.google.code.yanf4j.nio.NioSession;
|
||||
import com.google.code.yanf4j.nio.NioSessionConfig;
|
||||
import com.google.code.yanf4j.util.SelectorFactory;
|
||||
|
||||
/**
|
||||
* Abstract nio session
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractNioSession extends AbstractSession implements NioSession {
|
||||
|
||||
public SelectableChannel channel() {
|
||||
return selectableChannel;
|
||||
}
|
||||
|
||||
protected SelectorManager selectorManager;
|
||||
protected SelectableChannel selectableChannel;
|
||||
|
||||
public AbstractNioSession(NioSessionConfig sessionConfig) {
|
||||
super(sessionConfig);
|
||||
selectorManager = sessionConfig.selectorManager;
|
||||
selectableChannel = sessionConfig.selectableChannel;
|
||||
}
|
||||
|
||||
public final void enableRead(Selector selector) {
|
||||
SelectionKey key = selectableChannel.keyFor(selector);
|
||||
if (key != null && key.isValid()) {
|
||||
interestRead(key);
|
||||
} else {
|
||||
try {
|
||||
selectableChannel.register(selector, SelectionKey.OP_READ, this);
|
||||
} catch (ClosedChannelException e) {
|
||||
// ignore
|
||||
} catch (CancelledKeyException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void interestRead(SelectionKey key) {
|
||||
if (key.attachment() == null) {
|
||||
key.attach(this);
|
||||
}
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void start0() {
|
||||
registerSession();
|
||||
}
|
||||
|
||||
public InetAddress getLocalAddress() {
|
||||
return ((SocketChannel) selectableChannel).socket().getLocalAddress();
|
||||
}
|
||||
|
||||
protected abstract Object writeToChannel(WriteMessage msg)
|
||||
throws ClosedChannelException, IOException;
|
||||
|
||||
protected void onWrite(SelectionKey key) {
|
||||
boolean isLockedByMe = false;
|
||||
if (currentMessage.get() == null) {
|
||||
// get next message
|
||||
WriteMessage nextMessage = writeQueue.peek();
|
||||
if (nextMessage != null && writeLock.tryLock()) {
|
||||
if (!writeQueue.isEmpty() && currentMessage.compareAndSet(null, nextMessage)) {
|
||||
writeQueue.remove();
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (!writeLock.tryLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
isLockedByMe = true;
|
||||
WriteMessage currentMessage = null;
|
||||
|
||||
final long maxWritten = readBuffer.capacity() + readBuffer.capacity() >>> 1;
|
||||
try {
|
||||
long written = 0;
|
||||
while (this.currentMessage.get() != null) {
|
||||
currentMessage = this.currentMessage.get();
|
||||
currentMessage = preprocessWriteMessage(currentMessage);
|
||||
this.currentMessage.set(currentMessage);
|
||||
long before = this.currentMessage.get().getWriteBuffer().remaining();
|
||||
Object writeResult = null;
|
||||
|
||||
if (written < maxWritten) {
|
||||
writeResult = writeToChannel(currentMessage);
|
||||
written += this.currentMessage.get().getWriteBuffer().remaining() - before;
|
||||
} else {
|
||||
// wait for next time to write
|
||||
}
|
||||
// write complete
|
||||
if (writeResult != null) {
|
||||
this.currentMessage.set(writeQueue.poll());
|
||||
handler.onMessageSent(this, currentMessage.getMessage());
|
||||
// try to get next message
|
||||
if (this.currentMessage.get() == null) {
|
||||
if (isLockedByMe) {
|
||||
isLockedByMe = false;
|
||||
writeLock.unlock();
|
||||
}
|
||||
// get next message
|
||||
WriteMessage nextMessage = writeQueue.peek();
|
||||
if (nextMessage != null && writeLock.tryLock()) {
|
||||
isLockedByMe = true;
|
||||
if (!writeQueue.isEmpty() && this.currentMessage.compareAndSet(null, nextMessage)) {
|
||||
writeQueue.remove();
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// does't write complete
|
||||
if (isLockedByMe) {
|
||||
isLockedByMe = false;
|
||||
writeLock.unlock();
|
||||
}
|
||||
// register OP_WRITE event
|
||||
selectorManager.registerSession(this, EventType.ENABLE_WRITE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
handler.onExceptionCaught(this, e);
|
||||
if (currentMessage != null && currentMessage.getWriteFuture() != null) {
|
||||
currentMessage.getWriteFuture().failure(e);
|
||||
}
|
||||
if (isLockedByMe) {
|
||||
isLockedByMe = false;
|
||||
writeLock.unlock();
|
||||
}
|
||||
close();
|
||||
} finally {
|
||||
if (isLockedByMe) {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void enableWrite(Selector selector) {
|
||||
SelectionKey key = selectableChannel.keyFor(selector);
|
||||
if (key != null && key.isValid()) {
|
||||
interestWrite(key);
|
||||
} else {
|
||||
try {
|
||||
selectableChannel.register(selector, SelectionKey.OP_WRITE, this);
|
||||
} catch (ClosedChannelException e) {
|
||||
// ignore
|
||||
} catch (CancelledKeyException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void interestWrite(SelectionKey key) {
|
||||
if (key.attachment() == null) {
|
||||
key.attach(this);
|
||||
}
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
}
|
||||
|
||||
protected void onRead(SelectionKey key) {
|
||||
readFromBuffer();
|
||||
}
|
||||
|
||||
protected abstract void readFromBuffer();
|
||||
|
||||
@Override
|
||||
protected void closeChannel() throws IOException {
|
||||
flush0();
|
||||
unregisterSession();
|
||||
unregisterChannel();
|
||||
}
|
||||
|
||||
protected final void unregisterChannel() throws IOException {
|
||||
writeLock.lock();
|
||||
try {
|
||||
if (getAttribute(SelectorManager.REACTOR_ATTRIBUTE) != null) {
|
||||
((Reactor) getAttribute(SelectorManager.REACTOR_ATTRIBUTE))
|
||||
.unregisterChannel(selectableChannel);
|
||||
}
|
||||
if (selectableChannel.isOpen()) {
|
||||
selectableChannel.close();
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void registerSession() {
|
||||
selectorManager.registerSession(this, EventType.REGISTER);
|
||||
}
|
||||
|
||||
protected void unregisterSession() {
|
||||
selectorManager.registerSession(this, EventType.UNREGISTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFromUserCode(WriteMessage message) {
|
||||
if (schduleWriteMessage(message)) {
|
||||
return;
|
||||
}
|
||||
onWrite(null);
|
||||
}
|
||||
|
||||
protected boolean schduleWriteMessage(WriteMessage writeMessage) {
|
||||
boolean offered = writeQueue.offer(writeMessage);
|
||||
assert offered;
|
||||
final Reactor reactor = selectorManager.getReactorFromSession(this);
|
||||
if (Thread.currentThread() != reactor) {
|
||||
selectorManager.registerSession(this, EventType.ENABLE_WRITE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
if (isClosed()) {
|
||||
return;
|
||||
}
|
||||
flush0();
|
||||
}
|
||||
|
||||
protected final void flush0() {
|
||||
SelectionKey tmpKey = null;
|
||||
Selector writeSelector = null;
|
||||
int attempts = 0;
|
||||
try {
|
||||
while (true) {
|
||||
if (writeSelector == null) {
|
||||
writeSelector = SelectorFactory.getSelector();
|
||||
if (writeSelector == null) {
|
||||
return;
|
||||
}
|
||||
tmpKey = selectableChannel.register(writeSelector, SelectionKey.OP_WRITE);
|
||||
}
|
||||
if (writeSelector.select(1000) == 0) {
|
||||
attempts++;
|
||||
if (attempts > 2) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
onWrite(selectableChannel.keyFor(writeSelector));
|
||||
} catch (ClosedChannelException cce) {
|
||||
onException(cce);
|
||||
log.error("Flush error", cce);
|
||||
close();
|
||||
} catch (IOException ioe) {
|
||||
onException(ioe);
|
||||
log.error("Flush error", ioe);
|
||||
close();
|
||||
} finally {
|
||||
if (tmpKey != null) {
|
||||
// Cancel the key.
|
||||
tmpKey.cancel();
|
||||
tmpKey = null;
|
||||
}
|
||||
if (writeSelector != null) {
|
||||
try {
|
||||
writeSelector.selectNow();
|
||||
} catch (IOException e) {
|
||||
log.error("Temp selector selectNow error", e);
|
||||
}
|
||||
// return selector
|
||||
SelectorFactory.returnSelector(writeSelector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final long doRealWrite(SelectableChannel channel, IoBuffer buffer) throws IOException {
|
||||
if (log.isDebugEnabled()) {
|
||||
StringBuffer bufMsg = new StringBuffer("send buffers:\n[\n");
|
||||
final ByteBuffer buff = buffer.buf();
|
||||
bufMsg.append(" buffer:position=").append(buff.position()).append(",limit=")
|
||||
.append(buff.limit()).append(",capacity=").append(buff.capacity()).append("\n");
|
||||
|
||||
bufMsg.append("]");
|
||||
log.debug(bufMsg.toString());
|
||||
}
|
||||
return ((WritableByteChannel) channel).write(buffer.buf());
|
||||
}
|
||||
|
||||
/**
|
||||
* <20>ɷ<EFBFBD>IO<49>¼<EFBFBD>
|
||||
*/
|
||||
public final void onEvent(EventType event, Selector selector) {
|
||||
if (isClosed()) {
|
||||
return;
|
||||
}
|
||||
SelectionKey key = selectableChannel.keyFor(selector);
|
||||
|
||||
switch (event) {
|
||||
case EXPIRED:
|
||||
onExpired();
|
||||
break;
|
||||
case WRITEABLE:
|
||||
onWrite(key);
|
||||
break;
|
||||
case READABLE:
|
||||
onRead(key);
|
||||
break;
|
||||
case ENABLE_WRITE:
|
||||
enableWrite(selector);
|
||||
break;
|
||||
case ENABLE_READ:
|
||||
enableRead(selector);
|
||||
break;
|
||||
case IDLE:
|
||||
onIdle();
|
||||
break;
|
||||
case CONNECTED:
|
||||
onConnected();
|
||||
break;
|
||||
default:
|
||||
log.error("Unknown event:" + event.name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
226
src/main/java/com/google/code/yanf4j/nio/impl/NioController.java
Normal file
226
src/main/java/com/google/code/yanf4j/nio/impl/NioController.java
Normal file
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang] Licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
|
||||
* law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
|
||||
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.nio.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.util.Queue;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.CodecFactory;
|
||||
import com.google.code.yanf4j.core.Handler;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
import com.google.code.yanf4j.core.WriteMessage;
|
||||
import com.google.code.yanf4j.core.impl.AbstractController;
|
||||
import com.google.code.yanf4j.nio.NioSessionConfig;
|
||||
import com.google.code.yanf4j.nio.SelectionKeyHandler;
|
||||
import com.google.code.yanf4j.util.SystemUtils;
|
||||
|
||||
/**
|
||||
* Base nio controller
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public abstract class NioController extends AbstractController implements SelectionKeyHandler {
|
||||
|
||||
protected SelectorManager selectorManager;
|
||||
|
||||
/**
|
||||
* Reactor count
|
||||
*/
|
||||
protected int selectorPoolSize = SystemUtils.getSystemThreadCount();
|
||||
|
||||
/**
|
||||
* @see setSelectorPoolSize
|
||||
* @return
|
||||
*/
|
||||
public int getSelectorPoolSize() {
|
||||
return this.selectorPoolSize;
|
||||
}
|
||||
|
||||
public void setSelectorPoolSize(int selectorPoolSize) {
|
||||
if (isStarted()) {
|
||||
throw new IllegalStateException("Controller has been started");
|
||||
}
|
||||
this.selectorPoolSize = selectorPoolSize;
|
||||
}
|
||||
|
||||
public NioController() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NioController(Configuration configuration, CodecFactory codecFactory) {
|
||||
super(configuration, codecFactory);
|
||||
}
|
||||
|
||||
public NioController(Configuration configuration, Handler handler, CodecFactory codecFactory) {
|
||||
super(configuration, handler, codecFactory);
|
||||
}
|
||||
|
||||
public NioController(Configuration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write task
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
private final class WriteTask implements Runnable {
|
||||
private final SelectionKey key;
|
||||
|
||||
private WriteTask(SelectionKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public final void run() {
|
||||
dispatchWriteEvent(this.key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read task
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
private final class ReadTask implements Runnable {
|
||||
private final SelectionKey key;
|
||||
|
||||
private ReadTask(SelectionKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public final void run() {
|
||||
dispatchReadEvent(this.key);
|
||||
}
|
||||
}
|
||||
|
||||
public final SelectorManager getSelectorManager() {
|
||||
return this.selectorManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void start0() throws IOException {
|
||||
try {
|
||||
initialSelectorManager();
|
||||
doStart();
|
||||
} catch (IOException e) {
|
||||
log.error("Start server error", e);
|
||||
notifyException(e);
|
||||
stop();
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Start selector manager
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void initialSelectorManager() throws IOException {
|
||||
if (this.selectorManager == null) {
|
||||
this.selectorManager = new SelectorManager(this.selectorPoolSize, this, this.configuration);
|
||||
this.selectorManager.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner startup
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract void doStart() throws IOException;
|
||||
|
||||
/**
|
||||
* Read event occured
|
||||
*/
|
||||
public void onRead(SelectionKey key) {
|
||||
if (this.readEventDispatcher == null) {
|
||||
dispatchReadEvent(key);
|
||||
} else {
|
||||
this.readEventDispatcher.dispatch(new ReadTask(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writable event occured
|
||||
*/
|
||||
public void onWrite(final SelectionKey key) {
|
||||
if (this.writeEventDispatcher == null) {
|
||||
dispatchWriteEvent(key);
|
||||
} else {
|
||||
this.writeEventDispatcher.dispatch(new WriteTask(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel selection key
|
||||
*/
|
||||
public void closeSelectionKey(SelectionKey key) {
|
||||
if (key.attachment() instanceof Session) {
|
||||
Session session = (Session) key.attachment();
|
||||
if (session != null) {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch read event
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
protected abstract void dispatchReadEvent(final SelectionKey key);
|
||||
|
||||
/**
|
||||
* Dispatch write event
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
protected abstract void dispatchWriteEvent(final SelectionKey key);
|
||||
|
||||
@Override
|
||||
protected void stop0() throws IOException {
|
||||
if (this.selectorManager == null || !this.selectorManager.isStarted()) {
|
||||
return;
|
||||
}
|
||||
this.selectorManager.stop();
|
||||
}
|
||||
|
||||
public synchronized void bind(int port) throws IOException {
|
||||
if (isStarted()) {
|
||||
throw new IllegalStateException("Server has been bind to " + getLocalSocketAddress());
|
||||
}
|
||||
bind(new InetSocketAddress(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build nio session config
|
||||
*
|
||||
* @param sc
|
||||
* @param queue
|
||||
* @return
|
||||
*/
|
||||
protected final NioSessionConfig buildSessionConfig(SelectableChannel sc,
|
||||
Queue<WriteMessage> queue) {
|
||||
final NioSessionConfig sessionConfig =
|
||||
new NioSessionConfig(sc, getHandler(), this.selectorManager, getCodecFactory(),
|
||||
getStatistics(), queue, this.dispatchMessageDispatcher, isHandleReadWriteConcurrently(),
|
||||
this.sessionTimeout, this.configuration.getSessionIdleTimeout());
|
||||
return sessionConfig;
|
||||
}
|
||||
|
||||
}
|
327
src/main/java/com/google/code/yanf4j/nio/impl/NioTCPSession.java
Normal file
327
src/main/java/com/google/code/yanf4j/nio/impl/NioTCPSession.java
Normal file
@@ -0,0 +1,327 @@
|
||||
package com.google.code.yanf4j.nio.impl;
|
||||
|
||||
/**
|
||||
* Copyright [2008-2009] [dennis zhuang] Licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
|
||||
* law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
|
||||
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.concurrent.Future;
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.EventType;
|
||||
import com.google.code.yanf4j.core.WriteMessage;
|
||||
import com.google.code.yanf4j.core.impl.FutureImpl;
|
||||
import com.google.code.yanf4j.core.impl.WriteMessageImpl;
|
||||
import com.google.code.yanf4j.nio.NioSessionConfig;
|
||||
import com.google.code.yanf4j.util.ByteBufferUtils;
|
||||
import com.google.code.yanf4j.util.SelectorFactory;
|
||||
|
||||
/**
|
||||
* Nio tcp connection
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class NioTCPSession extends AbstractNioSession {
|
||||
private InetSocketAddress remoteAddress;
|
||||
|
||||
@Override
|
||||
public final boolean isExpired() {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("sessionTimeout=" + this.sessionTimeout + ",this.timestamp="
|
||||
+ this.lastOperationTimeStamp.get() + ",current=" + System.currentTimeMillis());
|
||||
}
|
||||
return this.sessionTimeout <= 0 ? false
|
||||
: System.currentTimeMillis() - this.lastOperationTimeStamp.get() >= this.sessionTimeout;
|
||||
}
|
||||
|
||||
public NioTCPSession(NioSessionConfig sessionConfig, int readRecvBufferSize) {
|
||||
super(sessionConfig);
|
||||
if (this.selectableChannel != null && this.getRemoteSocketAddress() != null) {
|
||||
this.loopback = this.getRemoteSocketAddress().getAddress().isLoopbackAddress();
|
||||
}
|
||||
this.setReadBuffer(IoBuffer.allocate(readRecvBufferSize));
|
||||
this.onCreated();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object writeToChannel(WriteMessage message) throws IOException {
|
||||
if (message.getWriteFuture() != null && !message.isWriting()
|
||||
&& message.getWriteFuture().isCancelled()) {
|
||||
return message.getMessage();
|
||||
}
|
||||
if (message.getWriteBuffer() == null) {
|
||||
if (message.getWriteFuture() != null) {
|
||||
message.getWriteFuture().setResult(Boolean.TRUE);
|
||||
}
|
||||
return message.getMessage();
|
||||
}
|
||||
IoBuffer writeBuffer = message.getWriteBuffer();
|
||||
// begin writing
|
||||
message.writing();
|
||||
if (this.useBlockingWrite) {
|
||||
return this.blockingWrite(this.selectableChannel, message, writeBuffer);
|
||||
} else {
|
||||
while (true) {
|
||||
long n = this.doRealWrite(this.selectableChannel, writeBuffer);
|
||||
if (n > 0) {
|
||||
this.statistics.statisticsWrite(n);
|
||||
this.scheduleWritenBytes.addAndGet(0 - n);
|
||||
}
|
||||
if (writeBuffer == null || !writeBuffer.hasRemaining()) {
|
||||
if (message.getWriteFuture() != null) {
|
||||
message.getWriteFuture().setResult(Boolean.TRUE);
|
||||
}
|
||||
return message.getMessage();
|
||||
} else if (n == 0) {
|
||||
// have more data, but the buffer is full,
|
||||
// wait next time to write
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public InetSocketAddress getRemoteSocketAddress() {
|
||||
if (this.remoteAddress == null) {
|
||||
this.remoteAddress = (InetSocketAddress) ((SocketChannel) this.selectableChannel).socket()
|
||||
.getRemoteSocketAddress();
|
||||
}
|
||||
return this.remoteAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking write using temp selector
|
||||
*
|
||||
* @param channel
|
||||
* @param message
|
||||
* @param writeBuffer
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws ClosedChannelException
|
||||
*/
|
||||
protected final Object blockingWrite(SelectableChannel channel, WriteMessage message,
|
||||
IoBuffer writeBuffer) throws IOException, ClosedChannelException {
|
||||
SelectionKey tmpKey = null;
|
||||
Selector writeSelector = null;
|
||||
int attempts = 0;
|
||||
int bytesProduced = 0;
|
||||
try {
|
||||
while (writeBuffer.hasRemaining()) {
|
||||
long len = this.doRealWrite(channel, writeBuffer);
|
||||
if (len > 0) {
|
||||
attempts = 0;
|
||||
bytesProduced += len;
|
||||
this.statistics.statisticsWrite(len);
|
||||
} else {
|
||||
attempts++;
|
||||
if (writeSelector == null) {
|
||||
writeSelector = SelectorFactory.getSelector();
|
||||
if (writeSelector == null) {
|
||||
// Continue using the main one.
|
||||
continue;
|
||||
}
|
||||
tmpKey = channel.register(writeSelector, SelectionKey.OP_WRITE);
|
||||
}
|
||||
if (writeSelector.select(1000) == 0) {
|
||||
if (attempts > 2) {
|
||||
throw new IOException("Client disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!writeBuffer.hasRemaining() && message.getWriteFuture() != null) {
|
||||
message.getWriteFuture().setResult(Boolean.TRUE);
|
||||
}
|
||||
} finally {
|
||||
if (tmpKey != null) {
|
||||
tmpKey.cancel();
|
||||
tmpKey = null;
|
||||
}
|
||||
if (writeSelector != null) {
|
||||
// Cancel the key.
|
||||
writeSelector.selectNow();
|
||||
SelectorFactory.returnSelector(writeSelector);
|
||||
}
|
||||
}
|
||||
this.scheduleWritenBytes.addAndGet(0 - bytesProduced);
|
||||
return message.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WriteMessage wrapMessage(Object msg, Future<Boolean> writeFuture) {
|
||||
WriteMessage message = new WriteMessageImpl(msg, (FutureImpl<Boolean>) writeFuture);
|
||||
if (message.getWriteBuffer() == null) {
|
||||
message.setWriteBuffer(this.encoder.encode(message.getMessage(), this));
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readFromBuffer() {
|
||||
if (!this.readBuffer.hasRemaining()) {
|
||||
if (this.readBuffer.capacity() < Configuration.MAX_READ_BUFFER_SIZE) {
|
||||
this.readBuffer =
|
||||
IoBuffer.wrap(ByteBufferUtils.increaseBufferCapatity(this.readBuffer.buf()));
|
||||
} else {
|
||||
// buffer's capacity is greater than maxium
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
int n = -1;
|
||||
int readCount = 0;
|
||||
try {
|
||||
while ((n = ((ReadableByteChannel) this.selectableChannel).read(this.readBuffer.buf())) > 0) {
|
||||
readCount += n;
|
||||
}
|
||||
if (readCount > 0) {
|
||||
decodeAndDispatch();
|
||||
} else if (readCount == 0
|
||||
&& !((SocketChannel) this.selectableChannel).socket().isInputShutdown()
|
||||
&& this.useBlockingRead) {
|
||||
n = this.blockingRead();
|
||||
if (n > 0) {
|
||||
readCount += n;
|
||||
}
|
||||
}
|
||||
if (n < 0) { // Connection closed
|
||||
this.close();
|
||||
} else {
|
||||
this.selectorManager.registerSession(this, EventType.ENABLE_READ);
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("read " + readCount + " bytes from channel");
|
||||
}
|
||||
} catch (ClosedChannelException e) {
|
||||
// ignore exception
|
||||
this.close();
|
||||
} catch (Throwable e) {
|
||||
this.onException(e);
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeAndDispatch() {
|
||||
updateTimeStamp();
|
||||
this.readBuffer.flip();
|
||||
this.decode();
|
||||
this.readBuffer.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking read using temp selector
|
||||
*
|
||||
* @return
|
||||
* @throws ClosedChannelException
|
||||
* @throws IOException
|
||||
*/
|
||||
protected final int blockingRead() throws ClosedChannelException, IOException {
|
||||
int n = 0;
|
||||
int readCount = 0;
|
||||
Selector readSelector = SelectorFactory.getSelector();
|
||||
SelectionKey tmpKey = null;
|
||||
try {
|
||||
if (this.selectableChannel.isOpen()) {
|
||||
tmpKey = this.selectableChannel.register(readSelector, 0);
|
||||
tmpKey.interestOps(tmpKey.interestOps() | SelectionKey.OP_READ);
|
||||
int code = readSelector.select(500);
|
||||
tmpKey.interestOps(tmpKey.interestOps() & ~SelectionKey.OP_READ);
|
||||
if (code > 0) {
|
||||
do {
|
||||
n = ((ReadableByteChannel) this.selectableChannel).read(this.readBuffer.buf());
|
||||
readCount += n;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("use temp selector read " + n + " bytes");
|
||||
}
|
||||
} while (n > 0 && this.readBuffer.hasRemaining());
|
||||
if (readCount > 0) {
|
||||
decodeAndDispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (tmpKey != null) {
|
||||
tmpKey.cancel();
|
||||
tmpKey = null;
|
||||
}
|
||||
if (readSelector != null) {
|
||||
// Cancel the key.
|
||||
readSelector.selectNow();
|
||||
SelectorFactory.returnSelector(readSelector);
|
||||
}
|
||||
}
|
||||
return readCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode buffer
|
||||
*/
|
||||
@Override
|
||||
public void decode() {
|
||||
Object message;
|
||||
int size = this.readBuffer.remaining();
|
||||
while (this.readBuffer.hasRemaining()) {
|
||||
try {
|
||||
message = this.decoder.decode(this.readBuffer, this);
|
||||
if (message == null) {
|
||||
break;
|
||||
} else {
|
||||
if (this.statistics.isStatistics()) {
|
||||
this.statistics.statisticsRead(size - this.readBuffer.remaining());
|
||||
size = this.readBuffer.remaining();
|
||||
}
|
||||
}
|
||||
this.dispatchReceivedMessage(message);
|
||||
} catch (Exception e) {
|
||||
this.onException(e);
|
||||
log.error("Decode error", e);
|
||||
super.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Socket socket() {
|
||||
return ((SocketChannel) this.selectableChannel).socket();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void closeChannel() throws IOException {
|
||||
this.flush0();
|
||||
// try to close output first
|
||||
Socket socket = ((SocketChannel) this.selectableChannel).socket();
|
||||
try {
|
||||
if (!socket.isClosed() && !socket.isOutputShutdown()) {
|
||||
socket.shutdownOutput();
|
||||
}
|
||||
if (!socket.isClosed() && !socket.isInputShutdown()) {
|
||||
socket.shutdownInput();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
socket.close();
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
this.unregisterSession();
|
||||
this.unregisterChannel();
|
||||
}
|
||||
|
||||
}
|
544
src/main/java/com/google/code/yanf4j/nio/impl/Reactor.java
Normal file
544
src/main/java/com/google/code/yanf4j/nio/impl/Reactor.java
Normal file
@@ -0,0 +1,544 @@
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
package com.google.code.yanf4j.nio.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.ClosedSelectorException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.EventType;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
import com.google.code.yanf4j.nio.NioSession;
|
||||
import com.google.code.yanf4j.util.SystemUtils;
|
||||
|
||||
/**
|
||||
* Reactor pattern
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public final class Reactor extends Thread {
|
||||
/**
|
||||
* JVM bug threshold
|
||||
*/
|
||||
public static final int JVMBUG_THRESHHOLD =
|
||||
Integer.getInteger("com.googlecode.yanf4j.nio.JVMBUG_THRESHHOLD", 128);
|
||||
public static final int JVMBUG_THRESHHOLD2 = JVMBUG_THRESHHOLD * 2;
|
||||
public static final int JVMBUG_THRESHHOLD1 = (JVMBUG_THRESHHOLD2 + JVMBUG_THRESHHOLD) / 2;
|
||||
public static final int DEFAULT_WAIT = 1000;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger("remoting");
|
||||
|
||||
private boolean jvmBug0;
|
||||
private boolean jvmBug1;
|
||||
|
||||
private final int reactorIndex;
|
||||
|
||||
private final SelectorManager selectorManager;
|
||||
|
||||
private final AtomicInteger jvmBug = new AtomicInteger(0);
|
||||
|
||||
private long lastJVMBug;
|
||||
|
||||
private Selector selector;
|
||||
|
||||
private final NioController controller;
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
static public class PaddingAtomicBoolean extends AtomicBoolean {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 5227571972657902891L;
|
||||
public int p1;
|
||||
public long p2, p3, p4, p5, p6, p7, p8;
|
||||
|
||||
PaddingAtomicBoolean(boolean v) {
|
||||
super(v);
|
||||
}
|
||||
}
|
||||
|
||||
private final AtomicBoolean wakenUp = new PaddingAtomicBoolean(false);
|
||||
|
||||
public static class RegisterEvent {
|
||||
SelectableChannel channel;
|
||||
int ops;
|
||||
EventType eventType;
|
||||
Object attachment;
|
||||
Session session;
|
||||
|
||||
public RegisterEvent(SelectableChannel channel, int ops, Object attachment) {
|
||||
super();
|
||||
this.channel = channel;
|
||||
this.ops = ops;
|
||||
this.attachment = attachment;
|
||||
}
|
||||
|
||||
public RegisterEvent(Session session, EventType eventType) {
|
||||
super();
|
||||
this.session = session;
|
||||
this.eventType = eventType;
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<RegisterEvent> register;
|
||||
|
||||
private final Lock gate = new ReentrantLock();
|
||||
|
||||
private int selectTries = 0;
|
||||
|
||||
private long nextTimeout = 0;
|
||||
|
||||
private long lastCheckTimestamp = 0L;
|
||||
|
||||
Reactor(SelectorManager selectorManager, Configuration configuration, int index)
|
||||
throws IOException {
|
||||
super();
|
||||
reactorIndex = index;
|
||||
this.register = (Queue<Reactor.RegisterEvent>) SystemUtils.createTransferQueue();
|
||||
this.selectorManager = selectorManager;
|
||||
controller = selectorManager.getController();
|
||||
selector = SystemUtils.openSelector();
|
||||
this.configuration = configuration;
|
||||
setName("Xmemcached-Reactor-" + index);
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
public final Selector getSelector() {
|
||||
return selector;
|
||||
}
|
||||
|
||||
public int getReactorIndex() {
|
||||
return reactorIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
selectorManager.notifyReady();
|
||||
while (selectorManager.isStarted() && selector.isOpen()) {
|
||||
try {
|
||||
beforeSelect();
|
||||
wakenUp.set(false);
|
||||
long before = -1;
|
||||
// Wether to look jvm bug
|
||||
if (SystemUtils.isLinuxPlatform() && !SystemUtils.isAfterJava6u4Version()) {
|
||||
before = System.currentTimeMillis();
|
||||
}
|
||||
long wait = DEFAULT_WAIT;
|
||||
if (nextTimeout > 0) {
|
||||
wait = nextTimeout;
|
||||
}
|
||||
int selected = selector.select(wait);
|
||||
if (selected == 0) {
|
||||
if (before != -1) {
|
||||
lookJVMBug(before, selected, wait);
|
||||
}
|
||||
selectTries++;
|
||||
// check timeout and idle
|
||||
nextTimeout = checkSessionTimeout();
|
||||
continue;
|
||||
} else {
|
||||
selectTries = 0;
|
||||
}
|
||||
|
||||
} catch (ClosedSelectorException e) {
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
log.error("Reactor select error", e);
|
||||
if (selector.isOpen()) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Set<SelectionKey> selectedKeys = selector.selectedKeys();
|
||||
gate.lock();
|
||||
try {
|
||||
postSelect(selectedKeys, selector.keys());
|
||||
dispatchEvent(selectedKeys);
|
||||
} finally {
|
||||
gate.unlock();
|
||||
}
|
||||
}
|
||||
if (selector != null) {
|
||||
if (selector.isOpen()) {
|
||||
try {
|
||||
controller.closeChannel(selector);
|
||||
selector.close();
|
||||
} catch (IOException e) {
|
||||
controller.notifyException(e);
|
||||
log.error("stop reactor error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Look jvm bug
|
||||
*
|
||||
* @param before
|
||||
* @param selected
|
||||
* @param wait
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean lookJVMBug(long before, int selected, long wait) throws IOException {
|
||||
boolean seeing = false;
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (JVMBUG_THRESHHOLD > 0 && selected == 0 && wait > JVMBUG_THRESHHOLD
|
||||
&& now - before < wait / 4 && !wakenUp.get() /* waken up */
|
||||
&& !Thread.currentThread().isInterrupted()/* Interrupted */) {
|
||||
jvmBug.incrementAndGet();
|
||||
if (jvmBug.get() >= JVMBUG_THRESHHOLD2) {
|
||||
gate.lock();
|
||||
try {
|
||||
lastJVMBug = now;
|
||||
log.warn("JVM bug occured at " + new Date(lastJVMBug)
|
||||
+ ",http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933,reactIndex="
|
||||
+ reactorIndex);
|
||||
if (jvmBug1) {
|
||||
log.debug("seeing JVM BUG(s) - recreating selector,reactIndex=" + reactorIndex);
|
||||
} else {
|
||||
jvmBug1 = true;
|
||||
log.info("seeing JVM BUG(s) - recreating selector,reactIndex=" + reactorIndex);
|
||||
}
|
||||
seeing = true;
|
||||
final Selector new_selector = SystemUtils.openSelector();
|
||||
|
||||
for (SelectionKey k : selector.keys()) {
|
||||
if (!k.isValid() || k.interestOps() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final SelectableChannel channel = k.channel();
|
||||
final Object attachment = k.attachment();
|
||||
|
||||
channel.register(new_selector, k.interestOps(), attachment);
|
||||
}
|
||||
|
||||
selector.close();
|
||||
selector = new_selector;
|
||||
|
||||
} finally {
|
||||
gate.unlock();
|
||||
}
|
||||
jvmBug.set(0);
|
||||
|
||||
} else if (jvmBug.get() == JVMBUG_THRESHHOLD || jvmBug.get() == JVMBUG_THRESHHOLD1) {
|
||||
if (jvmBug0) {
|
||||
log.debug("seeing JVM BUG(s) - cancelling interestOps==0,reactIndex=" + reactorIndex);
|
||||
} else {
|
||||
jvmBug0 = true;
|
||||
log.info("seeing JVM BUG(s) - cancelling interestOps==0,reactIndex=" + reactorIndex);
|
||||
}
|
||||
gate.lock();
|
||||
seeing = true;
|
||||
try {
|
||||
for (SelectionKey k : selector.keys()) {
|
||||
if (k.isValid() && k.interestOps() == 0) {
|
||||
k.cancel();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
gate.unlock();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
jvmBug.set(0);
|
||||
}
|
||||
return seeing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch selected event
|
||||
*
|
||||
* @param selectedKeySet
|
||||
*/
|
||||
public final void dispatchEvent(Set<SelectionKey> selectedKeySet) {
|
||||
Iterator<SelectionKey> it = selectedKeySet.iterator();
|
||||
boolean skipOpRead = false;
|
||||
while (it.hasNext()) {
|
||||
SelectionKey key = it.next();
|
||||
it.remove();
|
||||
if (!key.isValid()) {
|
||||
if (key.attachment() != null) {
|
||||
controller.closeSelectionKey(key);
|
||||
} else {
|
||||
key.cancel();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (key.isValid() && key.isAcceptable()) {
|
||||
controller.onAccept(key);
|
||||
continue;
|
||||
}
|
||||
if (key.isValid() && (key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
|
||||
// Remove write interest
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
controller.onWrite(key);
|
||||
if (!controller.isHandleReadWriteConcurrently()) {
|
||||
skipOpRead = true;
|
||||
}
|
||||
}
|
||||
if (!skipOpRead && key.isValid()
|
||||
&& (key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
|
||||
if (!controller.getStatistics().isReceiveOverFlow()) {
|
||||
// Remove read interest
|
||||
controller.onRead(key);
|
||||
} else {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||
}
|
||||
|
||||
}
|
||||
if ((key.readyOps() & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT) {
|
||||
controller.onConnect(key);
|
||||
}
|
||||
|
||||
} catch (CancelledKeyException e) {
|
||||
// ignore
|
||||
} catch (RejectedExecutionException e) {
|
||||
|
||||
if (key.attachment() instanceof AbstractNioSession) {
|
||||
((AbstractNioSession) key.attachment()).onException(e);
|
||||
}
|
||||
controller.notifyException(e);
|
||||
if (selector.isOpen()) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (key.attachment() instanceof AbstractNioSession) {
|
||||
((AbstractNioSession) key.attachment()).onException(e);
|
||||
}
|
||||
controller.closeSelectionKey(key);
|
||||
controller.notifyException(e);
|
||||
log.error("Reactor dispatch events error", e);
|
||||
if (selector.isOpen()) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void unregisterChannel(SelectableChannel channel) throws IOException {
|
||||
Selector selector = this.selector;
|
||||
if (selector != null) {
|
||||
if (channel != null) {
|
||||
SelectionKey key = channel.keyFor(selector);
|
||||
if (key != null) {
|
||||
key.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
wakeup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check session timeout or idle
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private final long checkSessionTimeout() {
|
||||
long nextTimeout = 0;
|
||||
if (configuration.getCheckSessionTimeoutInterval() > 0) {
|
||||
gate.lock();
|
||||
try {
|
||||
if (isNeedCheckSessionIdleTimeout()) {
|
||||
nextTimeout = configuration.getCheckSessionTimeoutInterval();
|
||||
for (SelectionKey key : selector.keys()) {
|
||||
|
||||
if (key.attachment() != null) {
|
||||
long n = checkExpiredIdle(key, getSessionFromAttchment(key));
|
||||
nextTimeout = n < nextTimeout ? n : nextTimeout;
|
||||
}
|
||||
}
|
||||
selectTries = 0;
|
||||
lastCheckTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
} finally {
|
||||
gate.unlock();
|
||||
}
|
||||
}
|
||||
return nextTimeout;
|
||||
}
|
||||
|
||||
private boolean isNeedCheckSessionIdleTimeout() {
|
||||
return selectTries * 1000 >= configuration.getCheckSessionTimeoutInterval()
|
||||
|| System.currentTimeMillis() - this.lastCheckTimestamp >= configuration
|
||||
.getCheckSessionTimeoutInterval();
|
||||
}
|
||||
|
||||
private final Session getSessionFromAttchment(SelectionKey key) {
|
||||
if (key.attachment() instanceof Session) {
|
||||
return (Session) key.attachment();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void registerSession(Session session, EventType event) {
|
||||
final Selector selector = this.selector;
|
||||
if (isReactorThread() && selector != null) {
|
||||
dispatchSessionEvent(session, event);
|
||||
} else {
|
||||
register.offer(new RegisterEvent(session, event));
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean isReactorThread() {
|
||||
return Thread.currentThread() == this;
|
||||
}
|
||||
|
||||
final void beforeSelect() {
|
||||
controller.checkStatisticsForRestart();
|
||||
processRegister();
|
||||
}
|
||||
|
||||
private final void processRegister() {
|
||||
RegisterEvent event = null;
|
||||
while ((event = register.poll()) != null) {
|
||||
if (event.session != null) {
|
||||
dispatchSessionEvent(event.session, event.eventType);
|
||||
} else {
|
||||
registerChannelNow(event.channel, event.ops, event.attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private final void dispatchSessionEvent(Session session, EventType event) {
|
||||
if (session.isClosed() && event != EventType.UNREGISTER) {
|
||||
return;
|
||||
}
|
||||
switch (event) {
|
||||
case REGISTER:
|
||||
controller.registerSession(session);
|
||||
break;
|
||||
case UNREGISTER:
|
||||
controller.unregisterSession(session);
|
||||
break;
|
||||
default:
|
||||
((NioSession) session).onEvent(event, selector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public final void postSelect(Set<SelectionKey> selectedKeys, Set<SelectionKey> allKeys) {
|
||||
if (controller.getSessionTimeout() > 0 || controller.getSessionIdleTimeout() > 0) {
|
||||
if (isNeedCheckSessionIdleTimeout()) {
|
||||
for (SelectionKey key : allKeys) {
|
||||
if (!selectedKeys.contains(key)) {
|
||||
if (key.attachment() != null) {
|
||||
checkExpiredIdle(key, getSessionFromAttchment(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
lastCheckTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long checkExpiredIdle(SelectionKey key, Session session) {
|
||||
if (session == null) {
|
||||
return 0;
|
||||
}
|
||||
long nextTimeout = 0;
|
||||
boolean expired = false;
|
||||
if (controller.getSessionTimeout() > 0) {
|
||||
expired = checkExpired(key, session);
|
||||
nextTimeout = controller.getSessionTimeout();
|
||||
}
|
||||
if (controller.getSessionIdleTimeout() > 0 && !expired) {
|
||||
checkIdle(session);
|
||||
nextTimeout = controller.getSessionIdleTimeout();
|
||||
}
|
||||
return nextTimeout;
|
||||
}
|
||||
|
||||
private final void checkIdle(Session session) {
|
||||
if (controller.getSessionIdleTimeout() > 0) {
|
||||
if (session.isIdle()) {
|
||||
((NioSession) session).onEvent(EventType.IDLE, selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean checkExpired(SelectionKey key, Session session) {
|
||||
if (session != null && session.isExpired()) {
|
||||
((NioSession) session).onEvent(EventType.EXPIRED, selector);
|
||||
controller.closeSelectionKey(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final void registerChannel(SelectableChannel channel, int ops, Object attachment) {
|
||||
if (isReactorThread()) {
|
||||
registerChannelNow(channel, ops, attachment);
|
||||
} else {
|
||||
register.offer(new RegisterEvent(channel, ops, attachment));
|
||||
wakeup();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void registerChannelNow(SelectableChannel channel, int ops, Object attachment) {
|
||||
if (channel.isOpen()) {
|
||||
gate.lock();
|
||||
try {
|
||||
channel.register(selector, ops, attachment);
|
||||
|
||||
} catch (ClosedChannelException e) {
|
||||
log.error("Register channel error", e);
|
||||
controller.notifyException(e);
|
||||
} finally {
|
||||
gate.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void wakeup() {
|
||||
if (wakenUp.compareAndSet(false, true)) {
|
||||
final Selector selector = this.selector;
|
||||
if (selector != null) {
|
||||
selector.wakeup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void selectNow() throws IOException {
|
||||
final Selector selector = this.selector;
|
||||
if (selector != null) {
|
||||
selector.selectNow();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,189 @@
|
||||
package com.google.code.yanf4j.nio.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.EventType;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
|
||||
/**
|
||||
* Selector manager
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class SelectorManager {
|
||||
private final Reactor[] reactorSet;
|
||||
private final AtomicInteger sets = new AtomicInteger(0);
|
||||
private final NioController controller;
|
||||
private final int dividend;
|
||||
|
||||
/**
|
||||
* Reactor count which are ready
|
||||
*/
|
||||
private int reactorReadyCount;
|
||||
|
||||
public SelectorManager(int selectorPoolSize, NioController controller, Configuration conf)
|
||||
throws IOException {
|
||||
if (selectorPoolSize <= 0) {
|
||||
throw new IllegalArgumentException("selectorPoolSize<=0");
|
||||
}
|
||||
log.info("Creating " + selectorPoolSize + " reactors...");
|
||||
reactorSet = new Reactor[selectorPoolSize];
|
||||
this.controller = controller;
|
||||
for (int i = 0; i < selectorPoolSize; i++) {
|
||||
reactorSet[i] = new Reactor(this, conf, i);
|
||||
}
|
||||
dividend = reactorSet.length - 1;
|
||||
}
|
||||
|
||||
private volatile boolean started;
|
||||
|
||||
public int getSelectorCount() {
|
||||
return reactorSet == null ? 0 : reactorSet.length;
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
started = true;
|
||||
for (Reactor reactor : reactorSet) {
|
||||
reactor.start();
|
||||
}
|
||||
}
|
||||
|
||||
Reactor getReactorFromSession(Session session) {
|
||||
Reactor reactor = (Reactor) session.getAttribute(REACTOR_ATTRIBUTE);
|
||||
|
||||
if (reactor == null) {
|
||||
reactor = nextReactor();
|
||||
final Reactor oldReactor = (Reactor) session.setAttributeIfAbsent(REACTOR_ATTRIBUTE, reactor);
|
||||
if (oldReactor != null) {
|
||||
reactor = oldReactor;
|
||||
}
|
||||
}
|
||||
return reactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find reactor by index
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public Reactor getReactorByIndex(int index) {
|
||||
if (index < 0 || index > reactorSet.length - 1) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
return reactorSet[index];
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
started = false;
|
||||
for (Reactor reactor : reactorSet) {
|
||||
reactor.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
public static final String REACTOR_ATTRIBUTE = System.currentTimeMillis() + "_Reactor_Attribute";
|
||||
|
||||
/**
|
||||
* Register channel
|
||||
*
|
||||
* @param channel
|
||||
* @param ops
|
||||
* @param attachment
|
||||
* @return
|
||||
*/
|
||||
public final Reactor registerChannel(SelectableChannel channel, int ops, Object attachment) {
|
||||
awaitReady();
|
||||
int index = 0;
|
||||
// Accept event used index 0 reactor
|
||||
if (ops == SelectionKey.OP_ACCEPT || ops == SelectionKey.OP_CONNECT) {
|
||||
index = 0;
|
||||
} else {
|
||||
index = sets.incrementAndGet() % dividend + 1;
|
||||
}
|
||||
final Reactor reactor = reactorSet[index];
|
||||
reactor.registerChannel(channel, ops, attachment);
|
||||
return reactor;
|
||||
|
||||
}
|
||||
|
||||
void awaitReady() {
|
||||
synchronized (this) {
|
||||
while (!started || reactorReadyCount != reactorSet.length) {
|
||||
try {
|
||||
this.wait(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();// reset interrupt status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next reactor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public final Reactor nextReactor() {
|
||||
if (dividend > 0) {
|
||||
return reactorSet[sets.incrementAndGet() % dividend + 1];
|
||||
} else {
|
||||
return reactorSet[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register session
|
||||
*
|
||||
* @param session
|
||||
* @param event
|
||||
*/
|
||||
public final void registerSession(Session session, EventType event) {
|
||||
if (session.isClosed() && event != EventType.UNREGISTER) {
|
||||
return;
|
||||
}
|
||||
Reactor reactor = (Reactor) session.getAttribute(REACTOR_ATTRIBUTE);
|
||||
|
||||
if (reactor == null) {
|
||||
reactor = nextReactor();
|
||||
final Reactor oldReactor = (Reactor) session.setAttributeIfAbsent(REACTOR_ATTRIBUTE, reactor);
|
||||
if (oldReactor != null) {
|
||||
reactor = oldReactor;
|
||||
}
|
||||
}
|
||||
reactor.registerSession(session, event);
|
||||
}
|
||||
|
||||
public NioController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all reactor have been ready
|
||||
*/
|
||||
synchronized void notifyReady() {
|
||||
reactorReadyCount++;
|
||||
if (reactorReadyCount == reactorSet.length) {
|
||||
controller.notifyReady();
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SelectorManager.class);
|
||||
|
||||
public final boolean isStarted() {
|
||||
return started;
|
||||
}
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
package com.google.code.yanf4j.nio.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Queue;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.CodecFactory;
|
||||
import com.google.code.yanf4j.core.EventType;
|
||||
import com.google.code.yanf4j.core.Handler;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
import com.google.code.yanf4j.core.WriteMessage;
|
||||
import com.google.code.yanf4j.core.impl.StandardSocketOption;
|
||||
import com.google.code.yanf4j.nio.NioSession;
|
||||
import com.google.code.yanf4j.nio.NioSessionConfig;
|
||||
|
||||
/**
|
||||
* Nio tcp socket controller
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public abstract class SocketChannelController extends NioController {
|
||||
|
||||
protected boolean soLingerOn = false;
|
||||
|
||||
public void setSoLinger(boolean on, int value) {
|
||||
this.soLingerOn = on;
|
||||
this.socketOptions.put(StandardSocketOption.SO_LINGER, value);
|
||||
}
|
||||
|
||||
public SocketChannelController() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SocketChannelController(Configuration configuration) {
|
||||
super(configuration, null, null);
|
||||
|
||||
}
|
||||
|
||||
public SocketChannelController(Configuration configuration, CodecFactory codecFactory) {
|
||||
super(configuration, null, codecFactory);
|
||||
}
|
||||
|
||||
public SocketChannelController(Configuration configuration, Handler handler,
|
||||
CodecFactory codecFactory) {
|
||||
super(configuration, handler, codecFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void dispatchReadEvent(SelectionKey key) {
|
||||
Session session = (Session) key.attachment();
|
||||
if (session != null) {
|
||||
((NioSession) session).onEvent(EventType.READABLE, key.selector());
|
||||
} else {
|
||||
log.warn("Could not find session for readable event,maybe it is closed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void dispatchWriteEvent(SelectionKey key) {
|
||||
Session session = (Session) key.attachment();
|
||||
if (session != null) {
|
||||
((NioSession) session).onEvent(EventType.WRITEABLE, key.selector());
|
||||
} else {
|
||||
log.warn("Could not find session for writable event,maybe it is closed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected NioSession buildSession(SocketChannel sc) {
|
||||
Queue<WriteMessage> queue = buildQueue();
|
||||
NioSessionConfig sessionConfig = buildSessionConfig(sc, queue);
|
||||
NioSession session =
|
||||
new NioTCPSession(sessionConfig, this.configuration.getSessionReadBufferSize());
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confiure socket channel
|
||||
*
|
||||
* @param sc
|
||||
* @throws IOException
|
||||
*/
|
||||
protected final void configureSocketChannel(SocketChannel sc) throws IOException {
|
||||
sc.socket().setSoTimeout(this.soTimeout);
|
||||
sc.configureBlocking(false);
|
||||
if (this.socketOptions.get(StandardSocketOption.SO_REUSEADDR) != null) {
|
||||
sc.socket().setReuseAddress(StandardSocketOption.SO_REUSEADDR.type()
|
||||
.cast(this.socketOptions.get(StandardSocketOption.SO_REUSEADDR)));
|
||||
}
|
||||
if (this.socketOptions.get(StandardSocketOption.SO_SNDBUF) != null) {
|
||||
sc.socket().setSendBufferSize(StandardSocketOption.SO_SNDBUF.type()
|
||||
.cast(this.socketOptions.get(StandardSocketOption.SO_SNDBUF)));
|
||||
}
|
||||
if (this.socketOptions.get(StandardSocketOption.SO_KEEPALIVE) != null) {
|
||||
sc.socket().setKeepAlive(StandardSocketOption.SO_KEEPALIVE.type()
|
||||
.cast(this.socketOptions.get(StandardSocketOption.SO_KEEPALIVE)));
|
||||
}
|
||||
if (this.socketOptions.get(StandardSocketOption.SO_LINGER) != null) {
|
||||
sc.socket().setSoLinger(this.soLingerOn, StandardSocketOption.SO_LINGER.type()
|
||||
.cast(this.socketOptions.get(StandardSocketOption.SO_LINGER)));
|
||||
}
|
||||
if (this.socketOptions.get(StandardSocketOption.SO_RCVBUF) != null) {
|
||||
sc.socket().setReceiveBufferSize(StandardSocketOption.SO_RCVBUF.type()
|
||||
.cast(this.socketOptions.get(StandardSocketOption.SO_RCVBUF)));
|
||||
|
||||
}
|
||||
if (this.socketOptions.get(StandardSocketOption.TCP_NODELAY) != null) {
|
||||
sc.socket().setTcpNoDelay(StandardSocketOption.TCP_NODELAY.type()
|
||||
.cast(this.socketOptions.get(StandardSocketOption.TCP_NODELAY)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
10
src/main/java/com/google/code/yanf4j/nio/package.html
Normal file
10
src/main/java/com/google/code/yanf4j/nio/package.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!--?xml version="1.0" encoding="UTF-8"?--><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!-- Copyright (c) 2007 Dustin Sallings <dustin+html@spy.net> -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Nio implementation</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Nio implementation</h1>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.statistics;
|
||||
|
||||
/**
|
||||
* Statistics
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface Statistics {
|
||||
|
||||
public void start();
|
||||
|
||||
public void stop();
|
||||
|
||||
public double getReceiveBytesPerSecond();
|
||||
|
||||
public double getSendBytesPerSecond();
|
||||
|
||||
public abstract void statisticsProcess(long n);
|
||||
|
||||
public abstract long getProcessedMessageCount();
|
||||
|
||||
public abstract double getProcessedMessageAverageTime();
|
||||
|
||||
public abstract void statisticsRead(long n);
|
||||
|
||||
public abstract void statisticsWrite(long n);
|
||||
|
||||
public abstract long getRecvMessageCount();
|
||||
|
||||
public abstract long getRecvMessageTotalSize();
|
||||
|
||||
public abstract long getRecvMessageAverageSize();
|
||||
|
||||
public abstract long getWriteMessageTotalSize();
|
||||
|
||||
public abstract long getWriteMessageCount();
|
||||
|
||||
public abstract long getWriteMessageAverageSize();
|
||||
|
||||
public abstract double getRecvMessageCountPerSecond();
|
||||
|
||||
public abstract double getWriteMessageCountPerSecond();
|
||||
|
||||
public void statisticsAccept();
|
||||
|
||||
public double getAcceptCountPerSecond();
|
||||
|
||||
public long getStartedTime();
|
||||
|
||||
public void reset();
|
||||
|
||||
public void restart();
|
||||
|
||||
public boolean isStatistics();
|
||||
|
||||
public void setReceiveThroughputLimit(double receiveThroughputLimit);
|
||||
|
||||
/**
|
||||
* Check session if receive bytes per second is over flow controll
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isReceiveOverFlow();
|
||||
|
||||
/**
|
||||
* Check session if receive bytes per second is over flow controll
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isSendOverFlow();
|
||||
|
||||
public double getSendThroughputLimit();
|
||||
|
||||
public void setSendThroughputLimit(double sendThroughputLimit);
|
||||
|
||||
public double getReceiveThroughputLimit();
|
||||
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.statistics.impl;
|
||||
|
||||
import com.google.code.yanf4j.statistics.Statistics;
|
||||
|
||||
/**
|
||||
* Default statistics implementation
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class DefaultStatistics implements Statistics {
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
public double getSendBytesPerSecond() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public double getReceiveBytesPerSecond() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isStatistics() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public long getStartedTime() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
|
||||
}
|
||||
|
||||
public double getProcessedMessageAverageTime() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getProcessedMessageCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void statisticsProcess(long n) {
|
||||
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
|
||||
}
|
||||
|
||||
public long getRecvMessageCount() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getRecvMessageTotalSize() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getRecvMessageAverageSize() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public double getRecvMessageCountPerSecond() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getWriteMessageCount() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getWriteMessageTotalSize() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getWriteMessageAverageSize() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void statisticsRead(long n) {
|
||||
|
||||
}
|
||||
|
||||
public void statisticsWrite(long n) {
|
||||
|
||||
}
|
||||
|
||||
public double getWriteMessageCountPerSecond() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public double getAcceptCountPerSecond() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void statisticsAccept() {
|
||||
|
||||
}
|
||||
|
||||
public void setReceiveThroughputLimit(double receivePacketRate) {}
|
||||
|
||||
public boolean isReceiveOverFlow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isSendOverFlow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public double getSendThroughputLimit() {
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
public void setSendThroughputLimit(double sendThroughputLimit) {}
|
||||
|
||||
public final double getReceiveThroughputLimit() {
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.statistics.impl;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import com.google.code.yanf4j.statistics.Statistics;
|
||||
|
||||
/**
|
||||
* A simple statistics implementation
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class SimpleStatistics implements Statistics {
|
||||
private boolean started = false;
|
||||
|
||||
public boolean isStatistics() {
|
||||
return this.started;
|
||||
}
|
||||
|
||||
public synchronized void reset() {
|
||||
if (this.started) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
this.startTime = this.stopTime = -1;
|
||||
this.recvMessageCount.set(0);
|
||||
this.recvMessageTotalSize.set(0);
|
||||
this.writeMessageCount.set(0);
|
||||
this.writeMessageTotalSize.set(0);
|
||||
this.processMessageCount.set(0);
|
||||
this.processMessageTotalTime.set(0);
|
||||
this.acceptCount.set(0);
|
||||
}
|
||||
|
||||
private double receiveThroughputLimit = -1.0; // receive bytes per second
|
||||
private double sendThroughputLimit = -1.0; // send bytes per second
|
||||
|
||||
public void setReceiveThroughputLimit(double receivePacketRate) {
|
||||
this.receiveThroughputLimit = receivePacketRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check session if receive bytes per second is over flow controll
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isReceiveOverFlow() {
|
||||
if (getReceiveThroughputLimit() < 0.0000f) {
|
||||
return false;
|
||||
}
|
||||
return getReceiveBytesPerSecond() > getReceiveThroughputLimit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check session if receive bytes per second is over flow controll
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isSendOverFlow() {
|
||||
if (getSendThroughputLimit() < 0.0000f) {
|
||||
return false;
|
||||
}
|
||||
return getSendBytesPerSecond() > getSendThroughputLimit();
|
||||
}
|
||||
|
||||
public double getSendThroughputLimit() {
|
||||
return this.sendThroughputLimit;
|
||||
}
|
||||
|
||||
public void setSendThroughputLimit(double sendThroughputLimit) {
|
||||
this.sendThroughputLimit = sendThroughputLimit;
|
||||
}
|
||||
|
||||
public final double getReceiveThroughputLimit() {
|
||||
return this.receiveThroughputLimit;
|
||||
}
|
||||
|
||||
public synchronized void restart() {
|
||||
stop();
|
||||
reset();
|
||||
start();
|
||||
}
|
||||
|
||||
private long startTime, stopTime = -1;
|
||||
|
||||
private AtomicLong recvMessageCount = new AtomicLong();
|
||||
|
||||
private AtomicLong recvMessageTotalSize = new AtomicLong();
|
||||
|
||||
private AtomicLong writeMessageCount = new AtomicLong();
|
||||
|
||||
private AtomicLong writeMessageTotalSize = new AtomicLong();
|
||||
|
||||
private AtomicLong processMessageCount = new AtomicLong();
|
||||
|
||||
private AtomicLong acceptCount = new AtomicLong();
|
||||
|
||||
private AtomicLong processMessageTotalTime = new AtomicLong();
|
||||
|
||||
public long getStartedTime() {
|
||||
return this.startTime;
|
||||
}
|
||||
|
||||
public double getProcessedMessageAverageTime() {
|
||||
return this.processMessageCount.get() == 0 ? 0
|
||||
: (double) this.processMessageTotalTime.get() / this.processMessageCount.get();
|
||||
}
|
||||
|
||||
public long getProcessedMessageCount() {
|
||||
return this.processMessageCount.get();
|
||||
}
|
||||
|
||||
public void statisticsProcess(long n) {
|
||||
if (!this.started) {
|
||||
return;
|
||||
}
|
||||
if (n < 0) {
|
||||
return;
|
||||
}
|
||||
this.processMessageTotalTime.addAndGet(n);
|
||||
this.processMessageCount.incrementAndGet();
|
||||
}
|
||||
|
||||
public SimpleStatistics() {
|
||||
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.started = true;
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
this.stopTime = System.currentTimeMillis();
|
||||
this.started = false;
|
||||
}
|
||||
|
||||
public void statisticsRead(long n) {
|
||||
if (!this.started) {
|
||||
return;
|
||||
}
|
||||
if (n <= 0) {
|
||||
return;
|
||||
}
|
||||
this.recvMessageCount.incrementAndGet();
|
||||
this.recvMessageTotalSize.addAndGet(n);
|
||||
}
|
||||
|
||||
public long getRecvMessageCount() {
|
||||
return this.recvMessageCount.get();
|
||||
}
|
||||
|
||||
public long getRecvMessageTotalSize() {
|
||||
return this.recvMessageTotalSize.get();
|
||||
}
|
||||
|
||||
public long getWriteMessageCount() {
|
||||
return this.writeMessageCount.get();
|
||||
}
|
||||
|
||||
public long getWriteMessageTotalSize() {
|
||||
return this.writeMessageTotalSize.get();
|
||||
}
|
||||
|
||||
public void statisticsWrite(long n) {
|
||||
if (!this.started) {
|
||||
return;
|
||||
}
|
||||
if (n <= 0) {
|
||||
return;
|
||||
}
|
||||
this.writeMessageCount.incrementAndGet();
|
||||
this.writeMessageTotalSize.addAndGet(n);
|
||||
}
|
||||
|
||||
public long getRecvMessageAverageSize() {
|
||||
return this.recvMessageCount.get() == 0 ? 0
|
||||
: this.recvMessageTotalSize.get() / this.recvMessageCount.get();
|
||||
}
|
||||
|
||||
public double getRecvMessageCountPerSecond() {
|
||||
long duration = (this.stopTime == -1) ? (System.currentTimeMillis() - this.startTime)
|
||||
: (this.stopTime - this.startTime);
|
||||
return duration == 0 ? 0 : (double) this.recvMessageCount.get() * 1000 / duration;
|
||||
}
|
||||
|
||||
public double getWriteMessageCountPerSecond() {
|
||||
long duration = (this.stopTime == -1) ? (System.currentTimeMillis() - this.startTime)
|
||||
: (this.stopTime - this.startTime);
|
||||
return duration == 0 ? 0 : (double) this.writeMessageCount.get() * 1000 / duration;
|
||||
}
|
||||
|
||||
public long getWriteMessageAverageSize() {
|
||||
return this.writeMessageCount.get() == 0 ? 0
|
||||
: this.writeMessageTotalSize.get() / this.writeMessageCount.get();
|
||||
}
|
||||
|
||||
public double getAcceptCountPerSecond() {
|
||||
long duration = (this.stopTime == -1) ? (System.currentTimeMillis() - this.startTime)
|
||||
: (this.stopTime - this.startTime);
|
||||
return duration == 0 ? 0 : (double) this.acceptCount.get() * 1000 / duration;
|
||||
}
|
||||
|
||||
public double getReceiveBytesPerSecond() {
|
||||
long duration = (this.stopTime == -1) ? (System.currentTimeMillis() - this.startTime)
|
||||
: (this.stopTime - this.startTime);
|
||||
return duration == 0 ? 0 : (double) this.recvMessageTotalSize.get() * 1000 / duration;
|
||||
}
|
||||
|
||||
public double getSendBytesPerSecond() {
|
||||
long duration = (this.stopTime == -1) ? (System.currentTimeMillis() - this.startTime)
|
||||
: (this.stopTime - this.startTime);
|
||||
return duration == 0 ? 0 : (double) this.writeMessageTotalSize.get() * 1000 / duration;
|
||||
}
|
||||
|
||||
public void statisticsAccept() {
|
||||
if (!this.started) {
|
||||
return;
|
||||
}
|
||||
this.acceptCount.incrementAndGet();
|
||||
}
|
||||
|
||||
}
|
10
src/main/java/com/google/code/yanf4j/statistics/package.html
Normal file
10
src/main/java/com/google/code/yanf4j/statistics/package.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!--?xml version="1.0" encoding="UTF-8"?--><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!-- Copyright (c) 2007 Dustin Sallings <dustin+html@spy.net> -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Statistics</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Networking statistics</h1>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
|
||||
/**
|
||||
* ByteBuffer matcher
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface ByteBufferMatcher {
|
||||
|
||||
public int matchFirst(IoBuffer buffer);
|
||||
|
||||
public List<Integer> matchAll(final IoBuffer buffer);
|
||||
|
||||
}
|
197
src/main/java/com/google/code/yanf4j/util/ByteBufferUtils.java
Normal file
197
src/main/java/com/google/code/yanf4j/util/ByteBufferUtils.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
/*
|
||||
* Copyright 2004-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
/**
|
||||
* 来自于cindy2.4的工具类,做了简化和新增
|
||||
*/
|
||||
import java.nio.ByteBuffer;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
|
||||
public class ByteBufferUtils {
|
||||
/**
|
||||
*
|
||||
* @param byteBuffer
|
||||
* @return *
|
||||
*/
|
||||
public static final ByteBuffer increaseBufferCapatity(ByteBuffer byteBuffer) {
|
||||
|
||||
if (byteBuffer == null) {
|
||||
throw new IllegalArgumentException("buffer is null");
|
||||
}
|
||||
if (Configuration.DEFAULT_INCREASE_BUFF_SIZE < 0) {
|
||||
throw new IllegalArgumentException("size less than 0");
|
||||
}
|
||||
|
||||
int capacity = byteBuffer.capacity() + Configuration.DEFAULT_INCREASE_BUFF_SIZE;
|
||||
if (capacity < 0) {
|
||||
throw new IllegalArgumentException("capacity can't be negative");
|
||||
}
|
||||
ByteBuffer result = (byteBuffer.isDirect() ? ByteBuffer.allocateDirect(capacity)
|
||||
: ByteBuffer.allocate(capacity));
|
||||
result.order(byteBuffer.order());
|
||||
byteBuffer.flip();
|
||||
result.put(byteBuffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final void flip(ByteBuffer[] buffers) {
|
||||
if (buffers == null) {
|
||||
return;
|
||||
}
|
||||
for (ByteBuffer buffer : buffers) {
|
||||
if (buffer != null) {
|
||||
buffer.flip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final ByteBuffer gather(ByteBuffer[] buffers) {
|
||||
if (buffers == null || buffers.length == 0) {
|
||||
return null;
|
||||
}
|
||||
ByteBuffer result = ByteBuffer.allocate(remaining(buffers));
|
||||
result.order(buffers[0].order());
|
||||
for (int i = 0; i < buffers.length; i++) {
|
||||
if (buffers[i] != null) {
|
||||
result.put(buffers[i]);
|
||||
}
|
||||
}
|
||||
result.flip();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final int remaining(ByteBuffer[] buffers) {
|
||||
if (buffers == null) {
|
||||
return 0;
|
||||
}
|
||||
int remaining = 0;
|
||||
for (int i = 0; i < buffers.length; i++) {
|
||||
if (buffers[i] != null) {
|
||||
remaining += buffers[i].remaining();
|
||||
}
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
|
||||
public static final void clear(ByteBuffer[] buffers) {
|
||||
if (buffers == null) {
|
||||
return;
|
||||
}
|
||||
for (ByteBuffer buffer : buffers) {
|
||||
if (buffer != null) {
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final String toHex(byte b) {
|
||||
return ("" + "0123456789ABCDEF".charAt(0xf & b >> 4) + "0123456789ABCDEF".charAt(b & 0xf));
|
||||
}
|
||||
|
||||
public static final int indexOf(ByteBuffer buffer, ByteBuffer pattern) {
|
||||
if (pattern == null || buffer == null) {
|
||||
return -1;
|
||||
}
|
||||
int n = buffer.remaining();
|
||||
int m = pattern.remaining();
|
||||
int patternPos = pattern.position();
|
||||
int bufferPos = buffer.position();
|
||||
if (n < m) {
|
||||
return -1;
|
||||
}
|
||||
for (int s = 0; s <= n - m; s++) {
|
||||
boolean match = true;
|
||||
for (int i = 0; i < m; i++) {
|
||||
if (buffer.get(s + i + bufferPos) != pattern.get(patternPos + i)) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
return (bufferPos + s);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static final int indexOf(ByteBuffer buffer, ByteBuffer pattern, int offset) {
|
||||
if (offset < 0) {
|
||||
throw new IllegalArgumentException("offset must be greater than 0");
|
||||
}
|
||||
if (pattern == null || buffer == null) {
|
||||
return -1;
|
||||
}
|
||||
int patternPos = pattern.position();
|
||||
int n = buffer.remaining();
|
||||
int m = pattern.remaining();
|
||||
if (n < m) {
|
||||
return -1;
|
||||
}
|
||||
if (offset < buffer.position() || offset > buffer.limit()) {
|
||||
return -1;
|
||||
}
|
||||
for (int s = 0; s <= n - m; s++) {
|
||||
boolean match = true;
|
||||
for (int i = 0; i < m; i++) {
|
||||
if (buffer.get(s + i + offset) != pattern.get(patternPos + i)) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
return (offset + s);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看ByteBuffer数组是否还有剩余
|
||||
*
|
||||
* @param buffers ByteBuffers
|
||||
* @return have remaining
|
||||
*/
|
||||
public static final boolean hasRemaining(ByteBuffer[] buffers) {
|
||||
if (buffers == null) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < buffers.length; i++) {
|
||||
if (buffers[i] != null && buffers[i].hasRemaining()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final int uByte(byte b) {
|
||||
return b & 0xFF;
|
||||
}
|
||||
}
|
342
src/main/java/com/google/code/yanf4j/util/CircularQueue.java
Normal file
342
src/main/java/com/google/code/yanf4j/util/CircularQueue.java
Normal file
@@ -0,0 +1,342 @@
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work for additional information regarding
|
||||
* copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* A circular queue from mina
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public class CircularQueue<E> extends AbstractList<E> implements List<E>, Queue<E>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3993421269224511264L;
|
||||
|
||||
private static final int DEFAULT_CAPACITY = 4;
|
||||
|
||||
private final int initialCapacity;
|
||||
// XXX: This volatile keyword here is a workaround for SUN Java Compiler
|
||||
// bug,
|
||||
// which produces buggy byte code. I don't event know why adding a volatile
|
||||
// fixes the problem. Eclipse Java Compiler seems to produce correct byte
|
||||
// code.
|
||||
private volatile Object[] items;
|
||||
private int mask;
|
||||
private int first = 0;
|
||||
private int last = 0;
|
||||
private boolean full;
|
||||
private int shrinkThreshold;
|
||||
|
||||
/**
|
||||
* Construct a new, empty queue.
|
||||
*/
|
||||
public CircularQueue() {
|
||||
this(DEFAULT_CAPACITY);
|
||||
}
|
||||
|
||||
public CircularQueue(int initialCapacity) {
|
||||
int actualCapacity = normalizeCapacity(initialCapacity);
|
||||
this.items = new Object[actualCapacity];
|
||||
this.mask = actualCapacity - 1;
|
||||
this.initialCapacity = actualCapacity;
|
||||
this.shrinkThreshold = 0;
|
||||
}
|
||||
|
||||
private static int normalizeCapacity(int initialCapacity) {
|
||||
int actualCapacity = 1;
|
||||
while (actualCapacity < initialCapacity) {
|
||||
actualCapacity <<= 1;
|
||||
if (actualCapacity < 0) {
|
||||
actualCapacity = 1 << 30;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return actualCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the capacity of this queue.
|
||||
*/
|
||||
public int capacity() {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if (!isEmpty()) {
|
||||
Arrays.fill(this.items, null);
|
||||
this.first = 0;
|
||||
this.last = 0;
|
||||
this.full = false;
|
||||
shrinkIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public E poll() {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object ret = this.items[this.first];
|
||||
this.items[this.first] = null;
|
||||
decreaseSize();
|
||||
|
||||
if (this.first == this.last) {
|
||||
this.first = this.last = 0;
|
||||
}
|
||||
|
||||
shrinkIfNeeded();
|
||||
return (E) ret;
|
||||
}
|
||||
|
||||
public boolean offer(E item) {
|
||||
if (item == null) {
|
||||
throw new NullPointerException("item");
|
||||
}
|
||||
expandIfNeeded();
|
||||
this.items[this.last] = item;
|
||||
increaseSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public E peek() {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (E) this.items[this.first];
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public E get(int idx) {
|
||||
checkIndex(idx);
|
||||
return (E) this.items[getRealIndex(idx)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return (this.first == this.last) && !this.full;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
if (this.full) {
|
||||
return capacity();
|
||||
}
|
||||
|
||||
if (this.last >= this.first) {
|
||||
return this.last - this.first;
|
||||
} else {
|
||||
return this.last - this.first + capacity();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "first=" + this.first + ", last=" + this.last + ", size=" + size() + ", mask = "
|
||||
+ this.mask;
|
||||
}
|
||||
|
||||
private void checkIndex(int idx) {
|
||||
if (idx < 0 || idx >= size()) {
|
||||
throw new IndexOutOfBoundsException(String.valueOf(idx));
|
||||
}
|
||||
}
|
||||
|
||||
private int getRealIndex(int idx) {
|
||||
return (this.first + idx) & this.mask;
|
||||
}
|
||||
|
||||
private void increaseSize() {
|
||||
this.last = (this.last + 1) & this.mask;
|
||||
this.full = this.first == this.last;
|
||||
}
|
||||
|
||||
private void decreaseSize() {
|
||||
this.first = (this.first + 1) & this.mask;
|
||||
this.full = false;
|
||||
}
|
||||
|
||||
private void expandIfNeeded() {
|
||||
if (this.full) {
|
||||
// expand queue
|
||||
final int oldLen = this.items.length;
|
||||
final int newLen = oldLen << 1;
|
||||
Object[] tmp = new Object[newLen];
|
||||
|
||||
if (this.first < this.last) {
|
||||
System.arraycopy(this.items, this.first, tmp, 0, this.last - this.first);
|
||||
} else {
|
||||
System.arraycopy(this.items, this.first, tmp, 0, oldLen - this.first);
|
||||
System.arraycopy(this.items, 0, tmp, oldLen - this.first, this.last);
|
||||
}
|
||||
|
||||
this.first = 0;
|
||||
this.last = oldLen;
|
||||
this.items = tmp;
|
||||
this.mask = tmp.length - 1;
|
||||
if (newLen >>> 3 > this.initialCapacity) {
|
||||
this.shrinkThreshold = newLen >>> 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void shrinkIfNeeded() {
|
||||
int size = size();
|
||||
if (size <= this.shrinkThreshold) {
|
||||
// shrink queue
|
||||
final int oldLen = this.items.length;
|
||||
int newLen = normalizeCapacity(size);
|
||||
if (size == newLen) {
|
||||
newLen <<= 1;
|
||||
}
|
||||
|
||||
if (newLen >= oldLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLen < this.initialCapacity) {
|
||||
if (oldLen == this.initialCapacity) {
|
||||
return;
|
||||
} else {
|
||||
newLen = this.initialCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
Object[] tmp = new Object[newLen];
|
||||
|
||||
// Copy only when there's something to copy.
|
||||
if (size > 0) {
|
||||
if (this.first < this.last) {
|
||||
System.arraycopy(this.items, this.first, tmp, 0, this.last - this.first);
|
||||
} else {
|
||||
System.arraycopy(this.items, this.first, tmp, 0, oldLen - this.first);
|
||||
System.arraycopy(this.items, 0, tmp, oldLen - this.first, this.last);
|
||||
}
|
||||
}
|
||||
|
||||
this.first = 0;
|
||||
this.last = size;
|
||||
this.items = tmp;
|
||||
this.mask = tmp.length - 1;
|
||||
this.shrinkThreshold = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E o) {
|
||||
return offer(o);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public E set(int idx, E o) {
|
||||
checkIndex(idx);
|
||||
|
||||
int realIdx = getRealIndex(idx);
|
||||
Object old = this.items[realIdx];
|
||||
this.items[realIdx] = o;
|
||||
return (E) old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int idx, E o) {
|
||||
if (idx == size()) {
|
||||
offer(o);
|
||||
return;
|
||||
}
|
||||
|
||||
checkIndex(idx);
|
||||
expandIfNeeded();
|
||||
|
||||
int realIdx = getRealIndex(idx);
|
||||
|
||||
// Make a room for a new element.
|
||||
if (this.first < this.last) {
|
||||
System.arraycopy(this.items, realIdx, this.items, realIdx + 1, this.last - realIdx);
|
||||
} else {
|
||||
if (realIdx >= this.first) {
|
||||
System.arraycopy(this.items, 0, this.items, 1, this.last);
|
||||
this.items[0] = this.items[this.items.length - 1];
|
||||
System.arraycopy(this.items, realIdx, this.items, realIdx + 1,
|
||||
this.items.length - realIdx - 1);
|
||||
} else {
|
||||
System.arraycopy(this.items, realIdx, this.items, realIdx + 1, this.last - realIdx);
|
||||
}
|
||||
}
|
||||
|
||||
this.items[realIdx] = o;
|
||||
increaseSize();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public E remove(int idx) {
|
||||
if (idx == 0) {
|
||||
return poll();
|
||||
}
|
||||
|
||||
checkIndex(idx);
|
||||
|
||||
int realIdx = getRealIndex(idx);
|
||||
Object removed = this.items[realIdx];
|
||||
|
||||
// Remove a room for the removed element.
|
||||
if (this.first < this.last) {
|
||||
System.arraycopy(this.items, this.first, this.items, this.first + 1, realIdx - this.first);
|
||||
} else {
|
||||
if (realIdx >= this.first) {
|
||||
System.arraycopy(this.items, this.first, this.items, this.first + 1, realIdx - this.first);
|
||||
} else {
|
||||
System.arraycopy(this.items, 0, this.items, 1, realIdx);
|
||||
this.items[0] = this.items[this.items.length - 1];
|
||||
System.arraycopy(this.items, this.first, this.items, this.first + 1,
|
||||
this.items.length - this.first - 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.items[this.first] = null;
|
||||
decreaseSize();
|
||||
|
||||
shrinkIfNeeded();
|
||||
return (E) removed;
|
||||
}
|
||||
|
||||
public E remove() {
|
||||
if (isEmpty()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return poll();
|
||||
}
|
||||
|
||||
public E element() {
|
||||
if (isEmpty()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return peek();
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* A {@link ConcurrentHashMap}-backed {@link Set}.
|
||||
*
|
||||
* @author The Apache MINA Project (dev@mina.apache.org)
|
||||
* @version $Rev: 597692 $, $Date: 2007-11-23 08:56:32 -0700 (Fri, 23 Nov 2007) $
|
||||
*/
|
||||
public class ConcurrentHashSet<E> extends MapBackedSet<E> {
|
||||
|
||||
private static final long serialVersionUID = 8518578988740277828L;
|
||||
|
||||
public ConcurrentHashSet() {
|
||||
super(new ConcurrentHashMap<E, Boolean>());
|
||||
}
|
||||
|
||||
public ConcurrentHashSet(Collection<E> c) {
|
||||
super(new ConcurrentHashMap<E, Boolean>(), c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E o) {
|
||||
Boolean answer = ((ConcurrentMap<E, Boolean>) map).putIfAbsent(o, Boolean.TRUE);
|
||||
return answer == null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.google.code.yanf4j.core.impl.PoolDispatcher;
|
||||
|
||||
/**
|
||||
* Dispatcher Factory
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class DispatcherFactory {
|
||||
public static com.google.code.yanf4j.core.Dispatcher newDispatcher(int size,
|
||||
RejectedExecutionHandler rejectedExecutionHandler, String prefix) {
|
||||
if (size > 0) {
|
||||
return new PoolDispatcher(size, 60, TimeUnit.SECONDS, rejectedExecutionHandler, prefix);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,780 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
||||
*
|
||||
* Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* The contents of this file are subject to the terms of either the GNU General Public License
|
||||
* Version 2 only ("GPL") or the Common Development and Distribution License("CDDL") (collectively,
|
||||
* the "License"). You may not use this file except in compliance with the License. You can obtain a
|
||||
* copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html or
|
||||
* glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*
|
||||
* When distributing the software, include this License Header Notice in each file and include the
|
||||
* License file at glassfish/bootstrap/legal/LICENSE.txt. Sun designates this particular file as
|
||||
* subject to the "Classpath" exception as provided by Sun in the GPL Version 2 section of the
|
||||
* License file that accompanied this code. If applicable, add the following below the License
|
||||
* Header, with the fields enclosed by brackets [] replaced by your own identifying information:
|
||||
* "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* If you wish your version of this file to be governed by only the CDDL or only the GPL Version 2,
|
||||
* indicate your decision by adding "[Contributor] elects to include this software in this
|
||||
* distribution under the [CDDL or GPL Version 2] license." If you don't indicate a single choice of
|
||||
* license, a recipient has the option to distribute your version of this file under either the
|
||||
* CDDL, the GPL Version 2 or to extend the choice of license to its licensees as provided above.
|
||||
* However, if you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then the
|
||||
* option applies only if the new code is made subject to such option by the copyright holder.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the
|
||||
* public domain, as explained at http://creativecommons.org/licenses/publicdomain
|
||||
*/
|
||||
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
* An unbounded <code>TransferQueue</code> based on linked nodes. This queue orders elements FIFO
|
||||
* (first-in-first-out) with respect to any given producer. The <em>head</em> of the queue is that
|
||||
* element that has been on the queue the longest time for some producer. The <em>tail</em> of the
|
||||
* queue is that element that has been on the queue the shortest time for some producer.
|
||||
*
|
||||
* <p>
|
||||
* Beware that, unlike in most collections, the <code>size</code> method is <em>NOT</em> a constant-time
|
||||
* operation. Because of the asynchronous nature of these queues, determining the current number of
|
||||
* elements requires a traversal of the elements.
|
||||
*
|
||||
* <p>
|
||||
* This class and its iterator implement all of the <em>optional</em> methods of the
|
||||
* {@link Collection} and {@link Iterator} interfaces.
|
||||
*
|
||||
* <p>
|
||||
* Memory consistency effects: As with other concurrent collections, actions in a thread prior to
|
||||
* placing an object into a {@code LinkedTransferQueue}
|
||||
* <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> actions subsequent to
|
||||
* the access or removal of that element from the {@code LinkedTransferQueue} in another thread.
|
||||
*
|
||||
* @author Doug Lea
|
||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||
* @author Trustin Lee (tlee@redhat.com)
|
||||
*
|
||||
* @param <E> the type of elements held in this collection
|
||||
*
|
||||
*/
|
||||
public class LinkedTransferQueue<E> extends AbstractQueue<E> implements BlockingQueue<E> {
|
||||
|
||||
/*
|
||||
* This class extends the approach used in FIFO-mode SynchronousQueues. See the internal
|
||||
* documentation, as well as the PPoPP 2006 paper "Scalable Synchronous Queues" by Scherer, Lea &
|
||||
* Scott (http://www.cs.rice.edu/~wns1/papers/2006-PPoPP-SQ.pdf)
|
||||
*
|
||||
* The main extension is to provide different Wait modes for the main "xfer" method that puts or
|
||||
* takes items. These don't impact the basic dual-queue logic, but instead control whether or how
|
||||
* threads block upon insertion of request or data nodes into the dual queue. It also uses
|
||||
* slightly different conventions for tracking whether nodes are off-list or cancelled.
|
||||
*/
|
||||
|
||||
// Wait modes for xfer method
|
||||
private static final int NOWAIT = 0;
|
||||
private static final int TIMEOUT = 1;
|
||||
private static final int WAIT = 2;
|
||||
|
||||
/** The number of CPUs, for spin control */
|
||||
private static final int NCPUS = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
/**
|
||||
* The number of times to spin before blocking in timed waits. The value is empirically derived --
|
||||
* it works well across a variety of processors and OSes. Empirically, the best value seems not to
|
||||
* vary with number of CPUs (beyond 2) so is just a constant.
|
||||
*/
|
||||
private static final int maxTimedSpins = NCPUS < 2 ? 0 : 32;
|
||||
|
||||
/**
|
||||
* The number of times to spin before blocking in untimed waits. This is greater than timed value
|
||||
* because untimed waits spin faster since they don't need to check times on each spin.
|
||||
*/
|
||||
private static final int maxUntimedSpins = maxTimedSpins * 16;
|
||||
|
||||
/**
|
||||
* The number of nanoseconds for which it is faster to spin rather than to use timed park. A rough
|
||||
* estimate suffices.
|
||||
*/
|
||||
private static final long spinForTimeoutThreshold = 1000L;
|
||||
|
||||
/**
|
||||
* Node class for LinkedTransferQueue. Opportunistically subclasses from AtomicReference to
|
||||
* represent item. Uses Object, not E, to allow setting item to "this" after use, to avoid garbage
|
||||
* retention. Similarly, setting the next field to this is used as sentinel that node is off list.
|
||||
*/
|
||||
private static final class QNode extends AtomicReference<Object> {
|
||||
private static final long serialVersionUID = 5925596372370723938L;
|
||||
|
||||
transient volatile QNode next;
|
||||
transient volatile Thread waiter; // to control park/unpark
|
||||
final boolean isData;
|
||||
public long p1, p2, p3, p4, p5, p6, p7;
|
||||
|
||||
QNode(Object item, boolean isData) {
|
||||
super(item);
|
||||
this.isData = isData;
|
||||
}
|
||||
|
||||
private static final AtomicReferenceFieldUpdater<QNode, QNode> nextUpdater;
|
||||
static {
|
||||
AtomicReferenceFieldUpdater<QNode, QNode> tmp = null;
|
||||
try {
|
||||
tmp = AtomicReferenceFieldUpdater.newUpdater(QNode.class, QNode.class, "next");
|
||||
|
||||
// Test if AtomicReferenceFieldUpdater is really working.
|
||||
QNode testNode = new QNode(null, false);
|
||||
tmp.set(testNode, testNode);
|
||||
if (testNode.next != testNode) {
|
||||
// Not set as expected - fall back to the safe mode.
|
||||
throw new Exception();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// Running in a restricted environment with a security manager.
|
||||
tmp = null;
|
||||
}
|
||||
nextUpdater = tmp;
|
||||
}
|
||||
|
||||
boolean casNext(QNode cmp, QNode val) {
|
||||
if (nextUpdater != null) {
|
||||
return nextUpdater.compareAndSet(this, cmp, val);
|
||||
} else {
|
||||
return alternativeCasNext(cmp, val);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean alternativeCasNext(QNode cmp, QNode val) {
|
||||
if (this.next == cmp) {
|
||||
this.next = val;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Padded version of AtomicReference used for head, tail and cleanMe, to alleviate contention
|
||||
* across threads CASing one vs the other.
|
||||
*/
|
||||
public static final class PaddedAtomicReference<T> extends AtomicReference<T> {
|
||||
private static final long serialVersionUID = 4684288940772921317L;
|
||||
|
||||
// enough padding for 64bytes with 4byte refs
|
||||
@SuppressWarnings("unused")
|
||||
public Object p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb, pc, pd, pe;
|
||||
|
||||
public PaddedAtomicReference(T r) {
|
||||
super(r);
|
||||
}
|
||||
}
|
||||
|
||||
/** head of the queue */
|
||||
private final PaddedAtomicReference<QNode> head;
|
||||
/** tail of the queue */
|
||||
private final PaddedAtomicReference<QNode> tail;
|
||||
|
||||
/**
|
||||
* Reference to a cancelled node that might not yet have been unlinked from queue because it was
|
||||
* the last inserted node when it cancelled.
|
||||
*/
|
||||
private final PaddedAtomicReference<QNode> cleanMe;
|
||||
|
||||
/**
|
||||
* Tries to cas nh as new head; if successful, unlink old head's next node to avoid garbage
|
||||
* retention.
|
||||
*/
|
||||
private boolean advanceHead(QNode h, QNode nh) {
|
||||
if (h == this.head.get() && this.head.compareAndSet(h, nh)) {
|
||||
h.next = h; // forget old next
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts or takes an item. Used for most queue operations (except poll() and tryTransfer()). See
|
||||
* the similar code in SynchronousQueue for detailed explanation.
|
||||
*
|
||||
* @param e the item or if null, signifies that this is a take
|
||||
* @param mode the wait mode: NOWAIT, TIMEOUT, WAIT
|
||||
* @param nanos timeout in nanosecs, used only if mode is TIMEOUT
|
||||
* @return an item, or null on failure
|
||||
*/
|
||||
private Object xfer(Object e, int mode, long nanos) {
|
||||
boolean isData = e != null;
|
||||
QNode s = null;
|
||||
final PaddedAtomicReference<QNode> head = this.head;
|
||||
final PaddedAtomicReference<QNode> tail = this.tail;
|
||||
|
||||
for (;;) {
|
||||
QNode t = tail.get();
|
||||
QNode h = head.get();
|
||||
|
||||
if (t != null && (t == h || t.isData == isData)) {
|
||||
if (s == null) {
|
||||
s = new QNode(e, isData);
|
||||
}
|
||||
QNode last = t.next;
|
||||
if (last != null) {
|
||||
if (t == tail.get()) {
|
||||
tail.compareAndSet(t, last);
|
||||
}
|
||||
} else if (t.casNext(null, s)) {
|
||||
tail.compareAndSet(t, s);
|
||||
return awaitFulfill(t, s, e, mode, nanos);
|
||||
}
|
||||
}
|
||||
|
||||
else if (h != null) {
|
||||
QNode first = h.next;
|
||||
if (t == tail.get() && first != null && advanceHead(h, first)) {
|
||||
Object x = first.get();
|
||||
if (x != first && first.compareAndSet(x, e)) {
|
||||
LockSupport.unpark(first.waiter);
|
||||
return isData ? e : x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of xfer for poll() and tryTransfer, which simplifies control paths both here and in
|
||||
* xfer
|
||||
*/
|
||||
private Object fulfill(Object e) {
|
||||
boolean isData = e != null;
|
||||
final PaddedAtomicReference<QNode> head = this.head;
|
||||
final PaddedAtomicReference<QNode> tail = this.tail;
|
||||
|
||||
for (;;) {
|
||||
QNode t = tail.get();
|
||||
QNode h = head.get();
|
||||
|
||||
if (t != null && (t == h || t.isData == isData)) {
|
||||
QNode last = t.next;
|
||||
if (t == tail.get()) {
|
||||
if (last != null) {
|
||||
tail.compareAndSet(t, last);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else if (h != null) {
|
||||
QNode first = h.next;
|
||||
if (t == tail.get() && first != null && advanceHead(h, first)) {
|
||||
Object x = first.get();
|
||||
if (x != first && first.compareAndSet(x, e)) {
|
||||
LockSupport.unpark(first.waiter);
|
||||
return isData ? e : x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spins/blocks until node s is fulfilled or caller gives up, depending on wait mode.
|
||||
*
|
||||
* @param pred the predecessor of waiting node
|
||||
* @param s the waiting node
|
||||
* @param e the comparison value for checking match
|
||||
* @param mode mode
|
||||
* @param nanos timeout value
|
||||
* @return matched item, or s if cancelled
|
||||
*/
|
||||
private Object awaitFulfill(QNode pred, QNode s, Object e, int mode, long nanos) {
|
||||
if (mode == NOWAIT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long lastTime = mode == TIMEOUT ? System.nanoTime() : 0;
|
||||
Thread w = Thread.currentThread();
|
||||
int spins = -1; // set to desired spin count below
|
||||
for (;;) {
|
||||
if (w.isInterrupted()) {
|
||||
s.compareAndSet(e, s);
|
||||
}
|
||||
Object x = s.get();
|
||||
if (x != e) { // Node was matched or cancelled
|
||||
advanceHead(pred, s); // unlink if head
|
||||
if (x == s) { // was cancelled
|
||||
clean(pred, s);
|
||||
return null;
|
||||
} else if (x != null) {
|
||||
s.set(s); // avoid garbage retention
|
||||
return x;
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
if (mode == TIMEOUT) {
|
||||
long now = System.nanoTime();
|
||||
nanos -= now - lastTime;
|
||||
lastTime = now;
|
||||
if (nanos <= 0) {
|
||||
s.compareAndSet(e, s); // try to cancel
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (spins < 0) {
|
||||
QNode h = this.head.get(); // only spin if at head
|
||||
spins = h != null && h.next == s ? (mode == TIMEOUT ? maxTimedSpins : maxUntimedSpins) : 0;
|
||||
}
|
||||
if (spins > 0) {
|
||||
--spins;
|
||||
} else if (s.waiter == null) {
|
||||
s.waiter = w;
|
||||
} else if (mode != TIMEOUT) {
|
||||
// LockSupport.park(this);
|
||||
LockSupport.park(); // allows run on java5
|
||||
s.waiter = null;
|
||||
spins = -1;
|
||||
} else if (nanos > spinForTimeoutThreshold) {
|
||||
// LockSupport.parkNanos(this, nanos);
|
||||
LockSupport.parkNanos(nanos);
|
||||
s.waiter = null;
|
||||
spins = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns validated tail for use in cleaning methods
|
||||
*/
|
||||
private QNode getValidatedTail() {
|
||||
for (;;) {
|
||||
QNode h = this.head.get();
|
||||
QNode first = h.next;
|
||||
if (first != null && first.next == first) { // help advance
|
||||
advanceHead(h, first);
|
||||
continue;
|
||||
}
|
||||
QNode t = this.tail.get();
|
||||
QNode last = t.next;
|
||||
if (t == this.tail.get()) {
|
||||
if (last != null) {
|
||||
this.tail.compareAndSet(t, last); // help advance
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rid of cancelled node s with original predecessor pred.
|
||||
*
|
||||
* @param pred predecessor of cancelled node
|
||||
* @param s the cancelled node
|
||||
*/
|
||||
void clean(QNode pred, QNode s) {
|
||||
Thread w = s.waiter;
|
||||
if (w != null) { // Wake up thread
|
||||
s.waiter = null;
|
||||
if (w != Thread.currentThread()) {
|
||||
LockSupport.unpark(w);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* At any given time, exactly one node on list cannot be deleted -- the last inserted node. To
|
||||
* accommodate this, if we cannot delete s, we save its predecessor as "cleanMe", processing the
|
||||
* previously saved version first. At least one of node s or the node previously saved can
|
||||
* always be processed, so this always terminates.
|
||||
*/
|
||||
while (pred.next == s) {
|
||||
QNode oldpred = reclean(); // First, help get rid of cleanMe
|
||||
QNode t = getValidatedTail();
|
||||
if (s != t) { // If not tail, try to unsplice
|
||||
QNode sn = s.next; // s.next == s means s already off list
|
||||
if (sn == s || pred.casNext(s, sn)) {
|
||||
break;
|
||||
}
|
||||
} else if (oldpred == pred || // Already saved
|
||||
oldpred == null && this.cleanMe.compareAndSet(null, pred)) {
|
||||
break; // Postpone cleaning
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to unsplice the cancelled node held in cleanMe that was previously uncleanable because it
|
||||
* was at tail.
|
||||
*
|
||||
* @return current cleanMe node (or null)
|
||||
*/
|
||||
private QNode reclean() {
|
||||
/*
|
||||
* cleanMe is, or at one time was, predecessor of cancelled node s that was the tail so could
|
||||
* not be unspliced. If s is no longer the tail, try to unsplice if necessary and make cleanMe
|
||||
* slot available. This differs from similar code in clean() because we must check that pred
|
||||
* still points to a cancelled node that must be unspliced -- if not, we can (must) clear
|
||||
* cleanMe without unsplicing. This can loop only due to contention on casNext or clearing
|
||||
* cleanMe.
|
||||
*/
|
||||
QNode pred;
|
||||
while ((pred = this.cleanMe.get()) != null) {
|
||||
QNode t = getValidatedTail();
|
||||
QNode s = pred.next;
|
||||
if (s != t) {
|
||||
QNode sn;
|
||||
if (s == null || s == pred || s.get() != s || (sn = s.next) == s || pred.casNext(s, sn)) {
|
||||
this.cleanMe.compareAndSet(pred, null);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pred;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
E cast(Object e) {
|
||||
return (E) e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an initially empty <code>LinkedTransferQueue</code>.
|
||||
*/
|
||||
public LinkedTransferQueue() {
|
||||
QNode dummy = new QNode(null, false);
|
||||
this.head = new PaddedAtomicReference<QNode>(dummy);
|
||||
this.tail = new PaddedAtomicReference<QNode>(dummy);
|
||||
this.cleanMe = new PaddedAtomicReference<QNode>(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>LinkedTransferQueue</code> initially containing the elements of the given
|
||||
* collection, added in traversal order of the collection's iterator.
|
||||
*
|
||||
* @param c the collection of elements to initially contain
|
||||
* @throws NullPointerException if the specified collection or any of its elements are null
|
||||
*/
|
||||
public LinkedTransferQueue(Collection<? extends E> c) {
|
||||
this();
|
||||
addAll(c);
|
||||
}
|
||||
|
||||
public void put(E e) throws InterruptedException {
|
||||
if (e == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
xfer(e, NOWAIT, 0);
|
||||
}
|
||||
|
||||
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
if (e == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
xfer(e, NOWAIT, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean offer(E e) {
|
||||
if (e == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
xfer(e, NOWAIT, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void transfer(E e) throws InterruptedException {
|
||||
if (e == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (xfer(e, WAIT, 0) == null) {
|
||||
Thread.interrupted();
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
if (e == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (xfer(e, TIMEOUT, unit.toNanos(timeout)) != null) {
|
||||
return true;
|
||||
}
|
||||
if (!Thread.interrupted()) {
|
||||
return false;
|
||||
}
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
public boolean tryTransfer(E e) {
|
||||
if (e == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return fulfill(e) != null;
|
||||
}
|
||||
|
||||
public E take() throws InterruptedException {
|
||||
Object e = xfer(null, WAIT, 0);
|
||||
if (e != null) {
|
||||
return cast(e);
|
||||
}
|
||||
Thread.interrupted();
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
Object e = xfer(null, TIMEOUT, unit.toNanos(timeout));
|
||||
if (e != null || !Thread.interrupted()) {
|
||||
return cast(e);
|
||||
}
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
public E poll() {
|
||||
return cast(fulfill(null));
|
||||
}
|
||||
|
||||
public int drainTo(Collection<? super E> c) {
|
||||
if (c == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (c == this) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int n = 0;
|
||||
E e;
|
||||
while ((e = poll()) != null) {
|
||||
c.add(e);
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public int drainTo(Collection<? super E> c, int maxElements) {
|
||||
if (c == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (c == this) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int n = 0;
|
||||
E e;
|
||||
while (n < maxElements && (e = poll()) != null) {
|
||||
c.add(e);
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Traversal-based methods
|
||||
|
||||
/**
|
||||
* Return head after performing any outstanding helping steps
|
||||
*/
|
||||
QNode traversalHead() {
|
||||
for (;;) {
|
||||
QNode t = this.tail.get();
|
||||
QNode h = this.head.get();
|
||||
if (h != null && t != null) {
|
||||
QNode last = t.next;
|
||||
QNode first = h.next;
|
||||
if (t == this.tail.get()) {
|
||||
if (last != null) {
|
||||
this.tail.compareAndSet(t, last);
|
||||
} else if (first != null) {
|
||||
Object x = first.get();
|
||||
if (x == first) {
|
||||
advanceHead(h, first);
|
||||
} else {
|
||||
return h;
|
||||
}
|
||||
} else {
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Itr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterators. Basic strategy is to traverse list, treating non-data (i.e., request) nodes as
|
||||
* terminating list. Once a valid data node is found, the item is cached so that the next call to
|
||||
* next() will return it even if subsequently removed.
|
||||
*/
|
||||
class Itr implements Iterator<E> {
|
||||
QNode nextNode; // Next node to return next
|
||||
QNode currentNode; // last returned node, for remove()
|
||||
QNode prevNode; // predecessor of last returned node
|
||||
E nextItem; // Cache of next item, once commited to in next
|
||||
|
||||
Itr() {
|
||||
this.nextNode = traversalHead();
|
||||
advance();
|
||||
}
|
||||
|
||||
E advance() {
|
||||
this.prevNode = this.currentNode;
|
||||
this.currentNode = this.nextNode;
|
||||
E x = this.nextItem;
|
||||
|
||||
QNode p = this.nextNode.next;
|
||||
for (;;) {
|
||||
if (p == null || !p.isData) {
|
||||
this.nextNode = null;
|
||||
this.nextItem = null;
|
||||
return x;
|
||||
}
|
||||
Object item = p.get();
|
||||
if (item != p && item != null) {
|
||||
this.nextNode = p;
|
||||
this.nextItem = cast(item);
|
||||
return x;
|
||||
}
|
||||
this.prevNode = p;
|
||||
p = p.next;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return this.nextNode != null;
|
||||
}
|
||||
|
||||
public E next() {
|
||||
if (this.nextNode == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return advance();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
QNode p = this.currentNode;
|
||||
QNode prev = this.prevNode;
|
||||
if (prev == null || p == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
Object x = p.get();
|
||||
if (x != null && x != p && p.compareAndSet(x, p)) {
|
||||
clean(prev, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public E peek() {
|
||||
for (;;) {
|
||||
QNode h = traversalHead();
|
||||
QNode p = h.next;
|
||||
if (p == null) {
|
||||
return null;
|
||||
}
|
||||
Object x = p.get();
|
||||
if (p != x) {
|
||||
if (!p.isData) {
|
||||
return null;
|
||||
}
|
||||
if (x != null) {
|
||||
return cast(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
for (;;) {
|
||||
QNode h = traversalHead();
|
||||
QNode p = h.next;
|
||||
if (p == null) {
|
||||
return true;
|
||||
}
|
||||
Object x = p.get();
|
||||
if (p != x) {
|
||||
if (!p.isData) {
|
||||
return true;
|
||||
}
|
||||
if (x != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasWaitingConsumer() {
|
||||
for (;;) {
|
||||
QNode h = traversalHead();
|
||||
QNode p = h.next;
|
||||
if (p == null) {
|
||||
return false;
|
||||
}
|
||||
Object x = p.get();
|
||||
if (p != x) {
|
||||
return !p.isData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of elements in this queue. If this queue contains more than
|
||||
* <code>Integer.MAX_VALUE</code> elements, returns <code>Integer.MAX_VALUE</code>.
|
||||
*
|
||||
* <p>
|
||||
* Beware that, unlike in most collections, this method is <em>NOT</em> a constant-time operation.
|
||||
* Because of the asynchronous nature of these queues, determining the current number of elements
|
||||
* requires an O(n) traversal.
|
||||
*
|
||||
* @return the number of elements in this queue
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
int count = 0;
|
||||
QNode h = traversalHead();
|
||||
for (QNode p = h.next; p != null && p.isData; p = p.next) {
|
||||
Object x = p.get();
|
||||
if (x != null && x != p) {
|
||||
if (++count == Integer.MAX_VALUE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getWaitingConsumerCount() {
|
||||
int count = 0;
|
||||
QNode h = traversalHead();
|
||||
for (QNode p = h.next; p != null && !p.isData; p = p.next) {
|
||||
if (p.get() == null) {
|
||||
if (++count == Integer.MAX_VALUE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public int remainingCapacity() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
60
src/main/java/com/google/code/yanf4j/util/MapBackedSet.java
Normal file
60
src/main/java/com/google/code/yanf4j/util/MapBackedSet.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A {@link Map}-backed {@link Set}.
|
||||
*
|
||||
* @author The Apache MINA Project (dev@mina.apache.org)
|
||||
* @version $Rev: 597692 $, $Date: 2007-11-23 08:56:32 -0700 (Fri, 23 Nov 2007) $
|
||||
*/
|
||||
public class MapBackedSet<E> extends AbstractSet<E> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8347878570391674042L;
|
||||
|
||||
protected final Map<E, Boolean> map;
|
||||
|
||||
public MapBackedSet(Map<E, Boolean> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public MapBackedSet(Map<E, Boolean> map, Collection<E> c) {
|
||||
this.map = map;
|
||||
addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return map.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E o) {
|
||||
return map.put(o, Boolean.TRUE) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return map.remove(o) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
}
|
52
src/main/java/com/google/code/yanf4j/util/PropertyUtils.java
Normal file
52
src/main/java/com/google/code/yanf4j/util/PropertyUtils.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* java.util.Property utils
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class PropertyUtils {
|
||||
|
||||
public static int getPropertyAsInteger(Properties props, String propName) {
|
||||
return Integer.parseInt(PropertyUtils.getProperty(props, propName));
|
||||
}
|
||||
|
||||
public static String getProperty(Properties props, String name) {
|
||||
return props.getProperty(name).trim();
|
||||
}
|
||||
|
||||
public static boolean getPropertyAsBoolean(Properties props, String name) {
|
||||
return Boolean.valueOf(getProperty(props, name));
|
||||
}
|
||||
|
||||
public static long getPropertyAsLong(Properties props, String name) {
|
||||
return Long.parseLong(getProperty(props, name));
|
||||
}
|
||||
|
||||
public static short getPropertyAsShort(Properties props, String name) {
|
||||
return Short.parseShort(getProperty(props, name));
|
||||
}
|
||||
|
||||
public static byte getPropertyAsByte(Properties props, String name) {
|
||||
return Byte.parseByte(getProperty(props, name));
|
||||
}
|
||||
}
|
208
src/main/java/com/google/code/yanf4j/util/ResourcesUtils.java
Normal file
208
src/main/java/com/google/code/yanf4j/util/ResourcesUtils.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Copyright [2008] [dennis zhuang] Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed
|
||||
* to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Resource utils
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class ResourcesUtils extends Object {
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns the URL of the resource on the classpath
|
||||
*
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static URL getResourceURL(String resource) throws IOException {
|
||||
URL url = null;
|
||||
ClassLoader loader = ResourcesUtils.class.getClassLoader();
|
||||
if (loader != null) {
|
||||
url = loader.getResource(resource);
|
||||
}
|
||||
if (url == null) {
|
||||
url = ClassLoader.getSystemResource(resource);
|
||||
}
|
||||
if (url == null) {
|
||||
throw new IOException("Could not find resource " + resource);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns the URL of the resource on the classpath
|
||||
*
|
||||
* @param loader The classloader used to load the resource
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static URL getResourceURL(ClassLoader loader, String resource) throws IOException {
|
||||
URL url = null;
|
||||
if (loader != null) {
|
||||
url = loader.getResource(resource);
|
||||
}
|
||||
if (url == null) {
|
||||
url = ClassLoader.getSystemResource(resource);
|
||||
}
|
||||
if (url == null) {
|
||||
throw new IOException("Could not find resource " + resource);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns a resource on the classpath as a Stream object
|
||||
*
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static InputStream getResourceAsStream(String resource) throws IOException {
|
||||
InputStream in = null;
|
||||
ClassLoader loader = ResourcesUtils.class.getClassLoader();
|
||||
if (loader != null) {
|
||||
in = loader.getResourceAsStream(resource);
|
||||
}
|
||||
if (in == null) {
|
||||
in = ClassLoader.getSystemResourceAsStream(resource);
|
||||
}
|
||||
if (in == null) {
|
||||
throw new IOException("Could not find resource " + resource);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns a resource on the classpath as a Stream object
|
||||
*
|
||||
* @param loader The classloader used to load the resource
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static InputStream getResourceAsStream(ClassLoader loader, String resource)
|
||||
throws IOException {
|
||||
InputStream in = null;
|
||||
if (loader != null) {
|
||||
in = loader.getResourceAsStream(resource);
|
||||
}
|
||||
if (in == null) {
|
||||
in = ClassLoader.getSystemResourceAsStream(resource);
|
||||
}
|
||||
if (in == null) {
|
||||
throw new IOException("Could not find resource " + resource);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns a resource on the classpath as a Properties object
|
||||
*
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static Properties getResourceAsProperties(String resource) throws IOException {
|
||||
Properties props = new Properties();
|
||||
InputStream in = null;
|
||||
String propfile = resource;
|
||||
in = getResourceAsStream(propfile);
|
||||
props.load(in);
|
||||
in.close();
|
||||
return props;
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns a resource on the classpath as a Properties object
|
||||
*
|
||||
* @param loader The classloader used to load the resource
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static Properties getResourceAsProperties(ClassLoader loader, String resource)
|
||||
throws IOException {
|
||||
Properties props = new Properties();
|
||||
InputStream in = null;
|
||||
String propfile = resource;
|
||||
in = getResourceAsStream(loader, propfile);
|
||||
props.load(in);
|
||||
in.close();
|
||||
return props;
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns a resource on the classpath as a Reader object
|
||||
*
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static InputStreamReader getResourceAsReader(String resource) throws IOException {
|
||||
return new InputStreamReader(getResourceAsStream(resource));
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns a resource on the classpath as a Reader object
|
||||
*
|
||||
* @param loader The classloader used to load the resource
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException {
|
||||
return new InputStreamReader(getResourceAsStream(loader, resource));
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns a resource on the classpath as a File object
|
||||
*
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static File getResourceAsFile(String resource) throws IOException {
|
||||
return new File(getResourceURL(resource).getFile());
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Returns a resource on the classpath as a File object
|
||||
*
|
||||
* @param loader The classloader used to load the resource
|
||||
* @param resource The resource to find
|
||||
* @throws IOException If the resource cannot be found or read
|
||||
* @return The resource
|
||||
*/
|
||||
public static File getResourceAsFile(ClassLoader loader, String resource) throws IOException {
|
||||
return new File(getResourceURL(loader, resource).getFile());
|
||||
}
|
||||
|
||||
}
|
158
src/main/java/com/google/code/yanf4j/util/SelectorFactory.java
Normal file
158
src/main/java/com/google/code/yanf4j/util/SelectorFactory.java
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* The contents of this file are subject to the terms of the Common Development and Distribution
|
||||
* License (the License). You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at https://glassfish.dev.java.net/public/CDDLv1.0.html or
|
||||
* glassfish/bootstrap/legal/CDDLv1.0.txt. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL Header Notice in each file and include the
|
||||
* License file at glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable, add the following below
|
||||
* the CDDL Header, with the fields enclosed by brackets [] replaced by you own identifying
|
||||
* information: "Portions Copyrighted [year] [name of copyright owner]"
|
||||
*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.Selector;
|
||||
import java.util.EmptyStackException;
|
||||
import java.util.Stack;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Temp selector factory,come from grizzly
|
||||
*
|
||||
* @author dennis zhuang
|
||||
*/
|
||||
public class SelectorFactory {
|
||||
|
||||
public static final int DEFAULT_MAX_SELECTORS = 20;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SelectorFactory.class);
|
||||
/**
|
||||
* The timeout before we exit.
|
||||
*/
|
||||
public final static long timeout = 5000;
|
||||
|
||||
/**
|
||||
* The number of <code>Selector</code> to create.
|
||||
*/
|
||||
private static int maxSelectors;
|
||||
|
||||
/**
|
||||
* Cache of <code>Selector</code>
|
||||
*/
|
||||
private final static Stack<Selector> selectors = new Stack<Selector>();
|
||||
|
||||
/**
|
||||
* Creates the <code>Selector</code>
|
||||
*/
|
||||
static {
|
||||
try {
|
||||
setMaxSelectors(DEFAULT_MAX_SELECTORS);
|
||||
} catch (IOException ex) {
|
||||
logger.warn("SelectorFactory initializing Selector pool", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set max selector pool size.
|
||||
*
|
||||
* @param size max pool size
|
||||
*/
|
||||
public final static void setMaxSelectors(int size) throws IOException {
|
||||
synchronized (selectors) {
|
||||
if (size < maxSelectors) {
|
||||
reduce(size);
|
||||
} else if (size > maxSelectors) {
|
||||
grow(size);
|
||||
}
|
||||
|
||||
maxSelectors = size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns max selector pool size
|
||||
*
|
||||
* @return max pool size
|
||||
*/
|
||||
public final static int getMaxSelectors() {
|
||||
return maxSelectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a exclusive <code>Selector</code>
|
||||
*
|
||||
* @return <code>Selector</code>
|
||||
*/
|
||||
public final static Selector getSelector() {
|
||||
synchronized (selectors) {
|
||||
Selector s = null;
|
||||
try {
|
||||
if (selectors.size() != 0) {
|
||||
s = selectors.pop();
|
||||
}
|
||||
} catch (EmptyStackException ex) {
|
||||
}
|
||||
|
||||
int attempts = 0;
|
||||
try {
|
||||
while (s == null && attempts < 2) {
|
||||
selectors.wait(timeout);
|
||||
try {
|
||||
if (selectors.size() != 0) {
|
||||
s = selectors.pop();
|
||||
}
|
||||
} catch (EmptyStackException ex) {
|
||||
break;
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <code>Selector</code> to the cache
|
||||
*
|
||||
* @param s <code>Selector</code>
|
||||
*/
|
||||
public final static void returnSelector(Selector s) {
|
||||
synchronized (selectors) {
|
||||
selectors.push(s);
|
||||
if (selectors.size() == 1) {
|
||||
selectors.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase <code>Selector</code> pool size
|
||||
*/
|
||||
private static void grow(int size) throws IOException {
|
||||
for (int i = 0; i < size - maxSelectors; i++) {
|
||||
selectors.add(Selector.open());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease <code>Selector</code> pool size
|
||||
*/
|
||||
private static void reduce(int size) {
|
||||
for (int i = 0; i < maxSelectors - size; i++) {
|
||||
try {
|
||||
Selector selector = selectors.pop();
|
||||
selector.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("SelectorFactory.reduce", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
|
||||
/**
|
||||
* ByteBuffer matcher based on shift-and algorithm
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class ShiftAndByteBufferMatcher implements ByteBufferMatcher {
|
||||
|
||||
private int[] b;
|
||||
private int mask;
|
||||
|
||||
private int patternLimit;
|
||||
private int patternPos;
|
||||
private int patternLen;
|
||||
|
||||
public ShiftAndByteBufferMatcher(IoBuffer pat) {
|
||||
if (pat == null || pat.remaining() == 0) {
|
||||
throw new IllegalArgumentException("blank buffer");
|
||||
}
|
||||
this.patternLimit = pat.limit();
|
||||
this.patternPos = pat.position();
|
||||
this.patternLen = pat.remaining();
|
||||
preprocess(pat);
|
||||
this.mask = 1 << (this.patternLen - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ԥ<><D4A4><EFBFBD><EFBFBD>
|
||||
*
|
||||
* @param pat
|
||||
*/
|
||||
private void preprocess(IoBuffer pat) {
|
||||
this.b = new int[256];
|
||||
for (int i = this.patternPos; i < this.patternLimit; i++) {
|
||||
int p = ByteBufferUtils.uByte(pat.get(i));
|
||||
this.b[p] = this.b[p] | (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
public final List<Integer> matchAll(IoBuffer buffer) {
|
||||
List<Integer> matches = new ArrayList<Integer>();
|
||||
int bufferLimit = buffer.limit();
|
||||
int d = 0;
|
||||
for (int pos = buffer.position(); pos < bufferLimit; pos++) {
|
||||
d <<= 1;
|
||||
d |= 1;
|
||||
d &= this.b[ByteBufferUtils.uByte(buffer.get(pos))];
|
||||
if ((d & this.mask) != 0) {
|
||||
matches.add(pos - this.patternLen + 1);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
public final int matchFirst(IoBuffer buffer) {
|
||||
if (buffer == null) {
|
||||
return -1;
|
||||
}
|
||||
int bufferLimit = buffer.limit();
|
||||
int d = 0;
|
||||
for (int pos = buffer.position(); pos < bufferLimit; pos++) {
|
||||
d <<= 1;
|
||||
d |= 1;
|
||||
d &= this.b[buffer.get(pos) & 0XFF];
|
||||
if ((d & this.mask) != 0) {
|
||||
return pos - this.patternLen + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.google.code.yanf4j.buffer.IoBuffer;
|
||||
|
||||
/**
|
||||
* ByteBuffer matcher based on shift-or algorithm
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class ShiftOrByteBufferMatcher implements ByteBufferMatcher {
|
||||
|
||||
private int[] b;
|
||||
private int lim;
|
||||
|
||||
private int patternLen;
|
||||
|
||||
public ShiftOrByteBufferMatcher(IoBuffer pat) {
|
||||
if (pat == null || pat.remaining() == 0) {
|
||||
throw new IllegalArgumentException("blank buffer");
|
||||
}
|
||||
this.patternLen = pat.remaining();
|
||||
preprocess(pat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ԥ<><D4A4><EFBFBD><EFBFBD>
|
||||
*
|
||||
* @param pat
|
||||
*/
|
||||
private void preprocess(IoBuffer pat) {
|
||||
this.b = new int[256];
|
||||
this.lim = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
this.b[i] = ~0;
|
||||
|
||||
}
|
||||
for (int i = 0, j = 1; i < this.patternLen; i++, j <<= 1) {
|
||||
this.b[ByteBufferUtils.uByte(pat.get(i))] &= ~j;
|
||||
this.lim |= j;
|
||||
}
|
||||
this.lim = ~(this.lim >> 1);
|
||||
|
||||
}
|
||||
|
||||
public final List<Integer> matchAll(IoBuffer buffer) {
|
||||
List<Integer> matches = new ArrayList<Integer>();
|
||||
int bufferLimit = buffer.limit();
|
||||
int state = ~0;
|
||||
for (int pos = buffer.position(); pos < bufferLimit; pos++) {
|
||||
state <<= 1;
|
||||
state |= this.b[ByteBufferUtils.uByte(buffer.get(pos))];
|
||||
if (state < this.lim) {
|
||||
matches.add(pos - this.patternLen + 1);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
public final int matchFirst(IoBuffer buffer) {
|
||||
if (buffer == null) {
|
||||
return -1;
|
||||
}
|
||||
int bufferLimit = buffer.limit();
|
||||
int state = ~0;
|
||||
for (int pos = buffer.position(); pos < bufferLimit; pos++) {
|
||||
state = (state <<= 1) | this.b[ByteBufferUtils.uByte(buffer.get(pos))];
|
||||
if (state < this.lim) {
|
||||
return pos - this.patternLen + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
45
src/main/java/com/google/code/yanf4j/util/SimpleQueue.java
Normal file
45
src/main/java/com/google/code/yanf4j/util/SimpleQueue.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Simple queue. All methods are thread-safe.
|
||||
*
|
||||
* @author dennis zhuang
|
||||
*/
|
||||
public class SimpleQueue<T> extends java.util.AbstractQueue<T> {
|
||||
|
||||
protected final LinkedList<T> list;
|
||||
|
||||
public SimpleQueue(int initializeCapacity) {
|
||||
this.list = new LinkedList<T>();
|
||||
}
|
||||
|
||||
public SimpleQueue() {
|
||||
this(100);
|
||||
}
|
||||
|
||||
public synchronized boolean offer(T e) {
|
||||
return this.list.add(e);
|
||||
}
|
||||
|
||||
public synchronized T peek() {
|
||||
return this.list.peek();
|
||||
}
|
||||
|
||||
public synchronized T poll() {
|
||||
return this.list.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return this.list.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.list.size();
|
||||
}
|
||||
|
||||
}
|
149
src/main/java/com/google/code/yanf4j/util/SystemUtils.java
Normal file
149
src/main/java/com/google/code/yanf4j/util/SystemUtils.java
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* System utils
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public final class SystemUtils {
|
||||
|
||||
private SystemUtils() {
|
||||
|
||||
}
|
||||
|
||||
public static final String OS_NAME = System.getProperty("os.name");
|
||||
|
||||
private static boolean isLinuxPlatform = false;
|
||||
|
||||
static {
|
||||
if (OS_NAME != null && OS_NAME.toLowerCase().indexOf("linux") >= 0) {
|
||||
isLinuxPlatform = true;
|
||||
}
|
||||
}
|
||||
public static final String JAVA_VERSION = System.getProperty("java.version");
|
||||
private static boolean isAfterJava6u4Version = false;
|
||||
static {
|
||||
if (JAVA_VERSION != null) {
|
||||
// java4 or java5
|
||||
if (JAVA_VERSION.indexOf("1.4.") >= 0 || JAVA_VERSION.indexOf("1.5.") >= 0) {
|
||||
isAfterJava6u4Version = false;
|
||||
} else if (JAVA_VERSION.indexOf("1.6.") >= 0) {
|
||||
int index = JAVA_VERSION.indexOf("_");
|
||||
if (index > 0) {
|
||||
String subVersionStr = JAVA_VERSION.substring(index + 1);
|
||||
if (subVersionStr != null && subVersionStr.length() > 0) {
|
||||
try {
|
||||
int subVersion = Integer.parseInt(subVersionStr);
|
||||
if (subVersion >= 4) {
|
||||
isAfterJava6u4Version = true;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// after java6
|
||||
} else {
|
||||
isAfterJava6u4Version = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final boolean isLinuxPlatform() {
|
||||
return isLinuxPlatform;
|
||||
}
|
||||
|
||||
public static final boolean isAfterJava6u4Version() {
|
||||
return isAfterJava6u4Version;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(isAfterJava6u4Version());
|
||||
}
|
||||
|
||||
public static final int getSystemThreadCount() {
|
||||
int cpus = getCpuProcessorCount();
|
||||
if (cpus <= 8) {
|
||||
return cpus;
|
||||
} else {
|
||||
return 8 + (cpus - 8) * 5 / 8;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int getCpuProcessorCount() {
|
||||
return Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
public static final Selector openSelector() throws IOException {
|
||||
Selector result = null;
|
||||
// check if it is linux os
|
||||
if (SystemUtils.isLinuxPlatform()) {
|
||||
try {
|
||||
Class<?> providerClazz = Class.forName("sun.nio.ch.EPollSelectorProvider");
|
||||
if (providerClazz != null) {
|
||||
try {
|
||||
Method method = providerClazz.getMethod("provider");
|
||||
if (method != null) {
|
||||
SelectorProvider selectorProvider = (SelectorProvider) method.invoke(null);
|
||||
if (selectorProvider != null) {
|
||||
result = selectorProvider.openSelector();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = Selector.open();
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public static final String getRawAddress(InetSocketAddress inetSocketAddress) {
|
||||
InetAddress address = inetSocketAddress.getAddress();
|
||||
return address != null ? address.getHostAddress() : inetSocketAddress.getHostName();
|
||||
}
|
||||
|
||||
public static final Queue<?> createTransferQueue() {
|
||||
try {
|
||||
return (Queue<?>) Class.forName("java.util.concurrent.LinkedTransferQueue").newInstance();
|
||||
} catch (Exception e) {
|
||||
return new LinkedTransferQueue<Object>();
|
||||
}
|
||||
}
|
||||
|
||||
public static Random createRandom() {
|
||||
return new Random();
|
||||
}
|
||||
}
|
101
src/main/java/com/google/code/yanf4j/util/TransferQueue.java
Normal file
101
src/main/java/com/google/code/yanf4j/util/TransferQueue.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the
|
||||
* public domain, as explained at http://creativecommons.org/licenses/publicdomain
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* A {@link BlockingQueue} in which producers may wait for consumers to receive elements. A
|
||||
* {@code TransferQueue} may be useful for example in message passing applications in which
|
||||
* producers sometimes (using method {@code transfer}) await receipt of elements by consumers
|
||||
* invoking {@code take} or {@code poll}, while at other times enqueue elements (via method
|
||||
* {@code put}) without waiting for receipt. Non-blocking and time-out versions of
|
||||
* {@code tryTransfer} are also available. A TransferQueue may also be queried via
|
||||
* {@code hasWaitingConsumer} whether there are any threads waiting for items, which is a converse
|
||||
* analogy to a {@code peek} operation.
|
||||
*
|
||||
* <p>
|
||||
* Like any {@code BlockingQueue}, a {@code TransferQueue} may be capacity bounded. If so, an
|
||||
* attempted {@code transfer} operation may initially block waiting for available space, and/or
|
||||
* subsequently block waiting for reception by a consumer. Note that in a queue with zero capacity,
|
||||
* such as {@link SynchronousQueue}, {@code put} and {@code transfer} are effectively synonymous.
|
||||
*
|
||||
* <p>
|
||||
* This interface is a member of the
|
||||
* <a href="{@docRoot} /../technotes/guides/collections/index.html"> Java Collections Framework</a>.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Doug Lea
|
||||
* @param <E> the type of elements held in this collection
|
||||
*/
|
||||
public interface TransferQueue<E> extends BlockingQueue<E> {
|
||||
/**
|
||||
* Transfers the specified element if there exists a consumer already waiting to receive it,
|
||||
* otherwise returning {@code false} without enqueuing the element.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @return {@code true} if the element was transferred, else {@code false}
|
||||
* @throws ClassCastException if the class of the specified element prevents it from being added
|
||||
* to this queue
|
||||
* @throws NullPointerException if the specified element is null
|
||||
* @throws IllegalArgumentException if some property of the specified element prevents it from
|
||||
* being added to this queue
|
||||
*/
|
||||
boolean tryTransfer(E e);
|
||||
|
||||
/**
|
||||
* Inserts the specified element into this queue, waiting if necessary for space to become
|
||||
* available and the element to be dequeued by a consumer invoking {@code take} or {@code poll}.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @throws InterruptedException if interrupted while waiting, in which case the element is not
|
||||
* enqueued.
|
||||
* @throws ClassCastException if the class of the specified element prevents it from being added
|
||||
* to this queue
|
||||
* @throws NullPointerException if the specified element is null
|
||||
* @throws IllegalArgumentException if some property of the specified element prevents it from
|
||||
* being added to this queue
|
||||
*/
|
||||
void transfer(E e) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Inserts the specified element into this queue, waiting up to the specified wait time if
|
||||
* necessary for space to become available and the element to be dequeued by a consumer invoking
|
||||
* {@code take} or {@code poll}.
|
||||
*
|
||||
* @param e the element to transfer
|
||||
* @param timeout how long to wait before giving up, in units of {@code unit}
|
||||
* @param unit a {@code TimeUnit} determining how to interpret the {@code timeout} parameter
|
||||
* @return {@code true} if successful, or {@code false} if the specified waiting time elapses
|
||||
* before completion, in which case the element is not enqueued.
|
||||
* @throws InterruptedException if interrupted while waiting, in which case the element is not
|
||||
* enqueued.
|
||||
* @throws ClassCastException if the class of the specified element prevents it from being added
|
||||
* to this queue
|
||||
* @throws NullPointerException if the specified element is null
|
||||
* @throws IllegalArgumentException if some property of the specified element prevents it from
|
||||
* being added to this queue
|
||||
*/
|
||||
boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Returns {@code true} if there is at least one consumer waiting to dequeue an element via
|
||||
* {@code take} or {@code poll}. The return value represents a momentary state of affairs.
|
||||
*
|
||||
* @return {@code true} if there is at least one waiting consumer
|
||||
*/
|
||||
boolean hasWaitingConsumer();
|
||||
|
||||
/**
|
||||
* Returns an estimate of the number of consumers waiting to dequeue elements via {@code take} or
|
||||
* {@code poll}. The return value is an approximation of a momentary state of affairs, that may be
|
||||
* inaccurate if consumers have completed or given up waiting. The value may be useful for
|
||||
* monitoring and heuristics, but not for synchronization control. Implementations of this method
|
||||
* are likely to be noticeably slower than those for {@link #hasWaitingConsumer}.
|
||||
*
|
||||
* @return the number of consumers waiting to dequeue elements
|
||||
*/
|
||||
int getWaitingConsumerCount();
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package com.google.code.yanf4j.util;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Thread factory for worker thread
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class WorkerThreadFactory implements ThreadFactory {
|
||||
private static final AtomicInteger poolNumber = new AtomicInteger(1);
|
||||
private final ThreadGroup group;
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
private final String namePrefix;
|
||||
|
||||
public WorkerThreadFactory(ThreadGroup group, String prefix) {
|
||||
if (group == null) {
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
|
||||
} else {
|
||||
this.group = group;
|
||||
}
|
||||
if (prefix == null) {
|
||||
this.namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
|
||||
} else {
|
||||
this.namePrefix = prefix + "-" + poolNumber.getAndIncrement() + "-thread-";
|
||||
}
|
||||
}
|
||||
|
||||
public WorkerThreadFactory(String prefix) {
|
||||
this(null, prefix);
|
||||
}
|
||||
|
||||
public WorkerThreadFactory() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(this.group, r, this.namePrefix + this.threadNumber.getAndIncrement(), 0);
|
||||
t.setDaemon(true);
|
||||
if (t.getPriority() != Thread.NORM_PRIORITY) {
|
||||
t.setPriority(Thread.NORM_PRIORITY);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
}
|
10
src/main/java/com/google/code/yanf4j/util/package.html
Normal file
10
src/main/java/com/google/code/yanf4j/util/package.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!--?xml version="1.0" encoding="UTF-8"?--><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!-- Copyright (c) 2007 Dustin Sallings <dustin+html@spy.net> -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Yanf4j utilities</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Yanf4j utilities</h1>
|
||||
</body>
|
||||
</html>
|
11
src/main/java/module-info.java
Normal file
11
src/main/java/module-info.java
Normal file
@@ -0,0 +1,11 @@
|
||||
module com.googlecode.xmemcached {
|
||||
requires org.slf4j;
|
||||
requires static java.security.sasl;
|
||||
requires static java.management;
|
||||
requires static java.rmi;
|
||||
requires static spring.beans;
|
||||
requires static spring.context;
|
||||
|
||||
exports net.rubyeye.xmemcached;
|
||||
exports net.rubyeye.xmemcached.exception;
|
||||
}
|
33
src/main/java/net/rubyeye/xmemcached/CASOperation.java
Normal file
33
src/main/java/net/rubyeye/xmemcached/CASOperation.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
/**
|
||||
* CAS operation,encapsulate gets and cas commands,and supports retry times.
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface CASOperation<T> {
|
||||
/**
|
||||
* Max retry times,If retry times is great than this value,xmemcached will throw TimeoutException
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getMaxTries();
|
||||
|
||||
/**
|
||||
* Return the new value which you want to cas
|
||||
*
|
||||
* @param currentCAS
|
||||
* @param currentValue
|
||||
* @return expected new value
|
||||
*/
|
||||
public T getNewValue(long currentCAS, T currentValue);
|
||||
}
|
294
src/main/java/net/rubyeye/xmemcached/CommandFactory.java
Normal file
294
src/main/java/net/rubyeye/xmemcached/CommandFactory.java
Normal file
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import net.rubyeye.xmemcached.buffer.BufferAllocator;
|
||||
import net.rubyeye.xmemcached.command.Command;
|
||||
import net.rubyeye.xmemcached.command.CommandType;
|
||||
import net.rubyeye.xmemcached.transcoders.Transcoder;
|
||||
import net.rubyeye.xmemcached.utils.Protocol;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface CommandFactory {
|
||||
|
||||
/**
|
||||
* set command factory's buffer allocator
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @param bufferAllocator
|
||||
*/
|
||||
public void setBufferAllocator(BufferAllocator bufferAllocator);
|
||||
|
||||
/**
|
||||
* create a delete command
|
||||
*
|
||||
* @param key
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public Command createDeleteCommand(final String key, final byte[] keyBytes, final int time,
|
||||
long cas, boolean noreply);
|
||||
|
||||
/**
|
||||
* create a version command
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Command createVersionCommand(CountDownLatch latch, InetSocketAddress server);
|
||||
|
||||
/**
|
||||
* create a flush_all command
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Command createFlushAllCommand(CountDownLatch latch, int delay, boolean noreply);
|
||||
|
||||
/**
|
||||
* create a stats command
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Command createStatsCommand(InetSocketAddress server, CountDownLatch latch,
|
||||
String itemName);
|
||||
|
||||
/**
|
||||
* create a get/gets command
|
||||
*
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param cmdType 命令类型
|
||||
* @param transcoder TODO
|
||||
* @param cmdBytes 命令的字节数组,如"get".getBytes()
|
||||
* @return
|
||||
*/
|
||||
|
||||
public Command createGetCommand(final String key, final byte[] keyBytes,
|
||||
final CommandType cmdType, Transcoder transcoder);
|
||||
|
||||
/**
|
||||
* Create a multi-get command
|
||||
*
|
||||
* @param <T>
|
||||
* @param keys
|
||||
* @param latch
|
||||
* @param result
|
||||
* @param cmdBytes
|
||||
* @param cmdType
|
||||
* @param transcoder
|
||||
* @return
|
||||
*/
|
||||
public <T> Command createGetMultiCommand(Collection<String> keys, CountDownLatch latch,
|
||||
CommandType cmdType, Transcoder<T> transcoder);
|
||||
|
||||
/**
|
||||
* create a incr/decr command
|
||||
*
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param delta
|
||||
* @param initial
|
||||
* @param expTime
|
||||
* @param cmdType
|
||||
* @param noreply
|
||||
* @return
|
||||
*/
|
||||
public Command createIncrDecrCommand(final String key, final byte[] keyBytes, final long delta,
|
||||
long initial, int expTime, CommandType cmdType, boolean noreply);
|
||||
|
||||
/**
|
||||
* Create a cas command
|
||||
*
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param exp
|
||||
* @param value
|
||||
* @param cas
|
||||
* @param noreply
|
||||
* @param transcoder
|
||||
* @return
|
||||
*/
|
||||
public Command createCASCommand(final String key, final byte[] keyBytes, final int exp,
|
||||
final Object value, long cas, boolean noreply, Transcoder transcoder);
|
||||
|
||||
/**
|
||||
* Create a set command
|
||||
*
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param exp
|
||||
* @param value
|
||||
* @param noreply
|
||||
* @param transcoder
|
||||
* @return
|
||||
*/
|
||||
public Command createSetCommand(final String key, final byte[] keyBytes, final int exp,
|
||||
final Object value, boolean noreply, Transcoder transcoder);
|
||||
|
||||
/**
|
||||
* create a add command
|
||||
*
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param exp
|
||||
* @param value
|
||||
* @param noreply
|
||||
* @param transcoder
|
||||
* @return
|
||||
*/
|
||||
public Command createAddCommand(final String key, final byte[] keyBytes, final int exp,
|
||||
final Object value, boolean noreply, Transcoder transcoder);
|
||||
|
||||
/**
|
||||
* create a replace command
|
||||
*
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param exp
|
||||
* @param value
|
||||
* @param noreply
|
||||
* @param transcoder
|
||||
* @return
|
||||
*/
|
||||
public Command createReplaceCommand(final String key, final byte[] keyBytes, final int exp,
|
||||
final Object value, boolean noreply, Transcoder transcoder);
|
||||
|
||||
/**
|
||||
* create a append command
|
||||
*
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param value
|
||||
* @param noreply
|
||||
* @param transcoder
|
||||
* @return
|
||||
*/
|
||||
public Command createAppendCommand(final String key, final byte[] keyBytes, final Object value,
|
||||
boolean noreply, Transcoder transcoder);
|
||||
|
||||
/**
|
||||
* Create a prepend command
|
||||
*
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param value
|
||||
* @param noreply
|
||||
* @param transcoder
|
||||
* @return
|
||||
*/
|
||||
public Command createPrependCommand(final String key, final byte[] keyBytes, final Object value,
|
||||
boolean noreply, Transcoder transcoder);
|
||||
|
||||
/**
|
||||
* Create a verbosity command
|
||||
*
|
||||
* @param latch
|
||||
* @param level
|
||||
* @param noreply
|
||||
* @return
|
||||
*/
|
||||
public Command createVerbosityCommand(CountDownLatch latch, int level, boolean noreply);
|
||||
|
||||
/**
|
||||
* Create a command for listing authentication mechanisms
|
||||
*
|
||||
* @param latch
|
||||
* @return
|
||||
*/
|
||||
public Command createAuthListMechanismsCommand(CountDownLatch latch);
|
||||
|
||||
/**
|
||||
* Create command for starting authentication
|
||||
*
|
||||
* @param mechanism
|
||||
* @param latch
|
||||
* @param authData
|
||||
* @return
|
||||
*/
|
||||
public Command createAuthStartCommand(String mechanism, CountDownLatch latch, byte[] authData);
|
||||
|
||||
/**
|
||||
* Create a command for stepping authentication
|
||||
*
|
||||
* @param mechanism
|
||||
* @param latch
|
||||
* @param authData
|
||||
* @return
|
||||
*/
|
||||
public Command createAuthStepCommand(String mechanism, CountDownLatch latch, byte[] authData);
|
||||
|
||||
/**
|
||||
* create a quit command
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Command createQuitCommand();
|
||||
|
||||
/**
|
||||
* Create a touch command
|
||||
*
|
||||
* @since 1.3.3
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param latch TODO
|
||||
* @param exp
|
||||
* @param noreply
|
||||
* @return
|
||||
*/
|
||||
public Command createTouchCommand(final String key, final byte[] keyBytes, CountDownLatch latch,
|
||||
int exp, boolean noreply);
|
||||
|
||||
/**
|
||||
* Create a get-and-touch command
|
||||
*
|
||||
* @since 1.3.3
|
||||
* @param key
|
||||
* @param keyBytes
|
||||
* @param latch TODO
|
||||
* @param exp
|
||||
* @param noreply
|
||||
* @return
|
||||
*/
|
||||
public Command createGetAndTouchCommand(final String key, final byte[] keyBytes,
|
||||
CountDownLatch latch, int exp, boolean noreply);
|
||||
|
||||
/**
|
||||
* Create a Auto discovery config command, only supports Cache Engine Version 1.4.14 or Higher.
|
||||
* This method works the same for both AWS and GCP Auto Discovery.
|
||||
*
|
||||
* @see <a href=
|
||||
* "http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/AutoDiscovery.AddingToYourClientLibrary.html">Adding
|
||||
* AWS Auto Discovery To Your Client Library</a>
|
||||
* @see <a href=
|
||||
* "https://cloud.google.com/memorystore/docs/memcached/use-auto-discovery">Adding
|
||||
* GCP Auto Discovery To Your Client Library</a>
|
||||
* @param subCommand
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Command createAutoDiscoveryCacheConfigCommand(String subCommand, String key);
|
||||
|
||||
/**
|
||||
* Get this client's protocol version
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Protocol getProtocol();
|
||||
|
||||
}
|
149
src/main/java/net/rubyeye/xmemcached/Counter.java
Normal file
149
src/main/java/net/rubyeye/xmemcached/Counter.java
Normal file
@@ -0,0 +1,149 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import net.rubyeye.xmemcached.exception.MemcachedClientException;
|
||||
import net.rubyeye.xmemcached.exception.MemcachedException;
|
||||
|
||||
/**
|
||||
* Counter,encapsulate the incr/decr methods.
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public final class Counter {
|
||||
private final MemcachedClient memcachedClient;
|
||||
private final String key;
|
||||
private final long initialValue;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((this.key == null) ? 0 : this.key.hashCode());
|
||||
result =
|
||||
prime * result + ((this.memcachedClient == null) ? 0 : this.memcachedClient.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Counter other = (Counter) obj;
|
||||
if (this.key == null) {
|
||||
if (other.key != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!this.key.equals(other.key)) {
|
||||
return false;
|
||||
}
|
||||
if (this.memcachedClient == null) {
|
||||
if (other.memcachedClient != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!this.memcachedClient.equals(other.memcachedClient)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public final String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current value
|
||||
*
|
||||
* @return
|
||||
* @throws MemcachedException
|
||||
* @throws InterruptedException
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
public long get() throws MemcachedException, InterruptedException, TimeoutException {
|
||||
Object result = this.memcachedClient.get(this.key);
|
||||
if (result == null) {
|
||||
throw new MemcachedClientException("key is not existed.");
|
||||
} else {
|
||||
if (result instanceof Long)
|
||||
return (Long) result;
|
||||
else
|
||||
return Long.valueOf(((String) result).trim());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set counter's value to expected.
|
||||
*
|
||||
* @param value
|
||||
* @throws MemcachedException
|
||||
* @throws InterruptedException
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
public void set(long value) throws MemcachedException, InterruptedException, TimeoutException {
|
||||
this.memcachedClient.set(this.key, 0, String.valueOf(value));
|
||||
}
|
||||
|
||||
public Counter(MemcachedClient memcachedClient, String key, long initialValue) {
|
||||
super();
|
||||
this.memcachedClient = memcachedClient;
|
||||
this.key = key;
|
||||
this.initialValue = initialValue;
|
||||
try {
|
||||
this.memcachedClient.add(key, 0, String.valueOf(this.initialValue));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Initialize counter failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase value by one
|
||||
*
|
||||
* @return
|
||||
* @throws MemcachedException
|
||||
* @throws InterruptedException
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
public long incrementAndGet() throws MemcachedException, InterruptedException, TimeoutException {
|
||||
return this.memcachedClient.incr(this.key, 1, this.initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease value by one
|
||||
*
|
||||
* @return
|
||||
* @throws MemcachedException
|
||||
* @throws InterruptedException
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
public long decrementAndGet() throws MemcachedException, InterruptedException, TimeoutException {
|
||||
return this.memcachedClient.decr(this.key, 1, this.initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add value and get the result
|
||||
*
|
||||
* @param delta
|
||||
* @return
|
||||
* @throws MemcachedException
|
||||
* @throws InterruptedException
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
public long addAndGet(long delta)
|
||||
throws MemcachedException, InterruptedException, TimeoutException {
|
||||
if (delta >= 0) {
|
||||
return this.memcachedClient.incr(this.key, delta, this.initialValue);
|
||||
} else {
|
||||
return this.memcachedClient.decr(this.key, -delta, this.initialValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
37
src/main/java/net/rubyeye/xmemcached/FlowControl.java
Normal file
37
src/main/java/net/rubyeye/xmemcached/FlowControl.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
/**
|
||||
* Flow control for noreply operations.
|
||||
*
|
||||
* @author dennis<killme2008@gmail.com>
|
||||
* @since 1.3.8
|
||||
*
|
||||
*/
|
||||
public class FlowControl {
|
||||
private Semaphore permits;
|
||||
private int max;
|
||||
|
||||
public FlowControl(int permits) {
|
||||
super();
|
||||
this.max = permits;
|
||||
this.permits = new Semaphore(permits);
|
||||
}
|
||||
|
||||
public int max() {
|
||||
return this.max;
|
||||
}
|
||||
|
||||
public int permits() {
|
||||
return this.permits.availablePermits();
|
||||
}
|
||||
|
||||
public boolean aquire() {
|
||||
return this.permits.tryAcquire();
|
||||
}
|
||||
|
||||
public void release() {
|
||||
this.permits.release();
|
||||
}
|
||||
}
|
75
src/main/java/net/rubyeye/xmemcached/GetsResponse.java
Normal file
75
src/main/java/net/rubyeye/xmemcached/GetsResponse.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
/**
|
||||
* Response for gets command.It's a value object.
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public final class GetsResponse<T> {
|
||||
private final long cas;
|
||||
private final T value;
|
||||
|
||||
public GetsResponse(final long cas, final T value) {
|
||||
super();
|
||||
this.cas = cas;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public long getCas() {
|
||||
return this.cas;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (int) (this.cas ^ (this.cas >>> 32));
|
||||
result = prime * result + ((this.value == null) ? 0 : this.value.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
GetsResponse other = (GetsResponse) obj;
|
||||
if (this.cas != other.cas) {
|
||||
return false;
|
||||
}
|
||||
if (this.value == null) {
|
||||
if (other.value != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!this.value.equals(other.value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GetsResponse[cas=" + this.cas + ",value=" + this.value + "]";
|
||||
}
|
||||
|
||||
}
|
223
src/main/java/net/rubyeye/xmemcached/HashAlgorithm.java
Normal file
223
src/main/java/net/rubyeye/xmemcached/HashAlgorithm.java
Normal file
@@ -0,0 +1,223 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.zip.CRC32;
|
||||
import net.rubyeye.xmemcached.exception.MemcachedClientException;
|
||||
import net.rubyeye.xmemcached.utils.ByteUtils;
|
||||
|
||||
/**
|
||||
* Known hashing algorithms for locating a server for a key. Note that all hash algorithms return
|
||||
* 64-bits of hash, but only the lower 32-bits are significant. This allows a positive 32-bit number
|
||||
* to be returned for all cases.
|
||||
*/
|
||||
public enum HashAlgorithm {
|
||||
|
||||
/**
|
||||
* Native hash (String.hashCode()).
|
||||
*/
|
||||
NATIVE_HASH,
|
||||
/**
|
||||
* CRC32_HASH as used by the perl API. This will be more consistent both across multiple API users
|
||||
* as well as java versions, but is mostly likely significantly slower.
|
||||
*/
|
||||
CRC32_HASH,
|
||||
/**
|
||||
* FNV hashes are designed to be fast while maintaining a low collision rate. The FNV speed allows
|
||||
* one to quickly hash lots of data while maintaining a reasonable collision rate.
|
||||
*
|
||||
* @see http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
* @see http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash
|
||||
*/
|
||||
FNV1_64_HASH,
|
||||
/**
|
||||
* Variation of FNV.
|
||||
*/
|
||||
FNV1A_64_HASH,
|
||||
/**
|
||||
* 32-bit FNV1.
|
||||
*/
|
||||
FNV1_32_HASH,
|
||||
/**
|
||||
* 32-bit FNV1a.
|
||||
*/
|
||||
FNV1A_32_HASH,
|
||||
/**
|
||||
* MD5-based hash algorithm used by ketama.
|
||||
*/
|
||||
KETAMA_HASH,
|
||||
|
||||
/**
|
||||
* From mysql source
|
||||
*/
|
||||
MYSQL_HASH,
|
||||
|
||||
ELF_HASH,
|
||||
|
||||
RS_HASH,
|
||||
|
||||
/**
|
||||
* From lua source,it is used for long key
|
||||
*/
|
||||
LUA_HASH,
|
||||
|
||||
ELECTION_HASH,
|
||||
/**
|
||||
* The Jenkins One-at-a-time hash ,please see http://www.burtleburtle.net/bob/hash/doobs.html
|
||||
*/
|
||||
ONE_AT_A_TIME;
|
||||
|
||||
private static final long FNV_64_INIT = 0xcbf29ce484222325L;
|
||||
private static final long FNV_64_PRIME = 0x100000001b3L;
|
||||
|
||||
private static final long FNV_32_INIT = 2166136261L;
|
||||
private static final long FNV_32_PRIME = 16777619;
|
||||
|
||||
/**
|
||||
* Compute the hash for the given key.
|
||||
*
|
||||
* @return a positive integer hash
|
||||
*/
|
||||
public long hash(final String k) {
|
||||
long rv = 0;
|
||||
switch (this) {
|
||||
case NATIVE_HASH:
|
||||
rv = k.hashCode();
|
||||
break;
|
||||
case CRC32_HASH:
|
||||
// return (crc32(shift) >> 16) & 0x7fff;
|
||||
CRC32 crc32 = new CRC32();
|
||||
crc32.update(ByteUtils.getBytes(k));
|
||||
rv = crc32.getValue() >> 16 & 0x7fff;
|
||||
break;
|
||||
case FNV1_64_HASH: {
|
||||
// Thanks to pierre@demartines.com for the pointer
|
||||
rv = FNV_64_INIT;
|
||||
int len = k.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
rv *= FNV_64_PRIME;
|
||||
rv ^= k.charAt(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FNV1A_64_HASH: {
|
||||
rv = FNV_64_INIT;
|
||||
int len = k.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
rv ^= k.charAt(i);
|
||||
rv *= FNV_64_PRIME;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FNV1_32_HASH: {
|
||||
rv = FNV_32_INIT;
|
||||
int len = k.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
rv *= FNV_32_PRIME;
|
||||
rv ^= k.charAt(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FNV1A_32_HASH: {
|
||||
rv = FNV_32_INIT;
|
||||
int len = k.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
rv ^= k.charAt(i);
|
||||
rv *= FNV_32_PRIME;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ELECTION_HASH:
|
||||
case KETAMA_HASH:
|
||||
byte[] bKey = computeMd5(k);
|
||||
rv = (long) (bKey[3] & 0xFF) << 24 | (long) (bKey[2] & 0xFF) << 16
|
||||
| (long) (bKey[1] & 0xFF) << 8 | bKey[0] & 0xFF;
|
||||
break;
|
||||
|
||||
case MYSQL_HASH:
|
||||
int nr2 = 4;
|
||||
for (int i = 0; i < k.length(); i++) {
|
||||
rv ^= ((rv & 63) + nr2) * k.charAt(i) + (rv << 8);
|
||||
nr2 += 3;
|
||||
}
|
||||
break;
|
||||
case ELF_HASH:
|
||||
long x = 0;
|
||||
for (int i = 0; i < k.length(); i++) {
|
||||
rv = (rv << 4) + k.charAt(i);
|
||||
if ((x = rv & 0xF0000000L) != 0) {
|
||||
rv ^= x >> 24;
|
||||
rv &= ~x;
|
||||
}
|
||||
}
|
||||
rv = rv & 0x7FFFFFFF;
|
||||
break;
|
||||
case RS_HASH:
|
||||
long b = 378551;
|
||||
long a = 63689;
|
||||
for (int i = 0; i < k.length(); i++) {
|
||||
rv = rv * a + k.charAt(i);
|
||||
a *= b;
|
||||
}
|
||||
rv = rv & 0x7FFFFFFF;
|
||||
break;
|
||||
case LUA_HASH:
|
||||
int step = (k.length() >> 5) + 1;
|
||||
rv = k.length();
|
||||
for (int len = k.length(); len >= step; len -= step) {
|
||||
rv = rv ^ (rv << 5) + (rv >> 2) + k.charAt(len - 1);
|
||||
}
|
||||
break;
|
||||
case ONE_AT_A_TIME:
|
||||
try {
|
||||
int hash = 0;
|
||||
for (byte bt : k.getBytes("utf-8")) {
|
||||
hash += (bt & 0xFF);
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >>> 6);
|
||||
}
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >>> 11);
|
||||
hash += (hash << 15);
|
||||
rv = hash;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Hash function error", e);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
|
||||
return rv & 0xffffffffL; /* Convert to unsigned 32-bits */
|
||||
}
|
||||
|
||||
private static ThreadLocal<MessageDigest> md5Local = new ThreadLocal<MessageDigest>();
|
||||
|
||||
/**
|
||||
* Get the md5 of the given key.
|
||||
*/
|
||||
public static byte[] computeMd5(String k) {
|
||||
MessageDigest md5 = md5Local.get();
|
||||
if (md5 == null) {
|
||||
try {
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
md5Local.set(md5);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("MD5 not supported", e);
|
||||
}
|
||||
}
|
||||
md5.reset();
|
||||
md5.update(ByteUtils.getBytes(k));
|
||||
return md5.digest();
|
||||
}
|
||||
|
||||
// public static void main(String[] args) {
|
||||
// HashAlgorithm alg=HashAlgorithm.CRC32_HASH;
|
||||
// long h=0;
|
||||
// long start=System.currentTimeMillis();
|
||||
// for(int i=0;i<100000;i++)
|
||||
// h=alg.hash("MYSQL_HASH");
|
||||
// System.out.println(System.currentTimeMillis()-start);
|
||||
// }
|
||||
}
|
61
src/main/java/net/rubyeye/xmemcached/KeyIterator.java
Normal file
61
src/main/java/net/rubyeye/xmemcached/KeyIterator.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import net.rubyeye.xmemcached.exception.MemcachedException;
|
||||
|
||||
/**
|
||||
* Key Iterator for memcached,use 'stats items' and 'stats cachedump' to iterate all keys,it is
|
||||
* inefficient and not thread-safe.The 'stats cachedump" has length limitation,then iterator could
|
||||
* not visit all keys if you have many keys.</br>
|
||||
* <p>
|
||||
* <strong>Note</strong>: memcached 1.6.x will remove cachedump stats,so this feature will be
|
||||
* invalid in memcached 1.6.x
|
||||
* </p>
|
||||
*
|
||||
* @deprecated memcached 1.6.x will remove cachedump stats command,so this feature will be removed
|
||||
* in the future
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public interface KeyIterator {
|
||||
/**
|
||||
* Get next key,if iterator has reached the end,throw ArrayIndexOutOfBoundsException
|
||||
*
|
||||
* @return
|
||||
* @throws ArrayIndexOutOfBoundsException
|
||||
* ,MemcachedException,TimeoutException,InterruptedException
|
||||
*
|
||||
*/
|
||||
public String next() throws MemcachedException, TimeoutException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Check if the iterator has more keys.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasNext();
|
||||
|
||||
/**
|
||||
* Close this iterator when you don't need it any more.It is not mandatory to call this method,
|
||||
* but you might want to invoke this method for maximum performance.
|
||||
*/
|
||||
public void close();
|
||||
|
||||
/**
|
||||
* Get current iterator's memcached server address
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public InetSocketAddress getServerAddress();
|
||||
|
||||
/**
|
||||
* Set operation timeout,default is 1000 MILLISECONDS.
|
||||
*
|
||||
* @param opTimeout
|
||||
*/
|
||||
public void setOpTimeout(long opTimeout);
|
||||
|
||||
}
|
17
src/main/java/net/rubyeye/xmemcached/KeyProvider.java
Normal file
17
src/main/java/net/rubyeye/xmemcached/KeyProvider.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
/**
|
||||
* Key provider to pre-process keys before sending to memcached.
|
||||
*
|
||||
* @author dennis<killme2008@gmail.com>
|
||||
* @since 1.3.8
|
||||
*/
|
||||
public interface KeyProvider {
|
||||
/**
|
||||
* Processes key and returns a new key.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public String process(String key);
|
||||
}
|
1754
src/main/java/net/rubyeye/xmemcached/MemcachedClient.java
Normal file
1754
src/main/java/net/rubyeye/xmemcached/MemcachedClient.java
Normal file
File diff suppressed because it is too large
Load Diff
294
src/main/java/net/rubyeye/xmemcached/MemcachedClientBuilder.java
Normal file
294
src/main/java/net/rubyeye/xmemcached/MemcachedClientBuilder.java
Normal file
@@ -0,0 +1,294 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.rubyeye.xmemcached.auth.AuthInfo;
|
||||
import net.rubyeye.xmemcached.buffer.BufferAllocator;
|
||||
import net.rubyeye.xmemcached.transcoders.Transcoder;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.SocketOption;
|
||||
|
||||
/**
|
||||
* Builder pattern.Configure XmemcachedClient's options,then build it
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface MemcachedClientBuilder {
|
||||
|
||||
/**
|
||||
*
|
||||
* @return net.rubyeye.xmemcached.MemcachedSessionLocator
|
||||
*/
|
||||
public MemcachedSessionLocator getSessionLocator();
|
||||
|
||||
/**
|
||||
* Set the XmemcachedClient's session locator.Use ArrayMemcachedSessionLocator by default.If you
|
||||
* want to choose consistent hash strategy,set it to KetamaMemcachedSessionLocator
|
||||
*
|
||||
* @param sessionLocator
|
||||
*/
|
||||
public void setSessionLocator(MemcachedSessionLocator sessionLocator);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return net.rubyeye.xmemcached.MemcachedSessionComparator
|
||||
*/
|
||||
public MemcachedSessionComparator getSessionComparator();
|
||||
|
||||
/**
|
||||
* Set the XmemcachedClient's session comparator.Use IndexMemcachedSessionComparator by default.
|
||||
*
|
||||
* @param sessionComparator
|
||||
*/
|
||||
public void setSessionComparator(MemcachedSessionComparator sessionComparator);
|
||||
|
||||
public BufferAllocator getBufferAllocator();
|
||||
|
||||
/**
|
||||
* Set nio ByteBuffer's allocator.Use SimpleBufferAllocator by default.You can choose
|
||||
* CachedBufferAllocator.
|
||||
*
|
||||
* @param bufferAllocator
|
||||
*/
|
||||
public void setBufferAllocator(BufferAllocator bufferAllocator);
|
||||
|
||||
/**
|
||||
* Return the default networking's configuration,you can change them.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Configuration getConfiguration();
|
||||
|
||||
/**
|
||||
* Set the XmemcachedClient's networking configuration(reuseAddr,receiveBufferSize,tcpDelay etc.)
|
||||
*
|
||||
* @param configuration
|
||||
*/
|
||||
public void setConfiguration(Configuration configuration);
|
||||
|
||||
/**
|
||||
* Build MemcachedClient by current options.
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public MemcachedClient build() throws IOException;
|
||||
|
||||
/**
|
||||
* In a high concurrent enviroment,you may want to pool memcached clients.But a xmemcached client
|
||||
* has to start a reactor thread and some thread pools,if you create too many clients,the cost is
|
||||
* very large. Xmemcached supports connection pool instead of client pool.you can create more
|
||||
* connections to one or more memcached servers,and these connections share the same reactor and
|
||||
* thread pools,it will reduce the cost of system.
|
||||
*
|
||||
* @param poolSize pool size,default is 1
|
||||
*/
|
||||
public void setConnectionPoolSize(int poolSize);
|
||||
|
||||
/**
|
||||
* Set xmemcached's transcoder,it is used for seriailizing
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Transcoder getTranscoder();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setTranscoder(Transcoder transcoder);
|
||||
|
||||
/**
|
||||
* get xmemcached's command factory
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public CommandFactory getCommandFactory();
|
||||
|
||||
/**
|
||||
* Add a state listener
|
||||
*
|
||||
* @param stateListener
|
||||
*/
|
||||
public void addStateListener(MemcachedClientStateListener stateListener);
|
||||
|
||||
/**
|
||||
* Remove a state listener
|
||||
*
|
||||
* @param stateListener
|
||||
*/
|
||||
public void removeStateListener(MemcachedClientStateListener stateListener);
|
||||
|
||||
/**
|
||||
* Set state listeners,replace current list
|
||||
*
|
||||
* @param stateListeners
|
||||
*/
|
||||
public void setStateListeners(List<MemcachedClientStateListener> stateListeners);
|
||||
|
||||
/**
|
||||
* set xmemcached's command factory.Default is TextCommandFactory,which implements memcached text
|
||||
* protocol.
|
||||
*
|
||||
* @param commandFactory
|
||||
*/
|
||||
public void setCommandFactory(CommandFactory commandFactory);
|
||||
|
||||
/**
|
||||
* Set tcp socket option
|
||||
*
|
||||
* @param socketOption
|
||||
* @param value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setSocketOption(SocketOption socketOption, Object value);
|
||||
|
||||
/**
|
||||
* Get all tcp socket options
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<SocketOption, Object> getSocketOptions();
|
||||
|
||||
/**
|
||||
* Configure auth info
|
||||
*
|
||||
* @param map Auth info map,key is memcached server address,and value is the auth info for the
|
||||
* key.
|
||||
*/
|
||||
public void setAuthInfoMap(Map<InetSocketAddress, AuthInfo> map);
|
||||
|
||||
/**
|
||||
* return current all auth info
|
||||
*
|
||||
* @return Auth info map,key is memcached server address,and value is the auth info for the key.
|
||||
*/
|
||||
public Map<InetSocketAddress, AuthInfo> getAuthInfoMap();
|
||||
|
||||
/**
|
||||
* Add auth info for memcached server
|
||||
*
|
||||
* @param address
|
||||
* @param authInfo
|
||||
*/
|
||||
public void addAuthInfo(InetSocketAddress address, AuthInfo authInfo);
|
||||
|
||||
/**
|
||||
* Remove auth info for memcached server
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
public void removeAuthInfo(InetSocketAddress address);
|
||||
|
||||
/**
|
||||
* Return the cache instance name
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Set cache instance name
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
public void setName(String name);
|
||||
|
||||
/**
|
||||
* Configure wheather to set client in failure mode.If set it to true,that means you want to
|
||||
* configure client in failure mode. Failure mode is that when a memcached server is down,it would
|
||||
* not taken from the server list but marked as unavailable,and then further requests to this
|
||||
* server will be transformed to standby node if configured or throw an exception until it comes
|
||||
* back up.
|
||||
*
|
||||
* @param failureMode true is to configure client in failure mode.
|
||||
*/
|
||||
public void setFailureMode(boolean failureMode);
|
||||
|
||||
/**
|
||||
* Returns if client is in failure mode.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isFailureMode();
|
||||
|
||||
/**
|
||||
* Returns connect timeout in milliseconds
|
||||
*
|
||||
* @return connect timeout
|
||||
*/
|
||||
public long getConnectTimeout();
|
||||
|
||||
/**
|
||||
* Set connect timeout in milliseconds
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClient#DEFAULT_CONNECT_TIMEOUT
|
||||
*
|
||||
* @param connectTimeout
|
||||
*/
|
||||
public void setConnectTimeout(long connectTimeout);
|
||||
|
||||
/**
|
||||
* Enables/disables sanitizing keys by URLEncoding.
|
||||
*
|
||||
* @param sanitizeKey if true, then URLEncode all keys
|
||||
*/
|
||||
public void setSanitizeKeys(boolean sanitizeKeys);
|
||||
|
||||
/**
|
||||
* Set a key provider for pre-processing keys before sending them to memcached.
|
||||
*
|
||||
* @since 1.3.8
|
||||
* @param keyProvider
|
||||
*/
|
||||
public void setKeyProvider(KeyProvider keyProvider);
|
||||
|
||||
/**
|
||||
* Set max queued noreply operations number
|
||||
*
|
||||
* @see MemcachedClient#DEFAULT_MAX_QUEUED_NOPS
|
||||
* @param maxQueuedNoReplyOperations
|
||||
* @since 1.3.8
|
||||
*/
|
||||
public void setMaxQueuedNoReplyOperations(int maxQueuedNoReplyOperations);
|
||||
|
||||
/**
|
||||
* If the memcached dump or network error cause connection closed,xmemcached would try to heal the
|
||||
* connection.The interval between reconnections is 2 seconds by default. You can change that
|
||||
* value by this method.
|
||||
*
|
||||
* @since 1.3.9
|
||||
* @param healConnectionInterval MILLISECONDS
|
||||
*/
|
||||
public void setHealSessionInterval(long healConnectionInterval);
|
||||
|
||||
/**
|
||||
* If the memcached dump or network error cause connection closed,xmemcached would try to heal the
|
||||
* connection.You can disable this behaviour by using this method:<br/>
|
||||
* <code> client.setEnableHealSession(false); </code><br/>
|
||||
* The default value is true.
|
||||
*
|
||||
* @param enableHealSession
|
||||
* @since 1.3.9
|
||||
*/
|
||||
public void setEnableHealSession(boolean enableHealSession);
|
||||
|
||||
/**
|
||||
* Set default operation timeout.
|
||||
*
|
||||
* @param opTimeout Operation timeout value in milliseconds.
|
||||
* @since 1.4.1
|
||||
*/
|
||||
public void setOpTimeout(long opTimeout);
|
||||
|
||||
/**
|
||||
* Returns the default operation timeout in milliseconds.
|
||||
*
|
||||
* @since 1.4.1
|
||||
* @return
|
||||
*/
|
||||
public long getOpTimeout();
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import net.rubyeye.xmemcached.exception.MemcachedException;
|
||||
|
||||
/**
|
||||
* MemcachedClient callable when using namespace in xmemcached.For example:
|
||||
*
|
||||
* <pre>
|
||||
* memcachedClient.withNamespace(userId,new MemcachedClientCallable<Void>{
|
||||
* public Void call(MemcachedClient client) throws MemcachedException,
|
||||
* InterruptedException, TimeoutException{
|
||||
* client.set("username",0,username);
|
||||
* client.set("email",0,email);
|
||||
* return null;
|
||||
* }
|
||||
* });
|
||||
* //invalidate all items under the namespace.
|
||||
* memcachedClient.invalidateNamespace(userId);
|
||||
* </pre>
|
||||
*
|
||||
* @author dennis<killme2008@gmail.com>
|
||||
* @see MemcachedClient#withNamespace(String, MemcachedClientCallable)
|
||||
* @since 1.4.2
|
||||
* @param <T>
|
||||
*/
|
||||
public interface MemcachedClientCallable<T> {
|
||||
public T call(MemcachedClient client)
|
||||
throws MemcachedException, InterruptedException, TimeoutException;
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* MemcachedClient state listener.When client startup,shutdown,connected to a memcached server or
|
||||
* disconnected happened,client will notify the listener instance which implemented this
|
||||
* interface.Please don't do any operations which may block in these callback methods.
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface MemcachedClientStateListener {
|
||||
/**
|
||||
* After client is started.
|
||||
*
|
||||
* @param memcachedClient
|
||||
*/
|
||||
public void onStarted(MemcachedClient memcachedClient);
|
||||
|
||||
/**
|
||||
* After client is shutdown.
|
||||
*
|
||||
* @param memcachedClient
|
||||
*/
|
||||
public void onShutDown(MemcachedClient memcachedClient);
|
||||
|
||||
/**
|
||||
* After a memcached server is connected,don't do any operations may block here.
|
||||
*
|
||||
* @param memcachedClient
|
||||
* @param inetSocketAddress
|
||||
*/
|
||||
public void onConnected(MemcachedClient memcachedClient, InetSocketAddress inetSocketAddress);
|
||||
|
||||
/**
|
||||
* After a memcached server is disconnected,don't do any operations may block here.
|
||||
*
|
||||
* @param memcachedClient
|
||||
* @param inetSocketAddress
|
||||
*/
|
||||
public void onDisconnected(MemcachedClient memcachedClient, InetSocketAddress inetSocketAddress);
|
||||
|
||||
/**
|
||||
* When exceptions occur
|
||||
*
|
||||
* @param memcachedClient
|
||||
* @param throwable
|
||||
*/
|
||||
public void onException(MemcachedClient memcachedClient, Throwable throwable);
|
||||
}
|
35
src/main/java/net/rubyeye/xmemcached/MemcachedOptimizer.java
Normal file
35
src/main/java/net/rubyeye/xmemcached/MemcachedOptimizer.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.util.Queue;
|
||||
import net.rubyeye.xmemcached.buffer.BufferAllocator;
|
||||
import net.rubyeye.xmemcached.command.Command;
|
||||
|
||||
/**
|
||||
* xmemcached Optimizer
|
||||
*
|
||||
* @author dennis
|
||||
*/
|
||||
public interface MemcachedOptimizer {
|
||||
@SuppressWarnings("unchecked")
|
||||
Command optimize(final Command currentCommand, final Queue writeQueue,
|
||||
final Queue<Command> executingCmds, int sendBufferSize);
|
||||
|
||||
@Deprecated
|
||||
public void setBufferAllocator(BufferAllocator bufferAllocator);
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.util.Comparator;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
|
||||
/**
|
||||
* Session comparator.
|
||||
*
|
||||
* @author Jungsub Shin
|
||||
*
|
||||
*/
|
||||
public interface MemcachedSessionComparator extends Comparator<Session> {
|
||||
/**
|
||||
* Returns a session by special key.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public int compare(Session o1, Session o2);
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)] Licensed under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
|
||||
* may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
* applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
|
||||
* the License for the specific language governing permissions and limitations under the License
|
||||
*/
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.util.Collection;
|
||||
import com.google.code.yanf4j.core.Session;
|
||||
|
||||
/**
|
||||
* Session locator.Find session by key.
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface MemcachedSessionLocator {
|
||||
/**
|
||||
* Returns a session by special key.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Session getSessionByKey(final String key);
|
||||
|
||||
/**
|
||||
* Update sessions when session was added or removed.
|
||||
*
|
||||
* @param list The newer sessions
|
||||
*/
|
||||
public void updateSessions(final Collection<Session> list);
|
||||
|
||||
/**
|
||||
* Configure failure mode
|
||||
*
|
||||
* @param failureMode true is using failure mode
|
||||
*/
|
||||
public void setFailureMode(boolean failureMode);
|
||||
}
|
2669
src/main/java/net/rubyeye/xmemcached/XMemcachedClient.java
Normal file
2669
src/main/java/net/rubyeye/xmemcached/XMemcachedClient.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,465 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.rubyeye.xmemcached.auth.AuthInfo;
|
||||
import net.rubyeye.xmemcached.buffer.BufferAllocator;
|
||||
import net.rubyeye.xmemcached.buffer.SimpleBufferAllocator;
|
||||
import net.rubyeye.xmemcached.command.TextCommandFactory;
|
||||
import net.rubyeye.xmemcached.impl.ArrayMemcachedSessionLocator;
|
||||
import net.rubyeye.xmemcached.impl.DefaultKeyProvider;
|
||||
import net.rubyeye.xmemcached.impl.IndexMemcachedSessionComparator;
|
||||
import net.rubyeye.xmemcached.impl.RandomMemcachedSessionLocaltor;
|
||||
import net.rubyeye.xmemcached.transcoders.SerializingTranscoder;
|
||||
import net.rubyeye.xmemcached.transcoders.Transcoder;
|
||||
import net.rubyeye.xmemcached.utils.AddrUtil;
|
||||
import net.rubyeye.xmemcached.utils.Protocol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.code.yanf4j.config.Configuration;
|
||||
import com.google.code.yanf4j.core.SocketOption;
|
||||
import com.google.code.yanf4j.core.impl.StandardSocketOption;
|
||||
|
||||
/**
|
||||
* Builder pattern.Configure XmemcachedClient's options,then build it
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class XMemcachedClientBuilder implements MemcachedClientBuilder {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(XMemcachedClientBuilder.class);
|
||||
|
||||
protected MemcachedSessionLocator sessionLocator = new ArrayMemcachedSessionLocator();
|
||||
protected MemcachedSessionComparator sessionComparator = new IndexMemcachedSessionComparator();
|
||||
protected BufferAllocator bufferAllocator = new SimpleBufferAllocator();
|
||||
protected Configuration configuration = getDefaultConfiguration();
|
||||
protected Map<InetSocketAddress, InetSocketAddress> addressMap =
|
||||
new LinkedHashMap<InetSocketAddress, InetSocketAddress>();
|
||||
|
||||
protected int[] weights;
|
||||
|
||||
protected long connectTimeout = MemcachedClient.DEFAULT_CONNECT_TIMEOUT;
|
||||
|
||||
protected int connectionPoolSize = MemcachedClient.DEFAULT_CONNECTION_POOL_SIZE;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final Map<SocketOption, Object> socketOptions = getDefaultSocketOptions();
|
||||
|
||||
protected List<MemcachedClientStateListener> stateListeners =
|
||||
new ArrayList<MemcachedClientStateListener>();
|
||||
|
||||
protected Map<InetSocketAddress, AuthInfo> authInfoMap =
|
||||
new HashMap<InetSocketAddress, AuthInfo>();
|
||||
|
||||
protected String name;
|
||||
|
||||
protected boolean failureMode;
|
||||
|
||||
protected boolean sanitizeKeys;
|
||||
|
||||
protected KeyProvider keyProvider = DefaultKeyProvider.INSTANCE;
|
||||
|
||||
protected int maxQueuedNoReplyOperations = MemcachedClient.DEFAULT_MAX_QUEUED_NOPS;
|
||||
|
||||
protected long healSessionInterval = MemcachedClient.DEFAULT_HEAL_SESSION_INTERVAL;
|
||||
|
||||
protected boolean enableHealSession = true;
|
||||
|
||||
protected long opTimeout = MemcachedClient.DEFAULT_OP_TIMEOUT;
|
||||
|
||||
protected boolean resolveInetAddresses = true;
|
||||
|
||||
public boolean isResolveInetAddresses() {
|
||||
return resolveInetAddresses;
|
||||
}
|
||||
|
||||
public void setResolveInetAddresses(boolean resolveInetAddresses) {
|
||||
this.resolveInetAddresses = resolveInetAddresses;
|
||||
}
|
||||
|
||||
public void doNotResolveInetAddresses() {
|
||||
this.resolveInetAddresses = false;
|
||||
}
|
||||
|
||||
public long getOpTimeout() {
|
||||
return opTimeout;
|
||||
}
|
||||
|
||||
public void setOpTimeout(long opTimeout) {
|
||||
if (opTimeout <= 0)
|
||||
throw new IllegalArgumentException("Invalid opTimeout value:" + opTimeout);
|
||||
this.opTimeout = opTimeout;
|
||||
}
|
||||
|
||||
public int getMaxQueuedNoReplyOperations() {
|
||||
return maxQueuedNoReplyOperations;
|
||||
}
|
||||
|
||||
public long getHealSessionInterval() {
|
||||
return healSessionInterval;
|
||||
}
|
||||
|
||||
public void setHealSessionInterval(long healSessionInterval) {
|
||||
this.healSessionInterval = healSessionInterval;
|
||||
}
|
||||
|
||||
public boolean isEnableHealSession() {
|
||||
return enableHealSession;
|
||||
}
|
||||
|
||||
public void setEnableHealSession(boolean enableHealSession) {
|
||||
this.enableHealSession = enableHealSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set max queued noreply operations number
|
||||
*
|
||||
* @see MemcachedClient#DEFAULT_MAX_QUEUED_NOPS
|
||||
* @param maxQueuedNoReplyOperations
|
||||
* @since 1.3.8
|
||||
*/
|
||||
public void setMaxQueuedNoReplyOperations(int maxQueuedNoReplyOperations) {
|
||||
this.maxQueuedNoReplyOperations = maxQueuedNoReplyOperations;
|
||||
}
|
||||
|
||||
public void setSanitizeKeys(boolean sanitizeKeys) {
|
||||
this.sanitizeKeys = sanitizeKeys;
|
||||
}
|
||||
|
||||
public void addStateListener(MemcachedClientStateListener stateListener) {
|
||||
this.stateListeners.add(stateListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setSocketOption(SocketOption socketOption, Object value) {
|
||||
if (socketOption == null) {
|
||||
throw new NullPointerException("Null socketOption");
|
||||
}
|
||||
if (value == null) {
|
||||
throw new NullPointerException("Null value");
|
||||
}
|
||||
if (!socketOption.type().equals(value.getClass())) {
|
||||
throw new IllegalArgumentException("Expected " + socketOption.type().getSimpleName()
|
||||
+ " value,but givend " + value.getClass().getSimpleName());
|
||||
}
|
||||
this.socketOptions.put(socketOption, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<SocketOption, Object> getSocketOptions() {
|
||||
return this.socketOptions;
|
||||
}
|
||||
|
||||
public final void setConnectionPoolSize(int poolSize) {
|
||||
if (this.connectionPoolSize <= 0) {
|
||||
throw new IllegalArgumentException("poolSize<=0");
|
||||
}
|
||||
this.connectionPoolSize = poolSize;
|
||||
}
|
||||
|
||||
public void removeStateListener(MemcachedClientStateListener stateListener) {
|
||||
this.stateListeners.remove(stateListener);
|
||||
}
|
||||
|
||||
public long getConnectTimeout() {
|
||||
return connectTimeout;
|
||||
}
|
||||
|
||||
public void setConnectTimeout(long connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
public void setStateListeners(List<MemcachedClientStateListener> stateListeners) {
|
||||
if (stateListeners == null) {
|
||||
throw new IllegalArgumentException("Null state listeners");
|
||||
}
|
||||
this.stateListeners = stateListeners;
|
||||
}
|
||||
|
||||
protected CommandFactory commandFactory = new TextCommandFactory();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Map<SocketOption, Object> getDefaultSocketOptions() {
|
||||
Map<SocketOption, Object> map = new HashMap<SocketOption, Object>();
|
||||
map.put(StandardSocketOption.TCP_NODELAY, MemcachedClient.DEFAULT_TCP_NO_DELAY);
|
||||
map.put(StandardSocketOption.SO_RCVBUF, MemcachedClient.DEFAULT_TCP_RECV_BUFF_SIZE);
|
||||
map.put(StandardSocketOption.SO_KEEPALIVE, MemcachedClient.DEFAULT_TCP_KEEPLIVE);
|
||||
map.put(StandardSocketOption.SO_SNDBUF, MemcachedClient.DEFAULT_TCP_SEND_BUFF_SIZE);
|
||||
map.put(StandardSocketOption.SO_LINGER, 0);
|
||||
map.put(StandardSocketOption.SO_REUSEADDR, true);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static final Configuration getDefaultConfiguration() {
|
||||
final Configuration configuration = new Configuration();
|
||||
configuration.setSessionReadBufferSize(MemcachedClient.DEFAULT_SESSION_READ_BUFF_SIZE);
|
||||
configuration.setReadThreadCount(MemcachedClient.DEFAULT_READ_THREAD_COUNT);
|
||||
configuration.setSessionIdleTimeout(MemcachedClient.DEFAULT_SESSION_IDLE_TIMEOUT);
|
||||
configuration.setWriteThreadCount(0);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public boolean isFailureMode() {
|
||||
return this.failureMode;
|
||||
}
|
||||
|
||||
public void setFailureMode(boolean failureMode) {
|
||||
this.failureMode = failureMode;
|
||||
}
|
||||
|
||||
public final CommandFactory getCommandFactory() {
|
||||
return this.commandFactory;
|
||||
}
|
||||
|
||||
public final void setCommandFactory(CommandFactory commandFactory) {
|
||||
this.commandFactory = commandFactory;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
protected Transcoder transcoder = new SerializingTranscoder();
|
||||
|
||||
public XMemcachedClientBuilder(String addressList) {
|
||||
this(AddrUtil.getAddresses(addressList));
|
||||
}
|
||||
|
||||
public XMemcachedClientBuilder(List<InetSocketAddress> addressList) {
|
||||
if (addressList != null) {
|
||||
for (InetSocketAddress addr : addressList) {
|
||||
this.addressMap.put(addr, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public XMemcachedClientBuilder(List<InetSocketAddress> addressList, int[] weights) {
|
||||
if (addressList != null) {
|
||||
for (InetSocketAddress addr : addressList) {
|
||||
this.addressMap.put(addr, null);
|
||||
}
|
||||
}
|
||||
this.weights = weights;
|
||||
}
|
||||
|
||||
public XMemcachedClientBuilder(Map<InetSocketAddress, InetSocketAddress> addressMap) {
|
||||
this.addressMap = addressMap;
|
||||
}
|
||||
|
||||
public XMemcachedClientBuilder(Map<InetSocketAddress, InetSocketAddress> addressMap,
|
||||
int[] weights) {
|
||||
this.addressMap = addressMap;
|
||||
this.weights = weights;
|
||||
}
|
||||
|
||||
public XMemcachedClientBuilder() {
|
||||
this((Map<InetSocketAddress, InetSocketAddress>) null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#getSessionLocator()
|
||||
*/
|
||||
public MemcachedSessionLocator getSessionLocator() {
|
||||
return this.sessionLocator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#setSessionLocator(net. rubyeye
|
||||
* .xmemcached.MemcachedSessionLocator)
|
||||
*/
|
||||
public void setSessionLocator(MemcachedSessionLocator sessionLocator) {
|
||||
if (sessionLocator == null) {
|
||||
throw new IllegalArgumentException("Null SessionLocator");
|
||||
}
|
||||
this.sessionLocator = sessionLocator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#getSessionComparator()
|
||||
*/
|
||||
public MemcachedSessionComparator getSessionComparator() {
|
||||
return this.sessionComparator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#setSessionComparator(net. rubyeye
|
||||
* .xmemcached.MemcachedSessionComparator)
|
||||
*/
|
||||
public void setSessionComparator(MemcachedSessionComparator sessionComparator) {
|
||||
if (sessionComparator == null) {
|
||||
throw new IllegalArgumentException("Null SessionComparator");
|
||||
}
|
||||
this.sessionComparator = sessionComparator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#getBufferAllocator()
|
||||
*/
|
||||
public BufferAllocator getBufferAllocator() {
|
||||
return this.bufferAllocator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#setBufferAllocator(net.
|
||||
* rubyeye.xmemcached.buffer.BufferAllocator)
|
||||
*/
|
||||
public void setBufferAllocator(BufferAllocator bufferAllocator) {
|
||||
if (bufferAllocator == null) {
|
||||
throw new IllegalArgumentException("Null bufferAllocator");
|
||||
}
|
||||
this.bufferAllocator = bufferAllocator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#getConfiguration()
|
||||
*/
|
||||
public Configuration getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#setConfiguration(com.google
|
||||
* .code.yanf4j.config.Configuration)
|
||||
*/
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#build()
|
||||
*/
|
||||
public MemcachedClient build() throws IOException {
|
||||
XMemcachedClient memcachedClient;
|
||||
// kestrel protocol use random session locator.
|
||||
if (this.commandFactory.getProtocol() == Protocol.Kestrel) {
|
||||
if (!(this.sessionLocator instanceof RandomMemcachedSessionLocaltor)) {
|
||||
log.warn(
|
||||
"Recommend to use `net.rubyeye.xmemcached.impl.RandomMemcachedSessionLocaltor` as session locator for kestrel protocol.");
|
||||
}
|
||||
}
|
||||
if (this.weights == null) {
|
||||
memcachedClient =
|
||||
new XMemcachedClient(this.sessionLocator, this.sessionComparator, this.bufferAllocator,
|
||||
this.configuration, this.socketOptions, this.commandFactory, this.transcoder,
|
||||
this.addressMap, this.stateListeners, this.authInfoMap, this.connectionPoolSize,
|
||||
this.connectTimeout, this.name, this.failureMode, this.resolveInetAddresses);
|
||||
|
||||
} else {
|
||||
if (this.addressMap == null) {
|
||||
throw new IllegalArgumentException("Null Address map");
|
||||
}
|
||||
if (this.addressMap.size() > this.weights.length) {
|
||||
throw new IllegalArgumentException("Weights Array's length is less than server's number");
|
||||
}
|
||||
memcachedClient = new XMemcachedClient(this.sessionLocator, this.sessionComparator,
|
||||
this.bufferAllocator, this.configuration, this.socketOptions, this.commandFactory,
|
||||
this.transcoder, this.addressMap, this.weights, this.stateListeners, this.authInfoMap,
|
||||
this.connectionPoolSize, this.connectTimeout, this.name, this.failureMode,
|
||||
this.resolveInetAddresses);
|
||||
}
|
||||
this.configureClient(memcachedClient);
|
||||
return memcachedClient;
|
||||
}
|
||||
|
||||
protected void configureClient(XMemcachedClient memcachedClient) {
|
||||
if (this.commandFactory.getProtocol() == Protocol.Kestrel) {
|
||||
memcachedClient.setOptimizeGet(false);
|
||||
}
|
||||
memcachedClient.setConnectTimeout(connectTimeout);
|
||||
memcachedClient.setSanitizeKeys(sanitizeKeys);
|
||||
memcachedClient.setKeyProvider(this.keyProvider);
|
||||
memcachedClient.setOpTimeout(this.opTimeout);
|
||||
memcachedClient.setHealSessionInterval(this.healSessionInterval);
|
||||
memcachedClient.setEnableHealSession(this.enableHealSession);
|
||||
memcachedClient.setMaxQueuedNoReplyOperations(this.maxQueuedNoReplyOperations);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Transcoder getTranscoder() {
|
||||
return this.transcoder;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#setTranscoder(transcoder)
|
||||
*/
|
||||
public void setTranscoder(Transcoder transcoder) {
|
||||
if (transcoder == null) {
|
||||
throw new IllegalArgumentException("Null Transcoder");
|
||||
}
|
||||
this.transcoder = transcoder;
|
||||
}
|
||||
|
||||
public Map<InetSocketAddress, AuthInfo> getAuthInfoMap() {
|
||||
return this.authInfoMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#setKeyProvider()
|
||||
*/
|
||||
public void setKeyProvider(KeyProvider keyProvider) {
|
||||
if (keyProvider == null)
|
||||
throw new IllegalArgumentException("null key provider");
|
||||
this.keyProvider = keyProvider;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#addAuthInfo()
|
||||
*/
|
||||
public void addAuthInfo(InetSocketAddress address, AuthInfo authInfo) {
|
||||
this.authInfoMap.put(address, authInfo);
|
||||
}
|
||||
|
||||
public void removeAuthInfo(InetSocketAddress address) {
|
||||
this.authInfoMap.remove(address);
|
||||
}
|
||||
|
||||
public void setAuthInfoMap(Map<InetSocketAddress, AuthInfo> authInfoMap) {
|
||||
this.authInfoMap = authInfoMap;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see net.rubyeye.xmemcached.MemcachedClientBuilder#setName()
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
|
||||
}
|
||||
|
||||
public void setSelectorPoolSize(int selectorPoolSize) {
|
||||
getConfiguration().setSelectorPoolSize(selectorPoolSize);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package net.rubyeye.xmemcached;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* XMemcachedClientMBean.It is used for JMX to add/remove memcached server.
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public interface XMemcachedClientMBean {
|
||||
|
||||
/**
|
||||
* Add memcached servers
|
||||
*
|
||||
* @param host a String in the form of "[host1]:[port1],[host2]:[port2]
|
||||
* [host3]:[port3],[host4]:[port4]"
|
||||
*/
|
||||
public void addServer(String hostList) throws IOException;
|
||||
|
||||
/**
|
||||
* Add a memcached server
|
||||
*
|
||||
* @param server a String in the form of "[host1]:[port1],[host2]:[port2]"
|
||||
* @param weight server's weight
|
||||
*/
|
||||
public void addOneServerWithWeight(String server, int weight) throws IOException;
|
||||
|
||||
/**
|
||||
* Remove memcached servers
|
||||
*
|
||||
* @param host a string in the form of "[host1]:[port1],[host2]:[port2]
|
||||
* [host3]:[port3],[host4]:[port4]"
|
||||
*/
|
||||
public void removeServer(String hostList);
|
||||
|
||||
/**
|
||||
* Get all connected memcached servers
|
||||
*
|
||||
* @return a list of string,every string is in the form of "[host1]:[port1](weight=num1)
|
||||
* [host2]:[port2](weight=num1)"
|
||||
*/
|
||||
public List<String> getServersDescription();
|
||||
|
||||
/**
|
||||
* Set a memcached server's weight
|
||||
*
|
||||
* @param server
|
||||
* @param weight
|
||||
*/
|
||||
public void setServerWeight(String server, int weight);
|
||||
|
||||
/**
|
||||
* Return the cache instance name
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
}
|
85
src/main/java/net/rubyeye/xmemcached/auth/AuthInfo.java
Normal file
85
src/main/java/net/rubyeye/xmemcached/auth/AuthInfo.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package net.rubyeye.xmemcached.auth;
|
||||
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
|
||||
/**
|
||||
* Authentication infomation for a memcached server
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class AuthInfo {
|
||||
private final CallbackHandler callbackHandler;
|
||||
private final String[] mechanisms;
|
||||
private final int maxAttempts =
|
||||
Integer.parseInt(System.getProperty("net.rubyeye.xmemcached.auth_max_attempts", "-1"));
|
||||
private int attempts;
|
||||
|
||||
public synchronized boolean isValid() {
|
||||
return this.attempts <= this.maxAttempts || this.maxAttempts < 0;
|
||||
}
|
||||
|
||||
public synchronized boolean isFirstTime() {
|
||||
return this.attempts == 0;
|
||||
}
|
||||
|
||||
public synchronized void increaseAttempts() {
|
||||
this.attempts++;
|
||||
}
|
||||
|
||||
public AuthInfo(CallbackHandler callbackHandler, String[] mechanisms) {
|
||||
super();
|
||||
this.callbackHandler = callbackHandler;
|
||||
this.mechanisms = mechanisms;
|
||||
}
|
||||
|
||||
public int getMaxAttempts() {
|
||||
return maxAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a typical auth descriptor for PLAIN auth with the given username and password.
|
||||
*
|
||||
* @param u the username
|
||||
* @param p the password
|
||||
*
|
||||
* @return an AuthInfo
|
||||
*/
|
||||
public static AuthInfo plain(String username, String password) {
|
||||
return new AuthInfo(new PlainCallbackHandler(username, password), new String[] {"PLAIN"});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a typical auth descriptor for CRAM-MD5 auth with the given username and password.
|
||||
*
|
||||
* @param u the username
|
||||
* @param p the password
|
||||
*
|
||||
* @return an AuthInfo
|
||||
*/
|
||||
public static AuthInfo cramMD5(String username, String password) {
|
||||
return new AuthInfo(new PlainCallbackHandler(username, password), new String[] {"CRAM-MD5"});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a typical auth descriptor for CRAM-MD5 or PLAIN auth with the given username and password.
|
||||
*
|
||||
* @param u the username
|
||||
* @param p the password
|
||||
*
|
||||
* @return an AuthInfo
|
||||
*/
|
||||
public static AuthInfo typical(String username, String password) {
|
||||
return new AuthInfo(new PlainCallbackHandler(username, password),
|
||||
new String[] {"CRAM-MD5", "PLAIN"});
|
||||
}
|
||||
|
||||
public CallbackHandler getCallbackHandler() {
|
||||
return callbackHandler;
|
||||
}
|
||||
|
||||
public String[] getMechanisms() {
|
||||
return mechanisms;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package net.rubyeye.xmemcached.auth;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Map;
|
||||
import net.rubyeye.xmemcached.MemcachedClient;
|
||||
import net.rubyeye.xmemcached.XMemcachedClient;
|
||||
import net.rubyeye.xmemcached.impl.MemcachedTCPSession;
|
||||
import net.rubyeye.xmemcached.networking.MemcachedSession;
|
||||
import net.rubyeye.xmemcached.networking.MemcachedSessionConnectListener;
|
||||
import net.rubyeye.xmemcached.utils.AddrUtil;
|
||||
|
||||
/**
|
||||
* Client state listener for auth
|
||||
*
|
||||
* @author dennis
|
||||
*
|
||||
*/
|
||||
public class AuthMemcachedConnectListener implements MemcachedSessionConnectListener {
|
||||
|
||||
public void onConnect(MemcachedSession session, MemcachedClient client) {
|
||||
MemcachedTCPSession tcpSession = (MemcachedTCPSession) session;
|
||||
Map<String, AuthInfo> authInfoMap = client.getAuthInfoStringMap();
|
||||
if (authInfoMap != null) {
|
||||
AuthInfo authInfo =
|
||||
authInfoMap.get(AddrUtil.getServerString(tcpSession.getRemoteSocketAddress()));
|
||||
if (authInfo != null) {
|
||||
XMemcachedClient xMemcachedClient = (XMemcachedClient) client;
|
||||
AuthTask task = new AuthTask(authInfo, xMemcachedClient.getCommandFactory(), tcpSession);
|
||||
task.start();
|
||||
// First time,try to wait
|
||||
if (authInfo.isFirstTime()) {
|
||||
try {
|
||||
task.join(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user