Imported Upstream version 1.5.1
This commit is contained in:
75
java/foundation/build-1.6.5.xml
Normal file
75
java/foundation/build-1.6.5.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||
<!-- the Compile on Save feature is turned off for the project. -->
|
||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||
<!-- in the project's Project Properties dialog box.-->
|
||||
<project name="Foundation" default="default" basedir=".">
|
||||
<description>Builds, tests, and runs the project Foundation as part of the XtreemFS project.</description>
|
||||
<import file="nbproject/build-impl-1.6.5.xml"/>
|
||||
|
||||
<!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
-pre-init: called before initialization of project properties
|
||||
-post-init: called after initialization of project properties
|
||||
-pre-compile: called before javac compilation
|
||||
-post-compile: called after javac compilation
|
||||
-pre-compile-single: called before javac compilation of single file
|
||||
-post-compile-single: called after javac compilation of single file
|
||||
-pre-compile-test: called before javac compilation of JUnit tests
|
||||
-post-compile-test: called after javac compilation of JUnit tests
|
||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||
-pre-jar: called before JAR building
|
||||
-post-jar: called after JAR building
|
||||
-post-clean: called after cleaning build products
|
||||
|
||||
(Targets beginning with '-' are not intended to be called on their own.)
|
||||
|
||||
Example of inserting an obfuscator after compilation could look like this:
|
||||
|
||||
<target name="-post-compile">
|
||||
<obfuscate>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</obfuscate>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
|
||||
Another way to customize the build is by overriding existing main targets.
|
||||
The targets of interest are:
|
||||
|
||||
-init-macrodef-javac: defines macro for javac compilation
|
||||
-init-macrodef-junit: defines macro for junit execution
|
||||
-init-macrodef-debug: defines macro for class debugging
|
||||
-init-macrodef-java: defines macro for class execution
|
||||
-do-jar-with-manifest: JAR building (if you are using a manifest)
|
||||
-do-jar-without-manifest: JAR building (if you are not using a manifest)
|
||||
run: execution of project
|
||||
-javadoc-build: Javadoc generation
|
||||
test-report: JUnit report generation
|
||||
|
||||
An example of overriding the target for project execution could look like this:
|
||||
|
||||
<target name="run" depends="XtreemFS-impl.jar">
|
||||
<exec dir="bin" executable="launcher.exe">
|
||||
<arg file="${dist.jar}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
Notice that the overridden target depends on the jar target and not only on
|
||||
the compile target as the regular run target does. Again, for a list of available
|
||||
properties which you can use, check the target you are overriding in the
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
-->
|
||||
</project>
|
||||
74
java/foundation/build-before-profiler.xml
Normal file
74
java/foundation/build-before-profiler.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||
<!-- the Compile on Save feature is turned off for the project. -->
|
||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||
<!-- in the project's Project Properties dialog box.-->
|
||||
<project name="Foundation" default="default" basedir=".">
|
||||
<description>Builds, tests, and runs the project Foundation as part of the XtreemFS project.</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
<!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
-pre-init: called before initialization of project properties
|
||||
-post-init: called after initialization of project properties
|
||||
-pre-compile: called before javac compilation
|
||||
-post-compile: called after javac compilation
|
||||
-pre-compile-single: called before javac compilation of single file
|
||||
-post-compile-single: called after javac compilation of single file
|
||||
-pre-compile-test: called before javac compilation of JUnit tests
|
||||
-post-compile-test: called after javac compilation of JUnit tests
|
||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||
-pre-jar: called before JAR building
|
||||
-post-jar: called after JAR building
|
||||
-post-clean: called after cleaning build products
|
||||
|
||||
(Targets beginning with '-' are not intended to be called on their own.)
|
||||
|
||||
Example of inserting an obfuscator after compilation could look like this:
|
||||
|
||||
<target name="-post-compile">
|
||||
<obfuscate>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</obfuscate>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
|
||||
Another way to customize the build is by overriding existing main targets.
|
||||
The targets of interest are:
|
||||
|
||||
-init-macrodef-javac: defines macro for javac compilation
|
||||
-init-macrodef-junit: defines macro for junit execution
|
||||
-init-macrodef-debug: defines macro for class debugging
|
||||
-init-macrodef-java: defines macro for class execution
|
||||
-do-jar-with-manifest: JAR building (if you are using a manifest)
|
||||
-do-jar-without-manifest: JAR building (if you are not using a manifest)
|
||||
run: execution of project
|
||||
-javadoc-build: Javadoc generation
|
||||
test-report: JUnit report generation
|
||||
|
||||
An example of overriding the target for project execution could look like this:
|
||||
|
||||
<target name="run" depends="XtreemFS-impl.jar">
|
||||
<exec dir="bin" executable="launcher.exe">
|
||||
<arg file="${dist.jar}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
Notice that the overridden target depends on the jar target and not only on
|
||||
the compile target as the regular run target does. Again, for a list of available
|
||||
properties which you can use, check the target you are overriding in the
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
-->
|
||||
</project>
|
||||
89
java/foundation/build.xml
Normal file
89
java/foundation/build.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||
<!-- the Compile on Save feature is turned off for the project. -->
|
||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||
<!-- in the project's Project Properties dialog box.-->
|
||||
<project name="Foundation" default="default" basedir=".">
|
||||
<description>Builds, tests, and runs the project Foundation as part of the XtreemFS project.</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
|
||||
<import file="nbproject/profiler-build-impl.xml"/> <!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
-pre-init: called before initialization of project properties
|
||||
-post-init: called after initialization of project properties
|
||||
-pre-compile: called before javac compilation
|
||||
-post-compile: called after javac compilation
|
||||
-pre-compile-single: called before javac compilation of single file
|
||||
-post-compile-single: called after javac compilation of single file
|
||||
-pre-compile-test: called before javac compilation of JUnit tests
|
||||
-post-compile-test: called after javac compilation of JUnit tests
|
||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||
-pre-jar: called before JAR building
|
||||
-post-jar: called after JAR building
|
||||
-post-clean: called after cleaning build products
|
||||
|
||||
(Targets beginning with '-' are not intended to be called on their own.)
|
||||
|
||||
Example of inserting an obfuscator after compilation could look like this:
|
||||
|
||||
<target name="-post-compile">
|
||||
<obfuscate>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</obfuscate>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
|
||||
Another way to customize the build is by overriding existing main targets.
|
||||
The targets of interest are:
|
||||
|
||||
-init-macrodef-javac: defines macro for javac compilation
|
||||
-init-macrodef-junit: defines macro for junit execution
|
||||
-init-macrodef-debug: defines macro for class debugging
|
||||
-init-macrodef-java: defines macro for class execution
|
||||
-do-jar-with-manifest: JAR building (if you are using a manifest)
|
||||
-do-jar-without-manifest: JAR building (if you are not using a manifest)
|
||||
run: execution of project
|
||||
-javadoc-build: Javadoc generation
|
||||
test-report: JUnit report generation
|
||||
|
||||
An example of overriding the target for project execution could look like this:
|
||||
|
||||
<target name="run" depends="XtreemFS-impl.jar">
|
||||
<exec dir="bin" executable="launcher.exe">
|
||||
<arg file="${dist.jar}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
Notice that the overridden target depends on the jar target and not only on
|
||||
the compile target as the regular run target does. Again, for a list of available
|
||||
properties which you can use, check the target you are overriding in the
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
-->
|
||||
|
||||
<!--
|
||||
This target is required by the BabuDB replication plugin which uses only a subset
|
||||
of the foundation code (the PBRPC communication infrastructure).
|
||||
The generated .jar file contains the required files of the subset.
|
||||
-->
|
||||
<target name="pbrpc-jar" depends="compile">
|
||||
<echo level="info">Creating PBRPC.jar.</echo>
|
||||
<!-- Create a new JAR. -->
|
||||
<jar destfile="dist/PBRPC.jar" basedir="build/classes">
|
||||
<include name="org/xtreemfs/foundation/pbrpc/**/*.class"/>
|
||||
<zipfileset dir="../../" includes="LICENSE" fullpath="/LICENSE"/>
|
||||
</jar>
|
||||
</target>
|
||||
</project>
|
||||
11
java/foundation/eclipse-project/.classpath
Normal file
11
java/foundation/eclipse-project/.classpath
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="test"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="../lib/protobuf-java-2.5.0.jar"/>
|
||||
<classpathentry kind="lib" path="../lib/test/junit-4.11.jar"/>
|
||||
<classpathentry kind="lib" path="../lib/test/hamcrest-core-1.3.jar"/>
|
||||
<classpathentry kind="lib" path="../lib/commons-codec-1.3.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
17
java/foundation/eclipse-project/.project
Normal file
17
java/foundation/eclipse-project/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>xtreemfs_foundation</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
682
java/foundation/nbproject/build-impl-1.6.5.xml
Normal file
682
java/foundation/nbproject/build-impl-1.6.5.xml
Normal file
@@ -0,0 +1,682 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
*** GENERATED FROM project.xml - DO NOT EDIT ***
|
||||
*** EDIT ../build.xml INSTEAD ***
|
||||
|
||||
For the purpose of easier reading the script
|
||||
is divided into following sections:
|
||||
|
||||
- initialization
|
||||
- compilation
|
||||
- jar
|
||||
- execution
|
||||
- debugging
|
||||
- javadoc
|
||||
- junit compilation
|
||||
- junit execution
|
||||
- junit debugging
|
||||
- applet
|
||||
- cleanup
|
||||
|
||||
-->
|
||||
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="XtreemFS-foundation-impl">
|
||||
|
||||
<target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
|
||||
<!--
|
||||
======================
|
||||
INITIALIZATION SECTION
|
||||
======================
|
||||
-->
|
||||
<target name="-pre-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="-pre-init" name="-init-private">
|
||||
<property file="nbproject/private/config.properties"/>
|
||||
<property file="nbproject/private/configs/${config}.properties"/>
|
||||
<property file="nbproject/private/private.properties"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private" name="-init-user">
|
||||
<property file="${user.properties.file}"/>
|
||||
<!-- The two properties below are usually overridden -->
|
||||
<!-- by the active platform. Just a fallback. -->
|
||||
<property name="default.javac.source" value="1.4"/>
|
||||
<property name="default.javac.target" value="1.4"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user" name="-init-project">
|
||||
<property file="nbproject/configs/${config}.properties"/>
|
||||
<property file="nbproject/project.properties"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
|
||||
<available file="${manifest.file}" property="manifest.available"/>
|
||||
<condition property="manifest.available+main.class">
|
||||
<and>
|
||||
<isset property="manifest.available"/>
|
||||
<isset property="main.class"/>
|
||||
<not>
|
||||
<equals arg1="${main.class}" arg2="" trim="true"/>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="manifest.available+main.class+mkdist.available">
|
||||
<and>
|
||||
<istrue value="${manifest.available+main.class}"/>
|
||||
<isset property="libs.CopyLibs.classpath"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="have.tests">
|
||||
<or>
|
||||
<available file="${test.src.dir}"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="have.sources">
|
||||
<or>
|
||||
<available file="${src.dir}"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="netbeans.home+have.tests">
|
||||
<and>
|
||||
<isset property="netbeans.home"/>
|
||||
<isset property="have.tests"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="no.javadoc.preview">
|
||||
<and>
|
||||
<isset property="javadoc.preview"/>
|
||||
<isfalse value="${javadoc.preview}"/>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="run.jvmargs" value=""/>
|
||||
<property name="javac.compilerargs" value=""/>
|
||||
<property name="work.dir" value="${basedir}"/>
|
||||
<condition property="no.deps">
|
||||
<and>
|
||||
<istrue value="${no.dependencies}"/>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="javac.debug" value="true"/>
|
||||
<property name="javadoc.preview" value="true"/>
|
||||
<property name="application.args" value=""/>
|
||||
<property name="source.encoding" value="${file.encoding}"/>
|
||||
<condition property="javadoc.encoding.used" value="${javadoc.encoding}">
|
||||
<and>
|
||||
<isset property="javadoc.encoding"/>
|
||||
<not>
|
||||
<equals arg1="${javadoc.encoding}" arg2=""/>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="javadoc.encoding.used" value="${source.encoding}"/>
|
||||
<property name="includes" value="**"/>
|
||||
<property name="excludes" value=""/>
|
||||
<property name="do.depend" value="false"/>
|
||||
<condition property="do.depend.true">
|
||||
<istrue value="${do.depend}"/>
|
||||
</condition>
|
||||
<condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
|
||||
<and>
|
||||
<isset property="jaxws.endorsed.dir"/>
|
||||
<available file="nbproject/jaxws-build.xml"/>
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
<target name="-post-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
|
||||
<fail unless="src.dir">Must set src.dir</fail>
|
||||
<fail unless="test.src.dir">Must set test.src.dir</fail>
|
||||
<fail unless="build.dir">Must set build.dir</fail>
|
||||
<fail unless="dist.dir">Must set dist.dir</fail>
|
||||
<fail unless="build.classes.dir">Must set build.classes.dir</fail>
|
||||
<fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
|
||||
<fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
|
||||
<fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
|
||||
<fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
|
||||
<fail unless="dist.jar">Must set dist.jar</fail>
|
||||
</target>
|
||||
<target name="-init-macrodef-property">
|
||||
<macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute name="name"/>
|
||||
<attribute name="value"/>
|
||||
<sequential>
|
||||
<property name="@{name}" value="${@{value}}"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-javac">
|
||||
<macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${src.dir}" name="srcdir"/>
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<attribute default="${javac.classpath}" name="classpath"/>
|
||||
<attribute default="${includes}" name="includes"/>
|
||||
<attribute default="${excludes}" name="excludes"/>
|
||||
<attribute default="${javac.debug}" name="debug"/>
|
||||
<attribute default="${empty.dir}" name="sourcepath"/>
|
||||
<attribute default="${empty.dir}" name="gensrcdir"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<property location="${build.dir}/empty" name="empty.dir"/>
|
||||
<mkdir dir="${empty.dir}"/>
|
||||
<mkdir dir="@{gensrcdir}"/>
|
||||
<javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
|
||||
<src>
|
||||
<dirset dir="@{gensrcdir}">
|
||||
<include name="*"/>
|
||||
</dirset>
|
||||
</src>
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
|
||||
<customize/>
|
||||
</javac>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${src.dir}" name="srcdir"/>
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<attribute default="${javac.classpath}" name="classpath"/>
|
||||
<sequential>
|
||||
<depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
</depend>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<sequential>
|
||||
<fail unless="javac.includes">Must set javac.includes</fail>
|
||||
<pathconvert pathsep="," property="javac.includes.binary">
|
||||
<path>
|
||||
<filelist dir="@{destdir}" files="${javac.includes}"/>
|
||||
</path>
|
||||
<globmapper from="*.java" to="*.class"/>
|
||||
</pathconvert>
|
||||
<delete>
|
||||
<files includes="${javac.includes.binary}"/>
|
||||
</delete>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-junit">
|
||||
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${includes}" name="includes"/>
|
||||
<attribute default="${excludes}" name="excludes"/>
|
||||
<attribute default="**" name="testincludes"/>
|
||||
<sequential>
|
||||
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
|
||||
<batchtest todir="${build.test.results.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
|
||||
<filename name="@{testincludes}"/>
|
||||
</fileset>
|
||||
</batchtest>
|
||||
<classpath>
|
||||
<path path="${run.test.classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<formatter type="brief" usefile="false"/>
|
||||
<formatter type="xml"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
</junit>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-nbjpda">
|
||||
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${main.class}" name="name"/>
|
||||
<attribute default="${debug.classpath}" name="classpath"/>
|
||||
<attribute default="" name="stopclassname"/>
|
||||
<sequential>
|
||||
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
</nbjpdastart>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${build.classes.dir}" name="dir"/>
|
||||
<sequential>
|
||||
<nbjpdareload>
|
||||
<fileset dir="@{dir}" includes="${fix.classes}">
|
||||
<include name="${fix.includes}*.class"/>
|
||||
</fileset>
|
||||
</nbjpdareload>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-debug-args">
|
||||
<property name="version-output" value="java version "${ant.java.version}"/>
|
||||
<condition property="have-jdk-older-than-1.4">
|
||||
<or>
|
||||
<contains string="${version-output}" substring="java version "1.0"/>
|
||||
<contains string="${version-output}" substring="java version "1.1"/>
|
||||
<contains string="${version-output}" substring="java version "1.2"/>
|
||||
<contains string="${version-output}" substring="java version "1.3"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
|
||||
<istrue value="${have-jdk-older-than-1.4}"/>
|
||||
</condition>
|
||||
</target>
|
||||
<target depends="-init-debug-args" name="-init-macrodef-debug">
|
||||
<macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${main.class}" name="classname"/>
|
||||
<attribute default="${debug.classpath}" name="classpath"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
||||
<jvmarg line="${debug-args-line}"/>
|
||||
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
|
||||
<jvmarg value="-Dfile.encoding=${source.encoding}"/>
|
||||
<redirector errorencoding="${source.encoding}" inputencoding="${source.encoding}" outputencoding="${source.encoding}"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-java">
|
||||
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${main.class}" name="classname"/>
|
||||
<attribute default="${run.classpath}" name="classpath"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
||||
<jvmarg value="-Dfile.encoding=${source.encoding}"/>
|
||||
<redirector errorencoding="${source.encoding}" inputencoding="${source.encoding}" outputencoding="${source.encoding}"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-presetdef-jar">
|
||||
<presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<jar compress="${jar.compress}" jarfile="${dist.jar}">
|
||||
<j2seproject1:fileset dir="${build.classes.dir}"/>
|
||||
</jar>
|
||||
</presetdef>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
|
||||
<!--
|
||||
===================
|
||||
COMPILATION SECTION
|
||||
===================
|
||||
-->
|
||||
<target depends="init" name="deps-jar" unless="no.deps"/>
|
||||
<target depends="init,deps-jar" name="-pre-pre-compile">
|
||||
<mkdir dir="${build.classes.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-compile">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target if="do.depend.true" name="-compile-depend">
|
||||
<pathconvert property="build.generated.subdirs">
|
||||
<dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
|
||||
<include name="*"/>
|
||||
</dirset>
|
||||
</pathconvert>
|
||||
<j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/>
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
|
||||
<j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
|
||||
<copy todir="${build.classes.dir}">
|
||||
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
|
||||
<target name="-pre-compile-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
|
||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
||||
<j2seproject3:force-recompile/>
|
||||
<j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/>
|
||||
</target>
|
||||
<target name="-post-compile-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
|
||||
<!--
|
||||
====================
|
||||
JAR BUILDING SECTION
|
||||
====================
|
||||
-->
|
||||
<target depends="init" name="-pre-pre-jar">
|
||||
<dirname file="${dist.jar}" property="dist.jar.dir"/>
|
||||
<mkdir dir="${dist.jar.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-jar">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
|
||||
<j2seproject1:jar/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
|
||||
<j2seproject1:jar manifest="${manifest.file}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
|
||||
<j2seproject1:jar manifest="${manifest.file}">
|
||||
<j2seproject1:manifest>
|
||||
<j2seproject1:attribute name="Main-Class" value="${main.class}"/>
|
||||
</j2seproject1:manifest>
|
||||
</j2seproject1:jar>
|
||||
<echo>To run this application from the command line without Ant, try:</echo>
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
||||
<pathconvert property="run.classpath.with.dist.jar">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
|
||||
</pathconvert>
|
||||
<echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to=""/>
|
||||
</pathconvert>
|
||||
<pathconvert pathsep=" " property="jar.classpath">
|
||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
||||
<chainedmapper>
|
||||
<flattenmapper/>
|
||||
<globmapper from="*" to="lib/*"/>
|
||||
</chainedmapper>
|
||||
</pathconvert>
|
||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="${main.class}"/>
|
||||
<attribute name="Class-Path" value="${jar.classpath}"/>
|
||||
</manifest>
|
||||
</copylibs>
|
||||
<echo>To run this application from the command line without Ant, try:</echo>
|
||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
||||
<echo>java -jar "${dist.jar.resolved}"</echo>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="libs.CopyLibs.classpath" name="-do-jar-with-libraries-without-manifest" unless="manifest.available+main.class">
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to=""/>
|
||||
</pathconvert>
|
||||
<pathconvert pathsep=" " property="jar.classpath">
|
||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
||||
<chainedmapper>
|
||||
<flattenmapper/>
|
||||
<globmapper from="*" to="lib/*"/>
|
||||
</chainedmapper>
|
||||
</pathconvert>
|
||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</copylibs>
|
||||
</target>
|
||||
<target name="-post-jar">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-do-jar-with-libraries-without-manifest,-post-jar" description="Build JAR." name="jar"/>
|
||||
<!--
|
||||
=================
|
||||
EXECUTION SECTION
|
||||
=================
|
||||
-->
|
||||
<target depends="init,compile" description="Run a main class." name="run">
|
||||
<j2seproject1:java>
|
||||
<customize>
|
||||
<arg line="${application.args}"/>
|
||||
</customize>
|
||||
</j2seproject1:java>
|
||||
</target>
|
||||
<target name="-do-not-recompile">
|
||||
<property name="javac.includes.binary" value=""/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-single" name="run-single">
|
||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
||||
<j2seproject1:java classname="${run.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single" name="run-test-with-main">
|
||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
||||
<j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
|
||||
</target>
|
||||
<!--
|
||||
=================
|
||||
DEBUGGING SECTION
|
||||
=================
|
||||
-->
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger">
|
||||
<j2seproject1:nbjpdastart name="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
|
||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init,compile" name="-debug-start-debuggee">
|
||||
<j2seproject3:debug>
|
||||
<customize>
|
||||
<arg line="${application.args}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
|
||||
<j2seproject1:nbjpdastart stopclassname="${main.class}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
|
||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
|
||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
||||
<j2seproject3:debug classname="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
|
||||
<target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
|
||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
||||
<j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
|
||||
<target depends="init" name="-pre-debug-fix">
|
||||
<fail unless="fix.includes">Must set fix.includes</fail>
|
||||
<property name="javac.includes" value="${fix.includes}.java"/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
|
||||
<j2seproject1:nbjpdareload/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
|
||||
<!--
|
||||
===============
|
||||
JAVADOC SECTION
|
||||
===============
|
||||
-->
|
||||
<target depends="init" name="-javadoc-build">
|
||||
<mkdir dir="${dist.javadoc.dir}"/>
|
||||
<javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
|
||||
<classpath>
|
||||
<path path="${javac.classpath}"/>
|
||||
</classpath>
|
||||
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
|
||||
<filename name="**/*.java"/>
|
||||
</fileset>
|
||||
<fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
|
||||
<include name="**/*.java"/>
|
||||
</fileset>
|
||||
</javadoc>
|
||||
</target>
|
||||
<target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
|
||||
<nbbrowse file="${dist.javadoc.dir}/index.html"/>
|
||||
</target>
|
||||
<target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
|
||||
<!--
|
||||
=========================
|
||||
JUNIT COMPILATION SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
|
||||
<mkdir dir="${build.test.classes.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-compile-test">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target if="do.depend.true" name="-compile-test-depend">
|
||||
<j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
|
||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
||||
<copy todir="${build.test.classes.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile-test">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
|
||||
<target name="-pre-compile-test-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
|
||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
||||
<j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
|
||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
|
||||
<copy todir="${build.test.classes.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile-test-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
|
||||
<!--
|
||||
=======================
|
||||
JUNIT EXECUTION SECTION
|
||||
=======================
|
||||
-->
|
||||
<target depends="init" if="have.tests" name="-pre-test-run">
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
|
||||
<j2seproject3:junit testincludes="**/*Test.java"/>
|
||||
</target>
|
||||
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
|
||||
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
|
||||
</target>
|
||||
<target depends="init" if="have.tests" name="test-report"/>
|
||||
<target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
|
||||
<target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
|
||||
<target depends="init" if="have.tests" name="-pre-test-run-single">
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
|
||||
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
|
||||
<j2seproject3:junit excludes="" includes="${test.includes}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
|
||||
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
|
||||
<!--
|
||||
=======================
|
||||
JUNIT DEBUGGING SECTION
|
||||
=======================
|
||||
-->
|
||||
<target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
|
||||
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
|
||||
<property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
|
||||
<delete file="${test.report.file}"/>
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
<j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
|
||||
<customize>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<arg value="${test.class}"/>
|
||||
<arg value="showoutput=true"/>
|
||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
|
||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
|
||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
|
||||
<target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
|
||||
<j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
|
||||
<!--
|
||||
=========================
|
||||
APPLET EXECUTION SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile-single" name="run-applet">
|
||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
||||
<j2seproject1:java classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</j2seproject1:java>
|
||||
</target>
|
||||
<!--
|
||||
=========================
|
||||
APPLET DEBUGGING SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
|
||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
||||
<j2seproject3:debug classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
|
||||
<!--
|
||||
===============
|
||||
CLEANUP SECTION
|
||||
===============
|
||||
-->
|
||||
<target depends="init" name="deps-clean" unless="no.deps"/>
|
||||
<target depends="init" name="-do-clean">
|
||||
<delete dir="${build.dir}"/>
|
||||
<delete dir="${dist.dir}"/>
|
||||
</target>
|
||||
<target name="-post-clean">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
|
||||
</project>
|
||||
687
java/foundation/nbproject/build-impl.xml
Normal file
687
java/foundation/nbproject/build-impl.xml
Normal file
@@ -0,0 +1,687 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
*** GENERATED FROM project.xml - DO NOT EDIT ***
|
||||
*** EDIT ../build.xml INSTEAD ***
|
||||
|
||||
For the purpose of easier reading the script
|
||||
is divided into following sections:
|
||||
|
||||
- initialization
|
||||
- compilation
|
||||
- jar
|
||||
- execution
|
||||
- debugging
|
||||
- javadoc
|
||||
- junit compilation
|
||||
- junit execution
|
||||
- junit debugging
|
||||
- applet
|
||||
- cleanup
|
||||
|
||||
-->
|
||||
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="XtreemFS-foundation-impl">
|
||||
<fail message="Please build using Ant 1.7.1 or higher.">
|
||||
<condition>
|
||||
<not>
|
||||
<antversion atleast="1.7.1"/>
|
||||
</not>
|
||||
</condition>
|
||||
</fail>
|
||||
<target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
|
||||
<!--
|
||||
======================
|
||||
INITIALIZATION SECTION
|
||||
======================
|
||||
-->
|
||||
<target name="-pre-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="-pre-init" name="-init-private">
|
||||
<property file="nbproject/private/config.properties"/>
|
||||
<property file="nbproject/private/configs/${config}.properties"/>
|
||||
<property file="nbproject/private/private.properties"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private" name="-init-user">
|
||||
<property file="${user.properties.file}"/>
|
||||
<!-- The two properties below are usually overridden -->
|
||||
<!-- by the active platform. Just a fallback. -->
|
||||
<property name="default.javac.source" value="1.4"/>
|
||||
<property name="default.javac.target" value="1.4"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user" name="-init-project">
|
||||
<property file="nbproject/configs/${config}.properties"/>
|
||||
<property file="nbproject/project.properties"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
|
||||
<available file="${manifest.file}" property="manifest.available"/>
|
||||
<condition property="manifest.available+main.class">
|
||||
<and>
|
||||
<isset property="manifest.available"/>
|
||||
<isset property="main.class"/>
|
||||
<not>
|
||||
<equals arg1="${main.class}" arg2="" trim="true"/>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="manifest.available+main.class+mkdist.available">
|
||||
<and>
|
||||
<istrue value="${manifest.available+main.class}"/>
|
||||
<isset property="libs.CopyLibs.classpath"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="have.tests">
|
||||
<or>
|
||||
<available file="${test.src.dir}"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="have.sources">
|
||||
<or>
|
||||
<available file="${src.dir}"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="netbeans.home+have.tests">
|
||||
<and>
|
||||
<isset property="netbeans.home"/>
|
||||
<isset property="have.tests"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="no.javadoc.preview">
|
||||
<and>
|
||||
<isset property="javadoc.preview"/>
|
||||
<isfalse value="${javadoc.preview}"/>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="run.jvmargs" value=""/>
|
||||
<property name="javac.compilerargs" value=""/>
|
||||
<property name="work.dir" value="${basedir}"/>
|
||||
<condition property="no.deps">
|
||||
<and>
|
||||
<istrue value="${no.dependencies}"/>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="javac.debug" value="true"/>
|
||||
<property name="javadoc.preview" value="true"/>
|
||||
<property name="application.args" value=""/>
|
||||
<property name="source.encoding" value="${file.encoding}"/>
|
||||
<condition property="javadoc.encoding.used" value="${javadoc.encoding}">
|
||||
<and>
|
||||
<isset property="javadoc.encoding"/>
|
||||
<not>
|
||||
<equals arg1="${javadoc.encoding}" arg2=""/>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="javadoc.encoding.used" value="${source.encoding}"/>
|
||||
<property name="includes" value="**"/>
|
||||
<property name="excludes" value=""/>
|
||||
<property name="do.depend" value="false"/>
|
||||
<condition property="do.depend.true">
|
||||
<istrue value="${do.depend}"/>
|
||||
</condition>
|
||||
<condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
|
||||
<and>
|
||||
<isset property="jaxws.endorsed.dir"/>
|
||||
<available file="nbproject/jaxws-build.xml"/>
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
<target name="-post-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
|
||||
<fail unless="src.dir">Must set src.dir</fail>
|
||||
<fail unless="test.src.dir">Must set test.src.dir</fail>
|
||||
<fail unless="build.dir">Must set build.dir</fail>
|
||||
<fail unless="dist.dir">Must set dist.dir</fail>
|
||||
<fail unless="build.classes.dir">Must set build.classes.dir</fail>
|
||||
<fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
|
||||
<fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
|
||||
<fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
|
||||
<fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
|
||||
<fail unless="dist.jar">Must set dist.jar</fail>
|
||||
</target>
|
||||
<target name="-init-macrodef-property">
|
||||
<macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute name="name"/>
|
||||
<attribute name="value"/>
|
||||
<sequential>
|
||||
<property name="@{name}" value="${@{value}}"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-javac">
|
||||
<macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${src.dir}" name="srcdir"/>
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<attribute default="${javac.classpath}" name="classpath"/>
|
||||
<attribute default="${includes}" name="includes"/>
|
||||
<attribute default="${excludes}" name="excludes"/>
|
||||
<attribute default="${javac.debug}" name="debug"/>
|
||||
<attribute default="${empty.dir}" name="sourcepath"/>
|
||||
<attribute default="${empty.dir}" name="gensrcdir"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<property location="${build.dir}/empty" name="empty.dir"/>
|
||||
<mkdir dir="${empty.dir}"/>
|
||||
<javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
|
||||
<src>
|
||||
<dirset dir="@{gensrcdir}" erroronmissingdir="false">
|
||||
<include name="*"/>
|
||||
</dirset>
|
||||
</src>
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
|
||||
<customize/>
|
||||
</javac>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${src.dir}" name="srcdir"/>
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<attribute default="${javac.classpath}" name="classpath"/>
|
||||
<sequential>
|
||||
<depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
</depend>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<sequential>
|
||||
<fail unless="javac.includes">Must set javac.includes</fail>
|
||||
<pathconvert pathsep="," property="javac.includes.binary">
|
||||
<path>
|
||||
<filelist dir="@{destdir}" files="${javac.includes}"/>
|
||||
</path>
|
||||
<globmapper from="*.java" to="*.class"/>
|
||||
</pathconvert>
|
||||
<delete>
|
||||
<files includes="${javac.includes.binary}"/>
|
||||
</delete>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-junit">
|
||||
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${includes}" name="includes"/>
|
||||
<attribute default="${excludes}" name="excludes"/>
|
||||
<attribute default="**" name="testincludes"/>
|
||||
<sequential>
|
||||
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
|
||||
<batchtest todir="${build.test.results.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
|
||||
<filename name="@{testincludes}"/>
|
||||
</fileset>
|
||||
</batchtest>
|
||||
<classpath>
|
||||
<path path="${run.test.classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<formatter type="brief" usefile="false"/>
|
||||
<formatter type="xml"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
</junit>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-nbjpda">
|
||||
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${main.class}" name="name"/>
|
||||
<attribute default="${debug.classpath}" name="classpath"/>
|
||||
<attribute default="" name="stopclassname"/>
|
||||
<sequential>
|
||||
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
</nbjpdastart>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${build.classes.dir}" name="dir"/>
|
||||
<sequential>
|
||||
<nbjpdareload>
|
||||
<fileset dir="@{dir}" includes="${fix.classes}">
|
||||
<include name="${fix.includes}*.class"/>
|
||||
</fileset>
|
||||
</nbjpdareload>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-debug-args">
|
||||
<property name="version-output" value="java version "${ant.java.version}"/>
|
||||
<condition property="have-jdk-older-than-1.4">
|
||||
<or>
|
||||
<contains string="${version-output}" substring="java version "1.0"/>
|
||||
<contains string="${version-output}" substring="java version "1.1"/>
|
||||
<contains string="${version-output}" substring="java version "1.2"/>
|
||||
<contains string="${version-output}" substring="java version "1.3"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
|
||||
<istrue value="${have-jdk-older-than-1.4}"/>
|
||||
</condition>
|
||||
</target>
|
||||
<target depends="-init-debug-args" name="-init-macrodef-debug">
|
||||
<macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${main.class}" name="classname"/>
|
||||
<attribute default="${debug.classpath}" name="classpath"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
||||
<jvmarg line="${debug-args-line}"/>
|
||||
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
|
||||
<jvmarg value="-Dfile.encoding=${source.encoding}"/>
|
||||
<redirector errorencoding="${source.encoding}" inputencoding="${source.encoding}" outputencoding="${source.encoding}"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-java">
|
||||
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${main.class}" name="classname"/>
|
||||
<attribute default="${run.classpath}" name="classpath"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
||||
<jvmarg value="-Dfile.encoding=${source.encoding}"/>
|
||||
<redirector errorencoding="${source.encoding}" inputencoding="${source.encoding}" outputencoding="${source.encoding}"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-presetdef-jar">
|
||||
<presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<jar compress="${jar.compress}" jarfile="${dist.jar}">
|
||||
<j2seproject1:fileset dir="${build.classes.dir}"/>
|
||||
</jar>
|
||||
</presetdef>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
|
||||
<!--
|
||||
===================
|
||||
COMPILATION SECTION
|
||||
===================
|
||||
-->
|
||||
<target depends="init" name="deps-jar" unless="no.deps"/>
|
||||
<target depends="init,deps-jar" name="-pre-pre-compile">
|
||||
<mkdir dir="${build.classes.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-compile">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target if="do.depend.true" name="-compile-depend">
|
||||
<pathconvert property="build.generated.subdirs">
|
||||
<dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
|
||||
<include name="*"/>
|
||||
</dirset>
|
||||
</pathconvert>
|
||||
<j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/>
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
|
||||
<j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
|
||||
<copy todir="${build.classes.dir}">
|
||||
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
|
||||
<target name="-pre-compile-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
|
||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
||||
<j2seproject3:force-recompile/>
|
||||
<j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/>
|
||||
</target>
|
||||
<target name="-post-compile-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
|
||||
<!--
|
||||
====================
|
||||
JAR BUILDING SECTION
|
||||
====================
|
||||
-->
|
||||
<target depends="init" name="-pre-pre-jar">
|
||||
<dirname file="${dist.jar}" property="dist.jar.dir"/>
|
||||
<mkdir dir="${dist.jar.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-jar">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
|
||||
<j2seproject1:jar/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
|
||||
<j2seproject1:jar manifest="${manifest.file}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
|
||||
<j2seproject1:jar manifest="${manifest.file}">
|
||||
<j2seproject1:manifest>
|
||||
<j2seproject1:attribute name="Main-Class" value="${main.class}"/>
|
||||
</j2seproject1:manifest>
|
||||
</j2seproject1:jar>
|
||||
<echo>To run this application from the command line without Ant, try:</echo>
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
||||
<pathconvert property="run.classpath.with.dist.jar">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
|
||||
</pathconvert>
|
||||
<echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to=""/>
|
||||
</pathconvert>
|
||||
<pathconvert pathsep=" " property="jar.classpath">
|
||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
||||
<chainedmapper>
|
||||
<flattenmapper/>
|
||||
<globmapper from="*" to="lib/*"/>
|
||||
</chainedmapper>
|
||||
</pathconvert>
|
||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="${main.class}"/>
|
||||
<attribute name="Class-Path" value="${jar.classpath}"/>
|
||||
</manifest>
|
||||
</copylibs>
|
||||
<echo>To run this application from the command line without Ant, try:</echo>
|
||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
||||
<echo>java -jar "${dist.jar.resolved}"</echo>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="libs.CopyLibs.classpath" name="-do-jar-with-libraries-without-manifest" unless="manifest.available+main.class">
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to=""/>
|
||||
</pathconvert>
|
||||
<pathconvert pathsep=" " property="jar.classpath">
|
||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
||||
<chainedmapper>
|
||||
<flattenmapper/>
|
||||
<globmapper from="*" to="lib/*"/>
|
||||
</chainedmapper>
|
||||
</pathconvert>
|
||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</copylibs>
|
||||
</target>
|
||||
<target name="-post-jar">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-do-jar-with-libraries-without-manifest,-post-jar" description="Build JAR." name="jar"/>
|
||||
<!--
|
||||
=================
|
||||
EXECUTION SECTION
|
||||
=================
|
||||
-->
|
||||
<target depends="init,compile" description="Run a main class." name="run">
|
||||
<j2seproject1:java>
|
||||
<customize>
|
||||
<arg line="${application.args}"/>
|
||||
</customize>
|
||||
</j2seproject1:java>
|
||||
</target>
|
||||
<target name="-do-not-recompile">
|
||||
<property name="javac.includes.binary" value=""/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-single" name="run-single">
|
||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
||||
<j2seproject1:java classname="${run.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single" name="run-test-with-main">
|
||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
||||
<j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
|
||||
</target>
|
||||
<!--
|
||||
=================
|
||||
DEBUGGING SECTION
|
||||
=================
|
||||
-->
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger">
|
||||
<j2seproject1:nbjpdastart name="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
|
||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init,compile" name="-debug-start-debuggee">
|
||||
<j2seproject3:debug>
|
||||
<customize>
|
||||
<arg line="${application.args}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
|
||||
<j2seproject1:nbjpdastart stopclassname="${main.class}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
|
||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
|
||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
||||
<j2seproject3:debug classname="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
|
||||
<target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
|
||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
||||
<j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
|
||||
<target depends="init" name="-pre-debug-fix">
|
||||
<fail unless="fix.includes">Must set fix.includes</fail>
|
||||
<property name="javac.includes" value="${fix.includes}.java"/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
|
||||
<j2seproject1:nbjpdareload/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
|
||||
<!--
|
||||
===============
|
||||
JAVADOC SECTION
|
||||
===============
|
||||
-->
|
||||
<target depends="init" name="-javadoc-build">
|
||||
<mkdir dir="${dist.javadoc.dir}"/>
|
||||
<javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
|
||||
<classpath>
|
||||
<path path="${javac.classpath}"/>
|
||||
</classpath>
|
||||
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
|
||||
<filename name="**/*.java"/>
|
||||
</fileset>
|
||||
<fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
|
||||
<include name="**/*.java"/>
|
||||
</fileset>
|
||||
</javadoc>
|
||||
</target>
|
||||
<target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
|
||||
<nbbrowse file="${dist.javadoc.dir}/index.html"/>
|
||||
</target>
|
||||
<target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
|
||||
<!--
|
||||
=========================
|
||||
JUNIT COMPILATION SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
|
||||
<mkdir dir="${build.test.classes.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-compile-test">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target if="do.depend.true" name="-compile-test-depend">
|
||||
<j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
|
||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
||||
<copy todir="${build.test.classes.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile-test">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
|
||||
<target name="-pre-compile-test-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
|
||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
||||
<j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
|
||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
|
||||
<copy todir="${build.test.classes.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile-test-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
|
||||
<!--
|
||||
=======================
|
||||
JUNIT EXECUTION SECTION
|
||||
=======================
|
||||
-->
|
||||
<target depends="init" if="have.tests" name="-pre-test-run">
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
|
||||
<j2seproject3:junit testincludes="**/*Test.java"/>
|
||||
</target>
|
||||
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
|
||||
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
|
||||
</target>
|
||||
<target depends="init" if="have.tests" name="test-report"/>
|
||||
<target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
|
||||
<target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
|
||||
<target depends="init" if="have.tests" name="-pre-test-run-single">
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
|
||||
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
|
||||
<j2seproject3:junit excludes="" includes="${test.includes}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
|
||||
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
|
||||
<!--
|
||||
=======================
|
||||
JUNIT DEBUGGING SECTION
|
||||
=======================
|
||||
-->
|
||||
<target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
|
||||
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
|
||||
<property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
|
||||
<delete file="${test.report.file}"/>
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
<j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
|
||||
<customize>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<arg value="${test.class}"/>
|
||||
<arg value="showoutput=true"/>
|
||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
|
||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
|
||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
|
||||
<target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
|
||||
<j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
|
||||
<!--
|
||||
=========================
|
||||
APPLET EXECUTION SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile-single" name="run-applet">
|
||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
||||
<j2seproject1:java classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</j2seproject1:java>
|
||||
</target>
|
||||
<!--
|
||||
=========================
|
||||
APPLET DEBUGGING SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
|
||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
||||
<j2seproject3:debug classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
|
||||
<!--
|
||||
===============
|
||||
CLEANUP SECTION
|
||||
===============
|
||||
-->
|
||||
<target depends="init" name="deps-clean" unless="no.deps"/>
|
||||
<target depends="init" name="-do-clean">
|
||||
<delete dir="${build.dir}"/>
|
||||
<delete dir="${dist.dir}"/>
|
||||
</target>
|
||||
<target name="-post-clean">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
|
||||
</project>
|
||||
11
java/foundation/nbproject/genfiles.properties
Normal file
11
java/foundation/nbproject/genfiles.properties
Normal file
@@ -0,0 +1,11 @@
|
||||
build.xml.data.CRC32=4a9eff70
|
||||
build.xml.script.CRC32=ce2ddeb0
|
||||
build.xml.stylesheet.CRC32=958a1d3e
|
||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||
nbproject/build-impl.xml.data.CRC32=beaaf17a
|
||||
nbproject/build-impl.xml.script.CRC32=3e4625c6
|
||||
nbproject/build-impl.xml.stylesheet.CRC32=78c6a6ee@1.38.1.45
|
||||
nbproject/profiler-build-impl.xml.data.CRC32=4a9eff70
|
||||
nbproject/profiler-build-impl.xml.script.CRC32=abda56ed
|
||||
nbproject/profiler-build-impl.xml.stylesheet.CRC32=42cb6bcf
|
||||
131
java/foundation/nbproject/profiler-build-impl.xml
Normal file
131
java/foundation/nbproject/profiler-build-impl.xml
Normal file
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
*** GENERATED FROM project.xml - DO NOT EDIT ***
|
||||
*** EDIT ../build.xml INSTEAD ***
|
||||
|
||||
For the purpose of easier reading the script
|
||||
is divided into following sections:
|
||||
|
||||
- initialization
|
||||
- profiling
|
||||
- applet profiling
|
||||
|
||||
-->
|
||||
<project name="-profiler-impl" default="profile" basedir="..">
|
||||
<target name="default" depends="profile" description="Build and profile the project."/>
|
||||
<!--
|
||||
======================
|
||||
INITIALIZATION SECTION
|
||||
======================
|
||||
-->
|
||||
<target name="profile-init" depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check"/>
|
||||
<target name="-profile-pre-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target name="-profile-post-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target name="-profile-init-macrodef-profile">
|
||||
<macrodef name="resolve">
|
||||
<attribute name="name"/>
|
||||
<attribute name="value"/>
|
||||
<sequential>
|
||||
<property name="@{name}" value="${env.@{value}}"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="profile">
|
||||
<attribute name="classname" default="${main.class}"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<property environment="env"/>
|
||||
<resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
|
||||
<java fork="true" classname="@{classname}" dir="${profiler.info.dir}" jvm="${profiler.info.jvm}">
|
||||
<jvmarg value="${profiler.info.jvmargs.agent}"/>
|
||||
<jvmarg line="${profiler.info.jvmargs}"/>
|
||||
<env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
|
||||
<arg line="${application.args}"/>
|
||||
<classpath>
|
||||
<path path="${run.classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper type="glob" from="run-sys-prop.*" to="*"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-profile-init-check" depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile">
|
||||
<fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
|
||||
<fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
|
||||
</target>
|
||||
<!--
|
||||
=================
|
||||
PROFILING SECTION
|
||||
=================
|
||||
-->
|
||||
<target name="profile" if="netbeans.home" depends="profile-init,compile" description="Profile a project in the IDE.">
|
||||
<nbprofiledirect>
|
||||
<classpath>
|
||||
<path path="${run.classpath}"/>
|
||||
</classpath>
|
||||
</nbprofiledirect>
|
||||
<profile/>
|
||||
</target>
|
||||
<target name="profile-single" if="netbeans.home" depends="profile-init,compile-single" description="Profile a selected class in the IDE.">
|
||||
<fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
|
||||
<nbprofiledirect>
|
||||
<classpath>
|
||||
<path path="${run.classpath}"/>
|
||||
</classpath>
|
||||
</nbprofiledirect>
|
||||
<profile classname="${profile.class}"/>
|
||||
</target>
|
||||
<!--
|
||||
=========================
|
||||
APPLET PROFILING SECTION
|
||||
=========================
|
||||
-->
|
||||
<target name="profile-applet" if="netbeans.home" depends="profile-init,compile-single">
|
||||
<nbprofiledirect>
|
||||
<classpath>
|
||||
<path path="${run.classpath}"/>
|
||||
</classpath>
|
||||
</nbprofiledirect>
|
||||
<profile classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</profile>
|
||||
</target>
|
||||
<!--
|
||||
=========================
|
||||
TESTS PROFILING SECTION
|
||||
=========================
|
||||
-->
|
||||
<target name="profile-test-single" if="netbeans.home" depends="profile-init,compile-test-single">
|
||||
<nbprofiledirect>
|
||||
<classpath>
|
||||
<path path="${run.test.classpath}"/>
|
||||
</classpath>
|
||||
</nbprofiledirect>
|
||||
<junit showoutput="true" fork="true" dir="${profiler.info.dir}" jvm="${profiler.info.jvm}" failureproperty="tests.failed" errorproperty="tests.failed">
|
||||
<env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
|
||||
<jvmarg value="${profiler.info.jvmargs.agent}"/>
|
||||
<jvmarg line="${profiler.info.jvmargs}"/>
|
||||
<test name="${profile.class}"/>
|
||||
<classpath>
|
||||
<path path="${run.test.classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper type="glob" from="test-sys-prop.*" to="*"/>
|
||||
</syspropertyset>
|
||||
<formatter type="brief" usefile="false"/>
|
||||
<formatter type="xml"/>
|
||||
</junit>
|
||||
</target>
|
||||
</project>
|
||||
82
java/foundation/nbproject/project.properties
Normal file
82
java/foundation/nbproject/project.properties
Normal file
@@ -0,0 +1,82 @@
|
||||
annotation.processing.enabled=true
|
||||
annotation.processing.enabled.in.editor=false
|
||||
annotation.processing.run.all.processors=true
|
||||
application.args=
|
||||
application.title=Foundation
|
||||
application.vendor=flangner
|
||||
build.classes.dir=${build.dir}/classes
|
||||
build.classes.excludes=**/*.java,**/*.form
|
||||
# This directory is removed when the project is cleaned:
|
||||
build.dir=build
|
||||
build.generated.dir=${build.dir}/generated
|
||||
build.generated.sources.dir=${build.dir}/generated-sources
|
||||
# Only compile against the classpath explicitly listed here:
|
||||
build.sysclasspath=ignore
|
||||
build.test.classes.dir=${build.dir}/test/classes
|
||||
build.test.results.dir=${build.dir}/test/results
|
||||
debug.classpath=\
|
||||
${run.classpath}
|
||||
debug.test.classpath=\
|
||||
${run.test.classpath}
|
||||
# This directory is removed when the project is cleaned:
|
||||
dist.dir=dist
|
||||
dist.jar=${dist.dir}/Foundation.jar
|
||||
dist.javadoc.dir=${dist.dir}/javadoc
|
||||
endorsed.classpath=
|
||||
excludes=
|
||||
file.reference.bcprov-jdk16-139.jar=lib/bcprov-jdk16-139.jar
|
||||
file.reference.cdaclient.jar=lib/cdaclient.jar
|
||||
file.reference.junit-4.11.jar=../lib/test/junit-4.11.jar
|
||||
file.reference.config.jar=lib/config.jar
|
||||
file.reference.je-3.2.13.jar=lib/je-3.2.13.jar
|
||||
file.reference.protobuf-java-2.5.0.jar=../lib/protobuf-java-2.5.0.jar
|
||||
file.reference.xbean.jar=lib/xbean.jar
|
||||
file.reference.commons-codec-1.3.jar=../lib/commons-codec-1.3.jar
|
||||
includes=**
|
||||
jar.compress=true
|
||||
javac.classpath=\
|
||||
${file.reference.commons-codec-1.3.jar}:\
|
||||
${file.reference.protobuf-java-2.5.0.jar}
|
||||
# Space-separated list of extra javac options
|
||||
javac.compilerargs=
|
||||
javac.deprecation=false
|
||||
javac.processorpath=\
|
||||
${javac.classpath}
|
||||
javac.source=1.6
|
||||
javac.target=1.6
|
||||
javac.test.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}:\
|
||||
${libs.junit_4.classpath}
|
||||
javadoc.additionalparam=
|
||||
javadoc.author=false
|
||||
javadoc.encoding=
|
||||
javadoc.noindex=false
|
||||
javadoc.nonavbar=false
|
||||
javadoc.notree=false
|
||||
javadoc.private=false
|
||||
javadoc.splitindex=true
|
||||
javadoc.use=true
|
||||
javadoc.version=false
|
||||
javadoc.windowtitle=
|
||||
jnlp.codebase.type=local
|
||||
jnlp.enabled=false
|
||||
jnlp.offline-allowed=false
|
||||
jnlp.signed=false
|
||||
main.class=
|
||||
manifest.file=manifest.mf
|
||||
meta.inf.dir=${src.dir}/META-INF
|
||||
mkdist.disabled=false
|
||||
platform.active=default_platform
|
||||
run.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}
|
||||
# Space-separated list of JVM arguments used when running the project
|
||||
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
|
||||
# or test-sys-prop.name=value to set system properties for unit tests):
|
||||
run.jvmargs=-ea
|
||||
run.test.classpath=\
|
||||
${javac.test.classpath}:\
|
||||
${build.test.classes.dir}
|
||||
src.dir=src
|
||||
test.src.dir=test
|
||||
22
java/foundation/nbproject/project.xml
Normal file
22
java/foundation/nbproject/project.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||
<type>org.netbeans.modules.java.j2seproject</type>
|
||||
<configuration>
|
||||
<buildExtensions xmlns="http://www.netbeans.org/ns/ant-build-extender/1">
|
||||
<extension file="protobuf-build.xml" id="protobuf">
|
||||
<dependency dependsOn="protobuf-code-generation" target="-pre-pre-compile"/>
|
||||
</extension>
|
||||
</buildExtensions>
|
||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<name>XtreemFS-foundation</name>
|
||||
<minimum-ant-version>1.6.5</minimum-ant-version>
|
||||
<source-roots>
|
||||
<root id="src.dir"/>
|
||||
</source-roots>
|
||||
<test-roots>
|
||||
<root id="test.src.dir"/>
|
||||
</test-roots>
|
||||
</data>
|
||||
<references xmlns="http://www.netbeans.org/ns/ant-project-references/1"/>
|
||||
</configuration>
|
||||
</project>
|
||||
154
java/foundation/src/org/xtreemfs/foundation/ClientLease.java
Normal file
154
java/foundation/src/org/xtreemfs/foundation/ClientLease.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public final class ClientLease implements Cloneable {
|
||||
|
||||
/**
|
||||
* Default time span for the client lease validity.
|
||||
* Must be smaller than a intra-OSD lease, if replication is
|
||||
* active!
|
||||
*/
|
||||
public static final long LEASE_VALIDITY = 15000;
|
||||
|
||||
/**
|
||||
* Indicates that a lease spans to EOF "append lease".
|
||||
* a lease from 0 to -1 spans the whole file, even if data is appended.
|
||||
*/
|
||||
public static final long TO_EOF = -1;
|
||||
|
||||
/**
|
||||
* timestamp when the lease expires
|
||||
*/
|
||||
private long firstObject;
|
||||
/**
|
||||
* last object the lease is valid for
|
||||
*/
|
||||
private long lastObject;
|
||||
|
||||
/**
|
||||
* UUID of the client owning the lease
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* timestamp when the lease expires (in seconds since 01/01/70)
|
||||
* must be XtreemFS global time!
|
||||
*/
|
||||
private long expires;
|
||||
|
||||
|
||||
/**
|
||||
* fileId this lease was issued for
|
||||
*/
|
||||
private final String fileId;
|
||||
|
||||
/**
|
||||
* sequenceNo, used to generate unique leaseId = fileId+"/"+sequenceNo
|
||||
*/
|
||||
private long sequenceNo;
|
||||
|
||||
/**
|
||||
* lease type/operation
|
||||
*/
|
||||
private String operation;
|
||||
|
||||
public static final String EXCLUSIVE_LEASE = "w";
|
||||
|
||||
|
||||
public ClientLease(final String fileId) {
|
||||
this.fileId = fileId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if two leases have conflicting (i.e. overlapping ranges)
|
||||
* @param other other lease for the same file
|
||||
* @return true, if there is an overlap in the ranges
|
||||
*/
|
||||
public boolean isConflicting(ClientLease other) {
|
||||
//checks
|
||||
if ( ((this.lastObject < other.firstObject) && (this.lastObject != TO_EOF)) ||
|
||||
((other.lastObject < this.firstObject) && (other.lastObject != TO_EOF)) ) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientLease clone() {
|
||||
ClientLease l = new ClientLease(this.fileId);
|
||||
l.clientId = this.clientId;
|
||||
l.expires = this.expires;
|
||||
l.firstObject = this.firstObject;
|
||||
l.lastObject = this.lastObject;
|
||||
l.operation = this.operation;
|
||||
l.sequenceNo = this.sequenceNo;
|
||||
return l;
|
||||
}
|
||||
|
||||
public long getFirstObject() {
|
||||
return firstObject;
|
||||
}
|
||||
|
||||
public void setFirstObject(long firstObject) {
|
||||
this.firstObject = firstObject;
|
||||
}
|
||||
|
||||
public long getLastObject() {
|
||||
return lastObject;
|
||||
}
|
||||
|
||||
public void setLastObject(long lastObject) {
|
||||
this.lastObject = lastObject;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public long getExpires() {
|
||||
return expires;
|
||||
}
|
||||
|
||||
public void setExpires(long expires) {
|
||||
this.expires = expires;
|
||||
}
|
||||
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public long getSequenceNo() {
|
||||
return sequenceNo;
|
||||
}
|
||||
|
||||
public void setSequenceNo(long sequenceNo) {
|
||||
this.sequenceNo = sequenceNo;
|
||||
}
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public void setOperation(String operation) {
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
102
java/foundation/src/org/xtreemfs/foundation/CrashReporter.java
Normal file
102
java/foundation/src/org/xtreemfs/foundation/CrashReporter.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class CrashReporter {
|
||||
|
||||
public static void reportXtreemFSCrash(String report) {
|
||||
/*try {
|
||||
URL u = new URL("http://www.xtreemfs.org/dump/dump.php?srv=server");
|
||||
HttpURLConnection con = (HttpURLConnection) u.openConnection();
|
||||
con.setRequestMethod("PUT");
|
||||
con.setDoOutput(true);
|
||||
con.connect();
|
||||
OutputStream os = con.getOutputStream();
|
||||
os.write(report.getBytes());
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
InputStream is = con.getInputStream();
|
||||
is.available();
|
||||
is.close();
|
||||
} catch (Throwable th) {
|
||||
System.out.println("cannot send crash report: "+th);
|
||||
}*/
|
||||
}
|
||||
|
||||
public static String createCrashReport(String service, String version, Throwable cause) {
|
||||
try {
|
||||
StringBuilder report = new StringBuilder();
|
||||
report.append("----------------------------------------------------------------\n");
|
||||
report.append("We are sorry, but your "+service+" has crashed. To report this bug\n");
|
||||
report.append("please go to http://www.xtreemfs.org and file an issue and attach\n");
|
||||
report.append("this crash report.\n\n");
|
||||
report.append("service: ");
|
||||
report.append(service);
|
||||
report.append(" version: ");
|
||||
report.append(version);
|
||||
report.append("\n");
|
||||
report.append("JVM version: ");
|
||||
report.append(System.getProperty("java.version"));
|
||||
report.append(" ");
|
||||
report.append(System.getProperty("java.vendor"));
|
||||
report.append(" on ");
|
||||
report.append(System.getProperty("os.name"));
|
||||
report.append(" ");
|
||||
report.append(System.getProperty("os.version"));
|
||||
report.append("\n");
|
||||
report.append("exception: ");
|
||||
report.append(cause.toString());
|
||||
report.append("\n");
|
||||
for (StackTraceElement elem : cause.getStackTrace()) {
|
||||
report.append(elem.toString());
|
||||
report.append("\n");
|
||||
}
|
||||
if (cause.getCause() != null) {
|
||||
report.append("\nroot cause: ");
|
||||
report.append(cause.getCause());
|
||||
report.append("\n");
|
||||
for (StackTraceElement elem : cause.getCause().getStackTrace()) {
|
||||
report.append(elem.toString());
|
||||
report.append("\n");
|
||||
}
|
||||
}
|
||||
reportThreadStates(report);
|
||||
|
||||
report.append("----------------------------------------------------------------\n");
|
||||
return report.toString();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return "Could not write crash report for: "+service+","+version+","+cause+" due to "+ex;
|
||||
}
|
||||
}
|
||||
|
||||
/** Logs the stack trace of each thread into {@code report}. */
|
||||
public static void reportThreadStates(StringBuilder report) {
|
||||
report.append("\n--- THREAD STATES ---\n");
|
||||
final Map<Thread,StackTraceElement[]> traces = Thread.getAllStackTraces();
|
||||
for (Thread t : traces.keySet()) {
|
||||
report.append("thread: ");
|
||||
report.append(t.getName());
|
||||
report.append("\n");
|
||||
for (StackTraceElement e : traces.get(t)) {
|
||||
report.append(e.toString());
|
||||
report.append("\n");
|
||||
}
|
||||
report.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
33
java/foundation/src/org/xtreemfs/foundation/LRUCache.java
Normal file
33
java/foundation/src/org/xtreemfs/foundation/LRUCache.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Jan Stender, Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class implements a LRU cache
|
||||
*
|
||||
* @author jmalo
|
||||
*/
|
||||
public class LRUCache<K,V> extends LinkedHashMap<K,V> {
|
||||
private static final long serialVersionUID = -4673214355284364245L;
|
||||
private int maximumSize;
|
||||
|
||||
/** Creates a new instance of LRUCache */
|
||||
public LRUCache(int size) {
|
||||
super(size, (float)0.75, true);
|
||||
|
||||
maximumSize = size;
|
||||
}
|
||||
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||
return size() > maximumSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
/**
|
||||
* Notifies a process of a life cycle event.
|
||||
*
|
||||
* @author stender
|
||||
*
|
||||
*/
|
||||
public interface LifeCycleListener {
|
||||
|
||||
public void startupPerformed();
|
||||
|
||||
public void shutdownPerformed();
|
||||
|
||||
public void crashPerformed(Throwable cause);
|
||||
|
||||
}
|
||||
167
java/foundation/src/org/xtreemfs/foundation/LifeCycleThread.java
Normal file
167
java/foundation/src/org/xtreemfs/foundation/LifeCycleThread.java
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
|
||||
/**
|
||||
* A base class for threads representing a life cycle. It offers methods for
|
||||
* blocking other threads until a certain life cycle event has occurred. It
|
||||
* currently supports two life cycle-related events: startup and shutdown.
|
||||
*
|
||||
* @author stender
|
||||
*
|
||||
*/
|
||||
public class LifeCycleThread extends Thread {
|
||||
|
||||
private final Object startLock;
|
||||
|
||||
private final Object stopLock;
|
||||
|
||||
private boolean started;
|
||||
|
||||
private boolean stopped;
|
||||
|
||||
private Exception exc;
|
||||
|
||||
private LifeCycleListener listener;
|
||||
|
||||
public LifeCycleThread(String name) {
|
||||
super(name);
|
||||
startLock = new Object();
|
||||
stopLock = new Object();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be invoked by subclasses when the startup procedure
|
||||
* has been completed.
|
||||
*/
|
||||
protected void notifyStarted() {
|
||||
|
||||
if (Logging.isInfo())
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.lifecycle, this, "Thread %s started", Thread
|
||||
.currentThread().getName());
|
||||
|
||||
synchronized (startLock) {
|
||||
started = true;
|
||||
startLock.notifyAll();
|
||||
if (listener != null)
|
||||
listener.startupPerformed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be invoked by subclasses when the shutdown procedure
|
||||
* has been completed.
|
||||
*/
|
||||
protected void notifyStopped() {
|
||||
|
||||
if (Logging.isInfo())
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.lifecycle, this, "Thread %s terminated", Thread
|
||||
.currentThread().getName());
|
||||
|
||||
synchronized (stopLock) {
|
||||
stopped = true;
|
||||
stopLock.notifyAll();
|
||||
if (listener != null)
|
||||
listener.shutdownPerformed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be invoked by subclasses when the thread has crashed.
|
||||
*/
|
||||
protected void notifyCrashed(Throwable exc) {
|
||||
|
||||
Logging.logMessage(Logging.LEVEL_CRIT, this, "service ***CRASHED***, shutting down");
|
||||
Logging.logError(Logging.LEVEL_CRIT, this, exc);
|
||||
|
||||
synchronized (startLock) {
|
||||
this.exc = exc instanceof Exception ? (Exception) exc : new Exception(exc);
|
||||
started = true;
|
||||
startLock.notifyAll();
|
||||
}
|
||||
|
||||
synchronized (stopLock) {
|
||||
this.exc = exc instanceof Exception ? (Exception) exc : new Exception(exc);
|
||||
stopped = true;
|
||||
stopLock.notifyAll();
|
||||
}
|
||||
|
||||
if (listener != null)
|
||||
listener.crashPerformed(exc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously waits for a notification indicating that the startup
|
||||
* procedure has been completed.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurred during the startup procedure
|
||||
*/
|
||||
public void waitForStartup() throws Exception {
|
||||
synchronized (startLock) {
|
||||
|
||||
while (!started)
|
||||
startLock.wait();
|
||||
|
||||
if (exc != null && listener == null)
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously waits for a notification indicating that the shutdown
|
||||
* procedure has been completed.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an error occurred during the shutdown procedure
|
||||
*/
|
||||
public void waitForShutdown() throws Exception {
|
||||
synchronized (stopLock) {
|
||||
|
||||
if (!started)
|
||||
return;
|
||||
while (!stopped)
|
||||
try {
|
||||
stopLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// In case this thread executes notifyCrashed(), he will
|
||||
// probably interrupt itself. However, this should not
|
||||
// interfere with the notifyCrashed() procedure and
|
||||
// therefore we swallow this exception.
|
||||
if (listener == null) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (exc != null && listener == null)
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the thread. This method should be overridden in subclasses.
|
||||
* @throws Exception if an error occurred
|
||||
*/
|
||||
public void shutdown() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener waiting for life cycle events.
|
||||
*
|
||||
* @param listener
|
||||
* the listener
|
||||
*/
|
||||
public void setLifeCycleListener(LifeCycleListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
}
|
||||
407
java/foundation/src/org/xtreemfs/foundation/SSLOptions.java
Normal file
407
java/foundation/src/org/xtreemfs/foundation/SSLOptions.java
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Bjoern Kolbeck, Jan Stender, Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Security;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
|
||||
/**
|
||||
* Encapsulates the SSLOptions for the connections of pinky and speedy
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class SSLOptions {
|
||||
/**
|
||||
* a Java JKS Keystore
|
||||
*/
|
||||
public final static String JKS_CONTAINER = "JKS";
|
||||
|
||||
/**
|
||||
* a PKCS12 Keystore
|
||||
*/
|
||||
public final static String PKCS12_CONTAINER = "PKCS12";
|
||||
|
||||
/**
|
||||
* Default SSL/TLS Protocol to use when no or an invalid protocol was specified
|
||||
*/
|
||||
public final static String DEFAULT_SSL_PROTOCOL = "TLS";
|
||||
|
||||
/**
|
||||
* file with the private key and the public cert for the server
|
||||
*/
|
||||
private final InputStream serverCredentialFile;
|
||||
|
||||
/**
|
||||
* file with trusted public certs
|
||||
*/
|
||||
private final InputStream trustedCertificatesFile;
|
||||
|
||||
/**
|
||||
* passphrase of the server credential file
|
||||
*/
|
||||
private final char[] serverCredentialFilePassphrase;
|
||||
|
||||
/**
|
||||
* passphrase of the trusted certificates file
|
||||
*/
|
||||
private final char[] trustedCertificatesFilePassphrase;
|
||||
|
||||
/**
|
||||
* using symmetric encryption or only authenticating via certs
|
||||
*/
|
||||
private boolean authenticationWithoutEncryption;
|
||||
|
||||
/**
|
||||
* file format of the server credential file
|
||||
*/
|
||||
private final String serverCredentialFileContainer;
|
||||
|
||||
/**
|
||||
* file format of the trusted certificates file
|
||||
*/
|
||||
private final String trustedCertificatesFileContainer;
|
||||
|
||||
/**
|
||||
* knows the used certs and more
|
||||
*/
|
||||
private final SSLContext sslContext;
|
||||
|
||||
private final boolean useFakeSSLMode;
|
||||
|
||||
public SSLOptions(InputStream serverCredentialFile, String serverCredentialFilePassphrase,
|
||||
String serverCredentialFileContainer, InputStream trustedCertificatesFile,
|
||||
String trustedCertificatesFilePassphrase, String trustedCertificatesFileContainer,
|
||||
boolean authenticationWithoutEncryption, boolean useFakeSSLMode, String sslProtocolString,
|
||||
TrustManager trustManager) throws IOException {
|
||||
|
||||
this.serverCredentialFile = serverCredentialFile;
|
||||
this.trustedCertificatesFile = trustedCertificatesFile;
|
||||
|
||||
if (serverCredentialFilePassphrase != null)
|
||||
this.serverCredentialFilePassphrase = serverCredentialFilePassphrase.toCharArray();
|
||||
else
|
||||
this.serverCredentialFilePassphrase = null;
|
||||
|
||||
if (trustedCertificatesFilePassphrase != null)
|
||||
this.trustedCertificatesFilePassphrase = trustedCertificatesFilePassphrase.toCharArray();
|
||||
else
|
||||
this.trustedCertificatesFilePassphrase = null;
|
||||
|
||||
this.serverCredentialFileContainer = serverCredentialFileContainer;
|
||||
this.trustedCertificatesFileContainer = trustedCertificatesFileContainer;
|
||||
|
||||
this.authenticationWithoutEncryption = authenticationWithoutEncryption;
|
||||
|
||||
this.useFakeSSLMode = useFakeSSLMode;
|
||||
|
||||
sslContext = createSSLContext(sslProtocolStringToProtocol(sslProtocolString), trustManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create/initialize the SSLContext with key material
|
||||
*
|
||||
* @param trustManager
|
||||
* the trust manager for the SSL context (may be
|
||||
* <code>null</code>)
|
||||
* @return the created and initialized SSLContext
|
||||
* @throws IOException
|
||||
*/
|
||||
private SSLContext createSSLContext(String sslProtocol, TrustManager trustManager) throws IOException {
|
||||
SSLContext sslContext = null;
|
||||
try {
|
||||
// First initialize the key and trust material.
|
||||
KeyStore ksKeys = KeyStore.getInstance(serverCredentialFileContainer);
|
||||
ksKeys.load(serverCredentialFile, serverCredentialFilePassphrase);
|
||||
|
||||
// KeyManager's decide which key material to use.
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
kmf.init(ksKeys, serverCredentialFilePassphrase);
|
||||
|
||||
// There are quite a few issues with the OpenJDK PKCS11 provider in combination with NSS,
|
||||
// so remove it no matter what the OpenJDK version is.
|
||||
if ("OpenJDK Runtime Environment".equals(System.getProperty("java.runtime.name"))) {
|
||||
try {
|
||||
Security.removeProvider("SunPKCS11-NSS");
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "Successfully removed faulty security provider 'SunPKCS11-NSS'.");
|
||||
}
|
||||
} catch(SecurityException e) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this,
|
||||
"Could not remove security provider 'SunPKCS11-NSS'. This might cause TLS connections to time out. " +
|
||||
"Known to affect multiple OpenJDK / NSS version combinations.");
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "%s:\n%s", e.getMessage(), OutputUtils.stackTraceToString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-enable disabled algorithms if the user requests it.
|
||||
final String defaultDisabledAlgorithms = Security.getProperty("jdk.tls.disabledAlgorithms");
|
||||
removeDisabledEntailedProtocolSupportForProtocol(sslProtocol);
|
||||
|
||||
try {
|
||||
sslContext = SSLContext.getInstance(sslProtocol);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this, "Unsupported algorithm '%s', defaulting to '%s'.",
|
||||
sslProtocol, DEFAULT_SSL_PROTOCOL);
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "%s:\n%s", e.getMessage(), OutputUtils.stackTraceToString(e));
|
||||
}
|
||||
|
||||
// Reset disabled algorithms because the context could not be created.
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "Trying to reset disabled algorithms.");
|
||||
}
|
||||
try {
|
||||
Security.setProperty("jdk.tls.disabledAlgorithms", defaultDisabledAlgorithms);
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "Successfully reset disabled algorithms.");
|
||||
}
|
||||
} catch (SecurityException e1) {
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "Could not reset disabled algorithms: %s", OutputUtils.stackTraceToString(e1));
|
||||
}
|
||||
}
|
||||
|
||||
// Setup everything anew for the default SSL protocol.
|
||||
removeDisabledEntailedProtocolSupportForProtocol(DEFAULT_SSL_PROTOCOL);
|
||||
sslContext = SSLContext.getInstance(DEFAULT_SSL_PROTOCOL);
|
||||
}
|
||||
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "Disabling the following algorithms: %s", Security.getProperty("jdk.tls.disabledAlgorithms"));
|
||||
}
|
||||
|
||||
if (trustManager != null) {
|
||||
// if a user-defined trust manager is set ...
|
||||
trustManager.init(trustedCertificatesFileContainer, trustedCertificatesFile,
|
||||
trustedCertificatesFilePassphrase);
|
||||
sslContext.init(kmf.getKeyManagers(), new TrustManager[] { trustManager }, null);
|
||||
} else if (trustedCertificatesFileContainer.equals("none")) {
|
||||
TrustManager[] myTMs = new TrustManager[] { new NoAuthTrustStore() };
|
||||
sslContext.init(kmf.getKeyManagers(), myTMs, null);
|
||||
} else {
|
||||
|
||||
// TrustManager's decide whether to allow connections.
|
||||
KeyStore ksTrust = null;
|
||||
if (trustedCertificatesFileContainer.equals("none")) {
|
||||
ksTrust = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
} else {
|
||||
ksTrust = KeyStore.getInstance(trustedCertificatesFileContainer);
|
||||
ksTrust.load(trustedCertificatesFile, trustedCertificatesFilePassphrase);
|
||||
}
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
||||
tmf.init(ksTrust);
|
||||
|
||||
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
}
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
e.printStackTrace();
|
||||
} catch (KeyManagementException e) {
|
||||
e.printStackTrace();
|
||||
} catch (KeyStoreException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
public boolean isAuthenticationWithoutEncryption() {
|
||||
return this.authenticationWithoutEncryption;
|
||||
}
|
||||
|
||||
public void setAuthenticationWithoutEncryption(boolean authenticationWithoutEncryption) {
|
||||
this.authenticationWithoutEncryption = authenticationWithoutEncryption;
|
||||
}
|
||||
|
||||
public InputStream getServerCredentialFile() {
|
||||
return this.serverCredentialFile;
|
||||
}
|
||||
|
||||
public String getServerCredentialFileContainer() {
|
||||
return this.serverCredentialFileContainer;
|
||||
}
|
||||
|
||||
public String getServerCredentialFilePassphrase() {
|
||||
return this.serverCredentialFilePassphrase.toString();
|
||||
}
|
||||
|
||||
public InputStream getTrustedCertificatesFile() {
|
||||
return this.trustedCertificatesFile;
|
||||
}
|
||||
|
||||
public String getTrustedCertificatesFileContainer() {
|
||||
return this.trustedCertificatesFileContainer;
|
||||
}
|
||||
|
||||
public String getTrustedCertificatesFilePassphrase() {
|
||||
return this.trustedCertificatesFilePassphrase.toString();
|
||||
}
|
||||
|
||||
public SSLContext getSSLContext() {
|
||||
return this.sslContext;
|
||||
}
|
||||
|
||||
public boolean isFakeSSLMode() {
|
||||
return this.useFakeSSLMode;
|
||||
}
|
||||
|
||||
public String getSSLProtocol() {
|
||||
return sslContext.getProtocol();
|
||||
}
|
||||
|
||||
public boolean isSSLEngineProtocolSupported(String sslEngineProtocol) {
|
||||
// Protocol names in JDK 5, 6: SSLv2Hello, SSLv3, TLSv1
|
||||
// Additionally in JDK 7, 8: TLSv1.2
|
||||
// TLSv1.1 seems to depend on the vendor
|
||||
String sslProtocol = getSSLProtocol();
|
||||
if ("SSLv3".equals(sslProtocol)) {
|
||||
return "SSLv3".equals(sslEngineProtocol);
|
||||
} else if ("TLS".equals(sslProtocol)) {
|
||||
return "SSLv3".equals(sslEngineProtocol) ||
|
||||
"TLSv1".equals(sslEngineProtocol) ||
|
||||
"TLSv1.1".equals(sslEngineProtocol) ||
|
||||
"TLSv1.2".equals(sslEngineProtocol);
|
||||
} else if ("TLSv1".equals(sslProtocol)) {
|
||||
return "TLSv1".equals(sslEngineProtocol);
|
||||
} else if ("TLSv1.1".equals(sslProtocol)) {
|
||||
return "TLSv1.1".equals(sslEngineProtocol);
|
||||
} else if ("TLSv1.2".equals(sslProtocol)) {
|
||||
return "TLSv1.2".equals(sslEngineProtocol);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String sslProtocolStringToProtocol(String sslProtocolString) {
|
||||
// SSL Context Protocol Strings:
|
||||
// JDK 6: SSL, SSLv2, SSLv3, TLS, TLSv1
|
||||
// additionally in JDK 7: TLSv1.2
|
||||
// TLSv1.1 seems to depend on the vendor
|
||||
if ("sslv3".equals(sslProtocolString)) {
|
||||
return "SSLv3";
|
||||
} else if ("ssltls".equals(sslProtocolString)) {
|
||||
return "TLS";
|
||||
} else if ("tlsv1".equals(sslProtocolString)) {
|
||||
return "TLSv1";
|
||||
} else if ("tlsv11".equals(sslProtocolString)) {
|
||||
return "TLSv1.1";
|
||||
} else if ("tlsv12".equals(sslProtocolString)) {
|
||||
return "TLSv1.2";
|
||||
} else {
|
||||
if (sslProtocolString != null) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"Unknown SSL Context Protocol: '%s', defaulting to '%s'.",
|
||||
sslProtocolString, DEFAULT_SSL_PROTOCOL);
|
||||
}
|
||||
return DEFAULT_SSL_PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all protocols that should be supported when using {@code sslProtocol} from the disabled
|
||||
* algorithms list that is set as system default, e.g. in /usr/lib/jvm/default-java/jre/lib/security/java.security.
|
||||
*
|
||||
* @param sslProtocol
|
||||
*/
|
||||
private void removeDisabledEntailedProtocolSupportForProtocol(String sslProtocol) {
|
||||
if (Security.getProperty("jdk.tls.disabledAlgorithms") == null) {
|
||||
return; // no disabled algorithms, everything is allowed by default
|
||||
}
|
||||
|
||||
String[] entailedSupportedProtocols = new String[] {};
|
||||
if ("SSLv3".equals(sslProtocol)) {
|
||||
entailedSupportedProtocols = new String[] { "SSLv3" };
|
||||
} else if ("TLS".equals(sslProtocol)) {
|
||||
entailedSupportedProtocols = new String[] { "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" };
|
||||
} else if ("TLSv1".equals(sslProtocol)) {
|
||||
entailedSupportedProtocols = new String[] { "TLSv1" };
|
||||
} else if ("TLSv1.1".equals(sslProtocol)) {
|
||||
entailedSupportedProtocols = new String[] { "TLSv1.1" };
|
||||
} else if ("TLSv1.2".equals(sslProtocol)) {
|
||||
entailedSupportedProtocols = new String[] { "TLSv1.2" };
|
||||
}
|
||||
|
||||
// For each protocol whose support is entailed by the requested protocol,
|
||||
// remove it from the disabled algorithms list if possible.
|
||||
for (String supportedSSLProtocol : entailedSupportedProtocols) {
|
||||
if (Security.getProperty("jdk.tls.disabledAlgorithms").contains(supportedSSLProtocol)) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this,
|
||||
"Algorithm '%s' is disabled in your java.security configuration file (see key 'jdk.tls.disabledAlgorithms'). " +
|
||||
"Trying to enable algorithm '%s' manually as specified in your configuration file (see key 'ssl.protocol'). " +
|
||||
"Consider using a newer SSL/TLS algorithm for your setup, " +
|
||||
"as algorithm '%s' has been disabled by default because of security issues.",
|
||||
supportedSSLProtocol, supportedSSLProtocol, supportedSSLProtocol);
|
||||
try {
|
||||
Security.setProperty("jdk.tls.disabledAlgorithms",
|
||||
Security.getProperty("jdk.tls.disabledAlgorithms").replace(supportedSSLProtocol, "").replace(" ", ""));
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "Successfully removed algorithm '%s' from disabled algorithms.",
|
||||
supportedSSLProtocol);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this, "Could not remove algorithm '%s' from disabled algorithm. " +
|
||||
"This might cause SSL Handshake exceptions. For SSLv3 this is known to affect all JDKs fixing issue CVE-2014-3566.",
|
||||
supportedSSLProtocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoAuthTrustStore implements TrustManager, X509TrustManager {
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(String trustedCertificatesFileContainer, InputStream trustedCertificatesFile,
|
||||
char[] trustedCertificatesFilePassphrase) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static interface TrustManager extends javax.net.ssl.TrustManager {
|
||||
public void init(String trustedCertificatesFileContainer, InputStream trustedCertificatesFile,
|
||||
char[] trustedCertificatesFilePassphrase);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by Felix Langner,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Provides methods to synchronize with a XtreemFS TimeServer, usually provided
|
||||
* by a DIR service.
|
||||
*
|
||||
* @author flangner
|
||||
* @since 03/01/2010
|
||||
*/
|
||||
|
||||
public interface TimeServerClient {
|
||||
|
||||
/**
|
||||
* Requests the global time at the given server.
|
||||
*
|
||||
* @param server - if null, the default will be used.
|
||||
* @return a {@link RPCResponse} future for an UNIX time-stamp.
|
||||
*/
|
||||
public long xtreemfs_global_time_get(InetSocketAddress server);
|
||||
}
|
||||
464
java/foundation/src/org/xtreemfs/foundation/TimeSync.java
Normal file
464
java/foundation/src/org/xtreemfs/foundation/TimeSync.java
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Jan Stender, Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
|
||||
/**
|
||||
* A class that offers a local time w/ adjustable granularity and a global time
|
||||
* based on the time reported by the DIR. Global time is adjusted periodically.
|
||||
* This class should be used to minimize the number of calls to
|
||||
* System.currentTimeMillis which is a costly system call on Linux. Moreover it
|
||||
* offers a system-global time.
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public final class TimeSync extends LifeCycleThread {
|
||||
|
||||
public enum ExtSyncSource {
|
||||
XTREEMFS_DIR, GPSD, LOCAL_CLOCK
|
||||
};
|
||||
|
||||
/**
|
||||
* The maximum round trip time for a clock synchronization message between
|
||||
* the <code>TimeSync</code> and the DIR. If the round trip time of a
|
||||
* synchronization message exceeds this value, the message will be ignored.
|
||||
*/
|
||||
private static final int MAX_RTT = 1000;
|
||||
|
||||
/**
|
||||
* A client used to synchronize clocks
|
||||
*/
|
||||
private TimeServerClient timeServerClient;
|
||||
|
||||
/**
|
||||
* interval in ms to wait between to synchronizations.
|
||||
*/
|
||||
private volatile int timeSyncInterval;
|
||||
|
||||
/**
|
||||
* interval between updates of the local system clock.
|
||||
*
|
||||
* If it's set to 0, the local renew by the thread is disabled and the time
|
||||
* is read from the system on demand.
|
||||
*/
|
||||
private volatile int localTimeRenew;
|
||||
|
||||
private volatile ExtSyncSource syncSource;
|
||||
|
||||
private InetSocketAddress gpsdAddr;
|
||||
|
||||
/**
|
||||
* local sys time as of last update
|
||||
*/
|
||||
private volatile long localSysTime;
|
||||
|
||||
/**
|
||||
* drift between local clock and global time as of last resync() operation.
|
||||
*/
|
||||
private volatile long currentDrift;
|
||||
|
||||
/**
|
||||
* set to true to stop thread
|
||||
*/
|
||||
private volatile boolean quit;
|
||||
|
||||
/**
|
||||
* timestamp of last resync operation
|
||||
*/
|
||||
private volatile long lastSuccessfulSync;
|
||||
|
||||
/**
|
||||
* Timestamp of the last resync attempt.
|
||||
*
|
||||
* @note No need to specify it as volatile since it's only used by run().
|
||||
*/
|
||||
private long lastSyncAttempt;
|
||||
|
||||
private volatile int syncRTT;
|
||||
|
||||
private volatile boolean syncSuccess;
|
||||
|
||||
private static TimeSync theInstance;
|
||||
|
||||
private final Pattern gpsdDatePattern;
|
||||
|
||||
private Socket gpsdSocket;
|
||||
|
||||
/**
|
||||
* Creates a new instance of TimeSync
|
||||
*
|
||||
* @dir a directory server to use for synchronizing clocks, can be null for
|
||||
* test setups only
|
||||
*/
|
||||
private TimeSync(ExtSyncSource source, TimeServerClient dir, InetSocketAddress gpsd, int timeSyncInterval, int localTimeRenew) {
|
||||
super("TSync Thr");
|
||||
setDaemon(true);
|
||||
this.syncSuccess = false;
|
||||
this.gpsdDatePattern = Pattern.compile("GPSD,D=(....)-(..)-(..)T(..):(..):(..)\\.(.+)Z");
|
||||
|
||||
init(source, dir, gpsd, timeSyncInterval, localTimeRenew);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the TimeSync with new parameters.
|
||||
*
|
||||
* @param source
|
||||
* @param dir
|
||||
* @param gpsd
|
||||
* @param timeSyncInterval
|
||||
* @param localTimeRenew
|
||||
*/
|
||||
public synchronized void init(ExtSyncSource source, TimeServerClient dir, InetSocketAddress gpsd, int timeSyncInterval, int localTimeRenew) {
|
||||
this.localTimeRenew = localTimeRenew;
|
||||
this.timeSyncInterval = timeSyncInterval;
|
||||
this.timeServerClient = dir;
|
||||
this.syncSource = source;
|
||||
this.gpsdAddr = gpsd;
|
||||
|
||||
if (this.timeServerClient != null && this.timeSyncInterval != 0 && this.localTimeRenew != 0) {
|
||||
this.localTimeRenew = 0;
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
"Disabled the periodic local time renew (set local_clock_renewal to 0)" +
|
||||
" and using always the current system time as base since the time will be corrected by synchronizing with the DIR service.");
|
||||
}
|
||||
|
||||
if (source == ExtSyncSource.GPSD) {
|
||||
try {
|
||||
if (gpsdSocket != null)
|
||||
gpsdSocket.close();
|
||||
|
||||
gpsdSocket = new Socket();
|
||||
gpsdSocket.setSoTimeout(2000);
|
||||
gpsdSocket.setTcpNoDelay(true);
|
||||
gpsdSocket.connect(gpsdAddr,2000);
|
||||
} catch (IOException ex) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, this,"cannot connect to GPSd: "+ex);
|
||||
gpsdSocket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* main loop
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
TimeSync.theInstance = this;
|
||||
notifyStarted();
|
||||
String tsStatus;
|
||||
if (localTimeRenew == 0) {
|
||||
tsStatus = "using the local clock";
|
||||
} else {
|
||||
tsStatus = "using the local clock (precision is " + this.localTimeRenew + " ms)";
|
||||
}
|
||||
if (this.timeServerClient != null && timeSyncInterval != 0) {
|
||||
tsStatus += " and remote sync every " + this.timeSyncInterval + " ms";
|
||||
}
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.lifecycle, this, "TimeSync is running %s", tsStatus);
|
||||
while (!quit) {
|
||||
// Renew cached local time.
|
||||
final long previousLocalSysTime = localSysTime;
|
||||
localSysTime = System.currentTimeMillis();
|
||||
if (localTimeRenew > 0 && previousLocalSysTime != 0) {
|
||||
final long timeBetweenUpdates = Math.abs(localSysTime - previousLocalSysTime);
|
||||
if (timeBetweenUpdates > 4 * localTimeRenew) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this,
|
||||
"The granularity of the renewed local time could not be guaranteed" +
|
||||
" since it took longer to retrieve the latest local time (%d ms) than configured (local_clock_renewal = %d)." +
|
||||
" Maybe the system is under high I/O load and therefore scheduling threads takes longer than usual?",
|
||||
timeBetweenUpdates,
|
||||
localTimeRenew);
|
||||
}
|
||||
}
|
||||
|
||||
// Remote sync time.
|
||||
if (timeSyncInterval != 0 && localSysTime - lastSyncAttempt > timeSyncInterval) {
|
||||
resync();
|
||||
}
|
||||
if (!quit) {
|
||||
//
|
||||
try {
|
||||
// If local refresh was disabled, use timeSyncInterval as sleep time.
|
||||
long sleepTimeMs = localTimeRenew != 0 ? localTimeRenew : timeSyncInterval;
|
||||
if (sleepTimeMs == 0) {
|
||||
// If there is no need to run this thread at all, let it sleep for 10 minutes.
|
||||
sleepTimeMs = 600000;
|
||||
}
|
||||
TimeSync.sleep(sleepTimeMs);
|
||||
} catch (InterruptedException ex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
notifyStopped();
|
||||
syncSuccess = false;
|
||||
theInstance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the time synchronizer. Note that only the first invocation of
|
||||
* this method has an effect, any further invocations will be ignored.
|
||||
*
|
||||
* @param dir
|
||||
* @param timeSyncInterval
|
||||
* @param localTimeRenew
|
||||
* @param dirAuthStr
|
||||
*/
|
||||
public static TimeSync initialize(TimeServerClient dir, int timeSyncInterval, int localTimeRenew) throws Exception {
|
||||
|
||||
if (theInstance != null) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.lifecycle, null, "time sync already running",
|
||||
new Object[0]);
|
||||
return theInstance;
|
||||
}
|
||||
|
||||
TimeSync s = new TimeSync(ExtSyncSource.XTREEMFS_DIR, dir, null, timeSyncInterval, localTimeRenew);
|
||||
s.start();
|
||||
s.waitForStartup();
|
||||
return s;
|
||||
}
|
||||
|
||||
public static TimeSync initializeLocal(int localTimeRenew) {
|
||||
if (theInstance != null) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.lifecycle, null, "time sync already running",
|
||||
new Object[0]);
|
||||
return theInstance;
|
||||
}
|
||||
|
||||
TimeSync s = new TimeSync(ExtSyncSource.LOCAL_CLOCK, null, null, 0, localTimeRenew);
|
||||
s.start();
|
||||
return s;
|
||||
}
|
||||
|
||||
public static TimeSync initializeGPSD(InetSocketAddress gpsd, int timeSyncInterval, int localTimeRenew) {
|
||||
if (theInstance != null) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.lifecycle, null, "time sync already running",
|
||||
new Object[0]);
|
||||
return theInstance;
|
||||
}
|
||||
|
||||
TimeSync s = new TimeSync(ExtSyncSource.GPSD, null, gpsd, timeSyncInterval, localTimeRenew);
|
||||
s.start();
|
||||
return s;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
shutdown();
|
||||
try {
|
||||
waitForShutdown();
|
||||
} catch (Exception e) {
|
||||
Logging.logError(Logging.LEVEL_ERROR, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stop the thread
|
||||
*/
|
||||
public void shutdown() {
|
||||
quit = true;
|
||||
this.interrupt();
|
||||
if (gpsdSocket != null) {
|
||||
try {
|
||||
gpsdSocket.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the current value of the local system time variable. Has a
|
||||
* resolution of localTimeRenew ms.
|
||||
*/
|
||||
public static long getLocalSystemTime() {
|
||||
TimeSync ts = getInstance();
|
||||
if (ts.localTimeRenew == 0 || ts.localSysTime == 0) {
|
||||
return System.currentTimeMillis();
|
||||
} else {
|
||||
return ts.localSysTime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the current value of the local system time adjusted to global
|
||||
* time. Has a resolution of localTimeRenew ms.
|
||||
*/
|
||||
public static long getGlobalTime() {
|
||||
TimeSync ts = getInstance();
|
||||
if (ts.localTimeRenew == 0 || ts.localSysTime == 0) {
|
||||
return System.currentTimeMillis() + ts.currentDrift;
|
||||
} else {
|
||||
return ts.localSysTime + ts.currentDrift;
|
||||
}
|
||||
}
|
||||
|
||||
public static long getLocalRenewInterval() {
|
||||
return getInstance().localTimeRenew;
|
||||
}
|
||||
|
||||
public static int getTimeSyncInterval() {
|
||||
return getInstance().timeSyncInterval;
|
||||
}
|
||||
|
||||
public static int getSyncRTT() {
|
||||
return getInstance().syncRTT;
|
||||
}
|
||||
|
||||
public static boolean lastSyncWasSuccessful() {
|
||||
return getInstance().syncSuccess;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the timestamp (local time) when the drift
|
||||
* was successfully calculated
|
||||
*/
|
||||
public static long getLastSuccessfulSyncTimestamp() {
|
||||
return getInstance().lastSuccessfulSync;
|
||||
}
|
||||
/**
|
||||
* returns the current clock drift.
|
||||
*/
|
||||
public long getDrift() {
|
||||
return this.currentDrift;
|
||||
}
|
||||
|
||||
/**
|
||||
* resynchronizes with the global time obtained from the DIR
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void resync() {
|
||||
switch (syncSource) {
|
||||
case LOCAL_CLOCK : return;
|
||||
case XTREEMFS_DIR : {
|
||||
try {
|
||||
long tStart = System.currentTimeMillis();
|
||||
lastSyncAttempt = tStart;
|
||||
long oldDrift = currentDrift;
|
||||
long globalTime = timeServerClient.xtreemfs_global_time_get(null);
|
||||
if (globalTime <= 0) {
|
||||
//error
|
||||
return;
|
||||
}
|
||||
|
||||
long tEnd = System.currentTimeMillis();
|
||||
// add half a roundtrip to estimate the delay
|
||||
syncRTT = (int)(tEnd - tStart);
|
||||
|
||||
if (syncRTT > MAX_RTT) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.misc, this,
|
||||
"Ignored time synchronization message because DIR took too long to respond (%d ms)",
|
||||
syncRTT);
|
||||
syncSuccess = false;
|
||||
return;
|
||||
}
|
||||
|
||||
globalTime += syncRTT / 2;
|
||||
syncSuccess = true;
|
||||
|
||||
currentDrift = globalTime - tEnd;
|
||||
lastSuccessfulSync = tEnd;
|
||||
|
||||
if (Math.abs(oldDrift - currentDrift) > 5000 && oldDrift != 0) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.misc, this,
|
||||
"STRANGE DRIFT CHANGE from %d to %d", oldDrift, currentDrift);
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
syncSuccess = false;
|
||||
ex.printStackTrace();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GPSD : {
|
||||
try {
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(gpsdSocket.getInputStream()));
|
||||
OutputStream os = gpsdSocket.getOutputStream();
|
||||
long tStart = System.currentTimeMillis();
|
||||
lastSyncAttempt = tStart;
|
||||
|
||||
os.write(new byte[]{'d','\n'});
|
||||
os.flush();
|
||||
|
||||
long oldDrift = currentDrift;
|
||||
String response = br.readLine();
|
||||
long tEnd = System.currentTimeMillis();
|
||||
|
||||
|
||||
Matcher m = gpsdDatePattern.matcher(response);
|
||||
Calendar c = Calendar.getInstance();
|
||||
if (m.matches()) {
|
||||
c.set(Calendar.YEAR, Integer.parseInt(m.group(1)));
|
||||
c.set(Calendar.MONTH, Integer.parseInt(m.group(2))-1);
|
||||
c.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(3)));
|
||||
c.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4)));
|
||||
c.set(Calendar.MINUTE, Integer.parseInt(m.group(5)));
|
||||
c.set(Calendar.SECOND, Integer.parseInt(m.group(6)));
|
||||
//c.set(Calendar.MILLISECOND, Integer.parseInt(m.group(7))*10);
|
||||
} else {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this,"cannot parse GPSd response: %s",response);
|
||||
syncSuccess = false;
|
||||
return;
|
||||
}
|
||||
|
||||
long globalTime = c.getTimeInMillis();
|
||||
Date d = new Date(globalTime);
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this,"global GPSd time: %d (%d:%d:%d)",c.getTimeInMillis(),d.getHours(),
|
||||
d.getMinutes(),d.getSeconds());
|
||||
|
||||
// add half a roundtrip to estimate the delay
|
||||
syncRTT = (int)(tEnd - tStart);
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this,"sync RTT: %d ms",syncRTT);
|
||||
globalTime += syncRTT / 2;
|
||||
syncSuccess = true;
|
||||
|
||||
currentDrift = globalTime - tEnd;
|
||||
lastSuccessfulSync = tEnd;
|
||||
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.misc, this,
|
||||
"resync success, drift: %d ms", Math.abs(oldDrift-currentDrift));
|
||||
|
||||
if (Math.abs(oldDrift - currentDrift) > 5000 && oldDrift != 0) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.misc, this,
|
||||
"STRANGE DRIFT CHANGE from %d to %d", oldDrift, currentDrift);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
syncSuccess = false;
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeSync getInstance() {
|
||||
if (theInstance == null)
|
||||
throw new RuntimeException("TimeSync not initialized!");
|
||||
return theInstance;
|
||||
}
|
||||
|
||||
public static boolean isInitialized() {
|
||||
return theInstance != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Jan Stender, Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation;
|
||||
|
||||
/**
|
||||
* This class is meant to maintain version numbers for different components used
|
||||
* in XtreemFS, in order to be able to detect possible incompatibilities between
|
||||
* different versions.
|
||||
*
|
||||
* When a new version of the protocol, database, etc. has been implemented, the
|
||||
* corresponding version number should be replaced. XtreemFS will rely on this
|
||||
* class to find out what the current version numbers are.
|
||||
*
|
||||
*/
|
||||
public class VersionManagement {
|
||||
|
||||
public static final String RELEASE_VERSION = "1.5.1 (Wonderful Waffles)";
|
||||
|
||||
private static final long mrcDataVersion = 10;
|
||||
|
||||
private static final long osdDataVersion = 1;
|
||||
|
||||
private static final long foundationVersion = 2;
|
||||
|
||||
public static long getMrcDataVersion() {
|
||||
return mrcDataVersion;
|
||||
}
|
||||
|
||||
public static long getOsdDataVersion() {
|
||||
return osdDataVersion;
|
||||
}
|
||||
|
||||
public static long getFoundationVersion() {
|
||||
return foundationVersion;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by Bjoern Kolbeck, Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.buffer;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public final class ASCIIString implements Serializable {
|
||||
private static final long serialVersionUID = 4633232360908659139L;
|
||||
|
||||
private byte[] data;
|
||||
|
||||
private int hash;
|
||||
|
||||
protected ASCIIString() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of ASCIIString
|
||||
*/
|
||||
public ASCIIString(String str) {
|
||||
this.data = str.getBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of ASCIIString
|
||||
*/
|
||||
protected ASCIIString(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new String(data);
|
||||
}
|
||||
|
||||
public char charAt(int index) {
|
||||
|
||||
return (char)data[index];
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
try {
|
||||
ASCIIString other = (ASCIIString)o;
|
||||
|
||||
if (other.length() != this.length())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
if (data[i] != other.data[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (ClassCastException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void marshall(ReusableBuffer target) {
|
||||
target.putInt(data.length);
|
||||
target.put(data);
|
||||
|
||||
}
|
||||
|
||||
public static ASCIIString unmarshall(ReusableBuffer target) {
|
||||
|
||||
int length = target.getInt();
|
||||
if (length < 0)
|
||||
return null;
|
||||
byte[] tmp = new byte[length];
|
||||
|
||||
target.get(tmp);
|
||||
|
||||
return new ASCIIString(tmp);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int h = hash;
|
||||
if (h == 0) {
|
||||
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
h = 31*h + data[i];
|
||||
}
|
||||
hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
public int getSerializedSize() {
|
||||
return length()+Integer.SIZE/8;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Bjoern Kolbeck, Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.buffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* A concurrent pool for buffer recycling.
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public final class BufferPool {
|
||||
|
||||
/**
|
||||
* size of buffers for each class.
|
||||
*/
|
||||
public static final int[] BUFF_SIZES = { 8192, 65536, 131072, 524288,
|
||||
2097152 };
|
||||
|
||||
/**
|
||||
* max pool size for each class
|
||||
*/
|
||||
public static final int[] MAX_POOL_SIZES = { 2000, 200, 100, 10, 5 };
|
||||
|
||||
/**
|
||||
* queues to store buffers in
|
||||
*/
|
||||
private final ConcurrentLinkedQueue<ByteBuffer>[] pools;
|
||||
|
||||
/**
|
||||
* pool sizes to avoid counting elements on each access
|
||||
*/
|
||||
private final AtomicInteger[] poolSizes;
|
||||
|
||||
/**
|
||||
* stats for num requests and creates of buffers per class
|
||||
*/
|
||||
private final AtomicLong[] requests, creates, deletes;
|
||||
|
||||
/**
|
||||
* singleton pattern.
|
||||
*/
|
||||
private static final BufferPool instance = new BufferPool();
|
||||
|
||||
/**
|
||||
* if true all allocate/free operations record the stack trace. Useful to
|
||||
* find memory leaks but slow.
|
||||
*/
|
||||
protected static boolean recordStackTraces = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of BufferPool
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private BufferPool() {
|
||||
|
||||
pools = new ConcurrentLinkedQueue[BUFF_SIZES.length];
|
||||
|
||||
creates = new AtomicLong[BUFF_SIZES.length];
|
||||
for (int i = 0; i < creates.length; i++) {
|
||||
creates[i] = new AtomicLong();
|
||||
}
|
||||
|
||||
requests = new AtomicLong[BUFF_SIZES.length + 1];
|
||||
deletes = new AtomicLong[BUFF_SIZES.length + 1];
|
||||
for (int i = 0; i < BUFF_SIZES.length + 1; i++) {
|
||||
requests[i] = new AtomicLong();
|
||||
deletes[i] = new AtomicLong();
|
||||
}
|
||||
|
||||
poolSizes = new AtomicInteger[BUFF_SIZES.length];
|
||||
for (int i = 0; i < BUFF_SIZES.length; i++) {
|
||||
pools[i] = new ConcurrentLinkedQueue<ByteBuffer>();
|
||||
poolSizes[i] = new AtomicInteger(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new buffer. The Buffer is taken from the pool or created if none is
|
||||
* available or the size exceedes the largest class.
|
||||
*
|
||||
* @param size
|
||||
* the buffer's size in bytes
|
||||
* @return a buffer of requested size
|
||||
* @throws OutOfMemoryError
|
||||
* if a buffer cannot be allocated
|
||||
*/
|
||||
public static ReusableBuffer allocate(int size) {
|
||||
ReusableBuffer tmp = instance.getNewBuffer(size);
|
||||
assert (tmp.refCount.get() == 1): "newly allocated buffer has invalid reference count: " + tmp.refCount.get();
|
||||
|
||||
if (recordStackTraces) {
|
||||
tmp.allocStack = "\n";
|
||||
for (StackTraceElement elem : new Exception().getStackTrace())
|
||||
tmp.allocStack += elem.toString() + "\n";
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a buffer to the pool, if the buffer is reusable. Other buffers
|
||||
* are ignored.
|
||||
*
|
||||
* @param buf
|
||||
* the buffer to return
|
||||
*/
|
||||
public static void free(ReusableBuffer buf) {
|
||||
if (buf != null) {
|
||||
instance.returnBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a buffer which has at least size bytes.
|
||||
*
|
||||
* @attention The returned buffer can be larger than requested!
|
||||
*/
|
||||
private ReusableBuffer getNewBuffer(int size) {
|
||||
|
||||
try {
|
||||
|
||||
// if there is a pooled buffer with sufficient capacity ...
|
||||
for (int i = 0; i < BUFF_SIZES.length; i++) {
|
||||
|
||||
if (size <= BUFF_SIZES[i]) {
|
||||
|
||||
ByteBuffer buf = pools[i].poll();
|
||||
|
||||
// if no free buffer is available in the pool ...
|
||||
if (buf == null) {
|
||||
|
||||
// ... create
|
||||
// - a direct buffer if the pool is not full yet,
|
||||
// - a non-direct buffer if the pool is full
|
||||
//
|
||||
// Thus, the first MAX_POOL_SIZES[i] buffers will be
|
||||
// pooled, whereas any additional buffers will be
|
||||
// allocated on demand and freed by the garbage
|
||||
// collector.
|
||||
|
||||
buf = creates[i].get() < MAX_POOL_SIZES[i] ? ByteBuffer.allocateDirect(BUFF_SIZES[i])
|
||||
: ByteBuffer.allocate(BUFF_SIZES[i]);
|
||||
creates[i].incrementAndGet();
|
||||
}
|
||||
|
||||
// otherwise, decrement the pool size to indicate that the
|
||||
// pooled buffer was handed out to the application
|
||||
else {
|
||||
poolSizes[i].decrementAndGet();
|
||||
}
|
||||
|
||||
requests[i].incrementAndGet();
|
||||
return new ReusableBuffer(buf, size);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ... otherwise, create an unpooled buffer
|
||||
requests[BUFF_SIZES.length].incrementAndGet();
|
||||
|
||||
ByteBuffer buf = ByteBuffer.allocate(size);
|
||||
return new ReusableBuffer(buf, size);
|
||||
|
||||
} catch (OutOfMemoryError ex) {
|
||||
System.out.println(getStatus());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private void returnBuffer(ReusableBuffer buffer) {
|
||||
returnBuffer(buffer, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* return a buffer to the pool
|
||||
*/
|
||||
private void returnBuffer(ReusableBuffer buffer, boolean callFromView) {
|
||||
|
||||
if (!buffer.isReusable())
|
||||
return;
|
||||
|
||||
if (buffer.viewParent != null) {
|
||||
|
||||
// view buffer
|
||||
if (recordStackTraces) {
|
||||
|
||||
if (buffer.freeStack == null)
|
||||
buffer.freeStack = "";
|
||||
buffer.freeStack += "\n";
|
||||
|
||||
StackTraceElement[] stackTrace = new Exception().getStackTrace();
|
||||
for (int i = 0; i < stackTrace.length; i++)
|
||||
buffer.freeStack += stackTrace[i].toString() + "\n";
|
||||
}
|
||||
|
||||
assert (!buffer.returned) : "buffer was already released: " + buffer.freeStack;
|
||||
buffer.returned = true;
|
||||
returnBuffer(buffer.viewParent, true);
|
||||
|
||||
} else {
|
||||
|
||||
assert (!buffer.returned || callFromView) : "buffer was already released: " + buffer.freeStack;
|
||||
|
||||
if (recordStackTraces) {
|
||||
|
||||
if (buffer.freeStack == null)
|
||||
buffer.freeStack = "";
|
||||
buffer.freeStack += "\n";
|
||||
|
||||
StackTraceElement[] stackTrace = new Exception().getStackTrace();
|
||||
for (int i = 0; i < stackTrace.length; i++)
|
||||
buffer.freeStack += stackTrace[i].toString() + "\n";
|
||||
}
|
||||
|
||||
if (!callFromView) {
|
||||
buffer.returned = true;
|
||||
}
|
||||
|
||||
if (buffer.refCount.getAndDecrement() > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ByteBuffer buf = buffer.getParent();
|
||||
buf.clear();
|
||||
|
||||
// determine the pool to which the buffer is supposed to be
|
||||
// returned
|
||||
// ...
|
||||
for (int i = 0; i < BUFF_SIZES.length; i++) {
|
||||
|
||||
if (buf.capacity() == BUFF_SIZES[i]) {
|
||||
|
||||
// return direct buffers to the pool
|
||||
if (buf.isDirect()) {
|
||||
|
||||
poolSizes[i].incrementAndGet();
|
||||
pools[i].add(buf);
|
||||
|
||||
// since only direct buffers will be returned to the
|
||||
// pool, which have been counted on allocation, there is
|
||||
// no need to check the pool size here
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if the buffer is non-direct, increment the delete counter
|
||||
// and implicitly make the buffer subject to garbage
|
||||
// collection
|
||||
else {
|
||||
deletes[i].incrementAndGet();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
assert (!buf.isDirect()) : "encountered direct buffer that does not fit in any of the pools (size="
|
||||
+ buf.capacity() + "): " + buffer.freeStack;
|
||||
|
||||
// if the buffer did not fit in any of the pools,
|
||||
// increment the delete counter for the unpooled buffers
|
||||
deletes[deletes.length - 1].incrementAndGet();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current pool size for a specific buffer size.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* when bufferSize is not in the pool
|
||||
*/
|
||||
public static int getPoolSize(int bufferSize) {
|
||||
for (int i = 0; i < BUFF_SIZES.length; i++) {
|
||||
if (BUFF_SIZES[i] == bufferSize) {
|
||||
return instance.poolSizes[i].get();
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Specified buffer size is not pooled. Check BufferPool configuration.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a textual representation of the pool status.
|
||||
*
|
||||
* @return a textual representation of the pool status.
|
||||
*/
|
||||
public static String getStatus() {
|
||||
|
||||
String str = "";
|
||||
for (int i = 0; i < BUFF_SIZES.length; i++) {
|
||||
str += String.format(
|
||||
"%8d: poolSize = %5d numRequests = %8d creates = %8d deletes = %8d\n",
|
||||
BUFF_SIZES[i], instance.poolSizes[i].get(), instance.requests[i].get(), instance.creates[i]
|
||||
.get(), instance.deletes[i].get());
|
||||
}
|
||||
str += String.format("unpooled (> %8d) numRequests = creates = %8d deletes = %8d",
|
||||
BUFF_SIZES[BUFF_SIZES.length - 1], instance.requests[instance.requests.length - 1].get(),
|
||||
instance.deletes[instance.deletes.length - 1].get());
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether stack traces shall be recorded when allocating and
|
||||
* freeing buffers. Since recording stack traces leads to some overhead, it
|
||||
* should only be enabled for debugging purposes.
|
||||
*
|
||||
* @param record
|
||||
*/
|
||||
public static void enableStackTraceRecording(boolean record) {
|
||||
recordStackTraces = record;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,724 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Bjoern Kolbeck, Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.buffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public final class ReusableBuffer {
|
||||
|
||||
private static final Charset ENC_UTF8 = Charset.forName("utf-8");
|
||||
|
||||
/**
|
||||
* A view buffer of parentBuffer with the requested size. For non-reusable
|
||||
* buffers this is the buffer itself
|
||||
*/
|
||||
private ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* A parent buffer which is returned to the pool
|
||||
*/
|
||||
private final ByteBuffer parentBuffer;
|
||||
|
||||
/**
|
||||
* True if the buffer can be returned to the pool
|
||||
*/
|
||||
private final boolean reusable;
|
||||
|
||||
/**
|
||||
* set to true after a buffer was returned to the pool
|
||||
*/
|
||||
protected volatile boolean returned;
|
||||
|
||||
/**
|
||||
* size (as requested), might be smaller than parentBuffer size but is
|
||||
* always equal to the (view) buffer size.
|
||||
*/
|
||||
private int size;
|
||||
|
||||
protected ReusableBuffer viewParent;
|
||||
|
||||
protected String freeStack, allocStack;
|
||||
|
||||
/**
|
||||
* reference count
|
||||
*/
|
||||
AtomicInteger refCount;
|
||||
|
||||
/**
|
||||
* Creates a new instance of ReusableBuffer. A view buffer of size is created.
|
||||
*
|
||||
* @param buffer
|
||||
* the parent buffer
|
||||
* @param size
|
||||
* the requested size
|
||||
*/
|
||||
protected ReusableBuffer(ByteBuffer buffer, int size) {
|
||||
buffer.position(0);
|
||||
buffer.limit(size);
|
||||
this.buffer = buffer.slice();
|
||||
this.parentBuffer = buffer;
|
||||
this.size = size;
|
||||
this.reusable = true;
|
||||
this.refCount = new AtomicInteger(1);
|
||||
returned = false;
|
||||
viewParent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for a non-reusable buffer. The buffer is not used by the pool
|
||||
* when returned.
|
||||
*/
|
||||
public ReusableBuffer(ByteBuffer nonManaged) {
|
||||
this.buffer = nonManaged;
|
||||
this.size = buffer.limit();
|
||||
this.reusable = false;
|
||||
this.parentBuffer = null;
|
||||
returned = false;
|
||||
this.refCount = new AtomicInteger(1);
|
||||
viewParent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a non-reusable buffer around a byte array. Uses the
|
||||
* ByteBuffer.wrap method.
|
||||
*
|
||||
* @param data
|
||||
* the byte arry containing the data
|
||||
* @return
|
||||
*/
|
||||
public static ReusableBuffer wrap(byte[] data) {
|
||||
return new ReusableBuffer(ByteBuffer.wrap(data));
|
||||
}
|
||||
|
||||
public static ReusableBuffer wrap(byte[] data, int offset, int length) {
|
||||
assert (offset >= 0);
|
||||
assert (length >= 0);
|
||||
if (offset + length > data.length)
|
||||
throw new IllegalArgumentException("offset+length > buffer size (" + offset + "+" + length
|
||||
+ " > " + data.length);
|
||||
ByteBuffer tmp = ByteBuffer.wrap(data);
|
||||
tmp.position(offset);
|
||||
tmp.limit(offset + length);
|
||||
return new ReusableBuffer(tmp.slice());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new view buffer. This view buffer shares the same data (i.e.
|
||||
* backing byte buffer) but has independent position, limit etc.
|
||||
*/
|
||||
public ReusableBuffer createViewBuffer() {
|
||||
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
|
||||
if (this.viewParent == null) {
|
||||
|
||||
if (parentBuffer == null) {
|
||||
// wraped buffers
|
||||
ReusableBuffer view = new ReusableBuffer(this.buffer.slice());
|
||||
view.viewParent = this;
|
||||
|
||||
return view;
|
||||
|
||||
} else {
|
||||
// regular buffer
|
||||
ReusableBuffer view = new ReusableBuffer(this.parentBuffer, this.size);
|
||||
view.viewParent = this;
|
||||
this.refCount.incrementAndGet();
|
||||
|
||||
if (BufferPool.recordStackTraces) {
|
||||
view.allocStack = "\n";
|
||||
StackTraceElement[] stackTrace = new Exception().getStackTrace();
|
||||
for (int i = 0; i < stackTrace.length; i++)
|
||||
view.allocStack += stackTrace[i].toString() + (i < stackTrace.length - 1 ? "\n" : "");
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (parentBuffer == null) {
|
||||
// wraped buffers
|
||||
ReusableBuffer view = new ReusableBuffer(this.buffer.slice());
|
||||
view.viewParent = this.viewParent;
|
||||
|
||||
return view;
|
||||
|
||||
} else {
|
||||
// regular buffer: use the parent to create a view buffer
|
||||
ReusableBuffer view = new ReusableBuffer(this.buffer, this.size);
|
||||
view.viewParent = this.viewParent;
|
||||
this.viewParent.refCount.incrementAndGet();
|
||||
|
||||
if (BufferPool.recordStackTraces) {
|
||||
view.allocStack = "\n";
|
||||
StackTraceElement[] stackTrace = new Exception().getStackTrace();
|
||||
for (int i = 0; i < stackTrace.length; i++)
|
||||
view.allocStack += stackTrace[i].toString() + (i < stackTrace.length - 1 ? "\n" : "");
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#capacity
|
||||
*/
|
||||
public int capacity() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* May be higher than {@link #capacity()} if the parent buffer is from the {@link BufferPool} which may
|
||||
* have returned a larger buffer.
|
||||
*/
|
||||
public int capacityUnderlying() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return parentBuffer != null ? parentBuffer.capacity() : this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#hasArray
|
||||
*/
|
||||
public boolean hasArray() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.hasArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte array of the buffer, creating a copy if the buffer is
|
||||
* not backed by an array
|
||||
*
|
||||
* @return a byte array with a copy of the data
|
||||
*/
|
||||
public byte[] array() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
byte[] array = null;
|
||||
|
||||
if (this.hasArray() && (this.viewParent == null)) {
|
||||
array = buffer.array();
|
||||
} else {
|
||||
array = new byte[this.limit()];
|
||||
final int oldPos = this.position();
|
||||
this.position(0);
|
||||
this.get(array);
|
||||
this.position(oldPos);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#flip
|
||||
*/
|
||||
public void flip() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#compact
|
||||
*/
|
||||
public void compact() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#limit(int)
|
||||
*/
|
||||
public void limit(int l) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.limit(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#limit()
|
||||
*/
|
||||
public int limit() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.limit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#position(int)
|
||||
*/
|
||||
public void position(int p) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.position(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#position()
|
||||
*/
|
||||
public int position() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.position();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#hasRemaining
|
||||
*/
|
||||
public boolean hasRemaining() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.hasRemaining();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view buffer encapsulated by this ReusableBuffer.
|
||||
*
|
||||
* @return the view buffer
|
||||
*/
|
||||
public ByteBuffer getBuffer() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return this.buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if this buffer is re-usable and can be returned to the
|
||||
* pool.
|
||||
*
|
||||
* @return true, if this buffer is re-usable
|
||||
*/
|
||||
public boolean isReusable() {
|
||||
// assert(!returned) :
|
||||
// "Buffer was already freed and cannot be used anymore"+this.freeStack;
|
||||
return this.reusable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent buffer.
|
||||
*
|
||||
* @return the parent buffer
|
||||
*/
|
||||
protected ByteBuffer getParent() {
|
||||
return this.parentBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#get()
|
||||
*/
|
||||
public byte get() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.get();
|
||||
}
|
||||
|
||||
public byte get(int index) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#get(byte[])
|
||||
*/
|
||||
public ReusableBuffer get(byte[] dst) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.get(dst);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#get(byte[], int offset, int length)
|
||||
*/
|
||||
public ReusableBuffer get(byte[] dst, int offset, int length) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.get(dst, offset, length);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#put(byte)
|
||||
*/
|
||||
public ReusableBuffer put(byte b) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.put(b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#put(byte[])
|
||||
*/
|
||||
public ReusableBuffer put(byte[] src) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.put(src);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#put(byte[],int,int)
|
||||
*/
|
||||
public ReusableBuffer put(byte[] src, int offset, int len) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.put(src, offset, len);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#put(ByteBuffer)
|
||||
*/
|
||||
public ReusableBuffer put(ByteBuffer src) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.put(src);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the content of src into this buffer.
|
||||
*
|
||||
* @param src
|
||||
* the buffer to read from
|
||||
* @return this ReusableBuffer after reading
|
||||
* @see java.nio.ByteBuffer#put(ByteBuffer)
|
||||
*/
|
||||
public ReusableBuffer put(ReusableBuffer src) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.put(src.buffer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#getInt
|
||||
*/
|
||||
public int getInt() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.getInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#putInt(int)
|
||||
*/
|
||||
public ReusableBuffer putInt(int i) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.putInt(i);
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getLong() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.getLong();
|
||||
}
|
||||
|
||||
public ReusableBuffer putLong(long l) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.putLong(l);
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getDouble() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.getDouble();
|
||||
}
|
||||
|
||||
public ReusableBuffer putDouble(double d) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.putDouble(d);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
int length = buffer.getInt();
|
||||
if (length > 0) {
|
||||
byte[] bytes = new byte[length];
|
||||
buffer.get(bytes);
|
||||
return new String(bytes, ENC_UTF8);
|
||||
} else if (length == 0) {
|
||||
return "";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ReusableBuffer putString(String str) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
if (str != null) {
|
||||
byte[] bytes = str.getBytes(ENC_UTF8);
|
||||
buffer.putInt(bytes.length);
|
||||
buffer.put(bytes);
|
||||
} else {
|
||||
buffer.putInt(-1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReusableBuffer putShortString(String str) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
assert (str.length() <= Short.MAX_VALUE);
|
||||
if (str != null) {
|
||||
byte[] bytes = str.getBytes(ENC_UTF8);
|
||||
buffer.putShort((short) bytes.length);
|
||||
buffer.put(bytes);
|
||||
} else {
|
||||
buffer.putInt(-1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ASCIIString getBufferBackedASCIIString() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return ASCIIString.unmarshall(this);
|
||||
}
|
||||
|
||||
public ReusableBuffer putBufferBackedASCIIString(ASCIIString str) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
if (str != null) {
|
||||
str.marshall(this);
|
||||
} else {
|
||||
buffer.putInt(-1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReusableBuffer putShort(short s) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.putShort(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public short getShort() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.getShort();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#isDirect
|
||||
*/
|
||||
public boolean isDirect() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.isDirect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#remaining
|
||||
*/
|
||||
public int remaining() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.remaining();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.Buffer#clear
|
||||
*/
|
||||
public void clear() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
byte[] array = new byte[this.limit()];
|
||||
this.position(0);
|
||||
this.get(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
public void shrink(int newSize) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
if (newSize > size) {
|
||||
throw new IllegalArgumentException("new size must not be larger than old size");
|
||||
}
|
||||
this.size = newSize;
|
||||
int oldPos = buffer.position();
|
||||
if (oldPos > newSize)
|
||||
oldPos = 0;
|
||||
|
||||
// save parent position and limit
|
||||
ByteBuffer originalBuffer;
|
||||
if (parentBuffer != null) {
|
||||
originalBuffer = parentBuffer;
|
||||
} else {
|
||||
originalBuffer = buffer;
|
||||
}
|
||||
int position = originalBuffer.position();
|
||||
int limit = originalBuffer.limit();
|
||||
|
||||
originalBuffer.position(0);
|
||||
originalBuffer.limit(newSize);
|
||||
this.buffer = originalBuffer.slice();
|
||||
buffer.position(oldPos);
|
||||
|
||||
// restore parent position and limit
|
||||
originalBuffer.position(position);
|
||||
originalBuffer.limit(limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increases the capacity of this buffer. Returns false if {@code newSize} is bigger than the capacity of
|
||||
* the underlying buffer.
|
||||
*
|
||||
* The underlying buffer can be a reusable buffer (parentBuffer != 0) or non-reusable buffer (viewParent
|
||||
* != 0).
|
||||
*/
|
||||
public boolean enlarge(int newSize) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
if (newSize == this.size) {
|
||||
return true;
|
||||
}
|
||||
if (parentBuffer == null && viewParent == null) {
|
||||
return false;
|
||||
}
|
||||
ByteBuffer underlyingBuffer = parentBuffer != null ? parentBuffer : viewParent.getBuffer();
|
||||
if (newSize > underlyingBuffer.capacity()) {
|
||||
return false;
|
||||
} else {
|
||||
this.size = newSize;
|
||||
int oldPos = buffer.position();
|
||||
if (oldPos > newSize)
|
||||
oldPos = 0;
|
||||
|
||||
// save parent position and limit
|
||||
int position = underlyingBuffer.position();
|
||||
int limit = underlyingBuffer.limit();
|
||||
|
||||
underlyingBuffer.position(0);
|
||||
underlyingBuffer.limit(newSize);
|
||||
this.buffer = underlyingBuffer.slice();
|
||||
buffer.position(oldPos);
|
||||
|
||||
// restore parent position and limit
|
||||
underlyingBuffer.position(position);
|
||||
underlyingBuffer.limit(limit);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the new range of the buffer starting from {@code offset} going for {@code length} bytes.
|
||||
*
|
||||
* The new position of the buffer will be 0 and the size/capacity will equal {@code length}.
|
||||
*/
|
||||
public void range(int offset, int length) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
|
||||
// useless call!
|
||||
if ((offset == 0) && (length == this.size))
|
||||
return;
|
||||
|
||||
if (offset >= size) {
|
||||
throw new IllegalArgumentException("offset must be < size. offset=" + offset + " size=" + size);
|
||||
}
|
||||
if (offset + length > size) {
|
||||
throw new IllegalArgumentException("offset+length must be <= size. size=" + size + " offset="
|
||||
+ offset + " length=" + length);
|
||||
}
|
||||
|
||||
this.size = length;
|
||||
|
||||
// save parent position and limit
|
||||
ByteBuffer originalBuffer;
|
||||
if (parentBuffer != null) {
|
||||
originalBuffer = parentBuffer;
|
||||
} else {
|
||||
originalBuffer = buffer;
|
||||
}
|
||||
int position = originalBuffer.position();
|
||||
int limit = originalBuffer.limit();
|
||||
|
||||
// ensure that the subsequent 'position' does not fail
|
||||
if (offset > limit)
|
||||
originalBuffer.limit(offset);
|
||||
|
||||
originalBuffer.position(offset);
|
||||
originalBuffer.limit(offset + length);
|
||||
this.buffer = originalBuffer.slice();
|
||||
assert (this.buffer.capacity() == length);
|
||||
|
||||
// restore parent position and limit
|
||||
originalBuffer.position(position);
|
||||
originalBuffer.limit(limit);
|
||||
}
|
||||
|
||||
public ReusableBuffer putBoolean(boolean bool) {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
buffer.put(bool ? (byte) 1 : (byte) 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean getBoolean() {
|
||||
assert (!returned) : "Buffer was already freed and cannot be used anymore" + this.freeStack;
|
||||
return buffer.get() == 1;
|
||||
}
|
||||
|
||||
public int getRefCount() {
|
||||
if (this.viewParent == null) {
|
||||
return this.refCount.get();
|
||||
} else {
|
||||
return this.viewParent.refCount.get();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
|
||||
if (!returned && reusable) {
|
||||
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this,
|
||||
"buffer was finalized but not freed before! buffer = %s, refCount=%d", this.toString(), getRefCount());
|
||||
|
||||
if (allocStack != null) {
|
||||
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this, "stacktrace: %s", allocStack);
|
||||
if (this.viewParent != null)
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this, "parent stacktrace: %s",
|
||||
viewParent.allocStack);
|
||||
}
|
||||
|
||||
if (freeStack != null) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this, "freed at: %s", freeStack);
|
||||
} else if (viewParent != null && viewParent.freeStack != null) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this, "freed at: %s", viewParent.freeStack);
|
||||
}
|
||||
|
||||
if (Logging.isDebug()) {
|
||||
|
||||
byte[] data = new byte[(this.capacity() > 128) ? 128 : this.capacity()];
|
||||
this.position(0);
|
||||
this.limit(this.capacity());
|
||||
this.get(data);
|
||||
String content = new String(data);
|
||||
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this, "content: %s", content);
|
||||
|
||||
if (this.viewParent != null) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this, "view parent: %s",
|
||||
this.viewParent.toString());
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this, "ref count: %d",
|
||||
this.viewParent.refCount.get());
|
||||
} else {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.buffer, this, "ref count: %d",
|
||||
this.refCount.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BufferPool.free(this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReusableBuffer( capacity=" + this.capacity() + " limit=" + this.limit() + " position="
|
||||
+ this.position() + ")";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An interface which must be implemented by checksum algorithms for XtreemFS.
|
||||
*
|
||||
* 19.08.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public interface ChecksumAlgorithm extends Cloneable {
|
||||
/**
|
||||
* Returns a string that identifies the algorithm, independent of
|
||||
* implementation details.
|
||||
*
|
||||
* @return name of algorithm
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Returns checksum value (as Hex-String) and resets the Algorithm.
|
||||
*
|
||||
* @return checksum
|
||||
*/
|
||||
public long getValue();
|
||||
|
||||
/**
|
||||
* Resets checksum to initial value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public void reset();
|
||||
|
||||
/**
|
||||
* Updates checksum with specified data.
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public void update(ByteBuffer data);
|
||||
|
||||
/**
|
||||
* returns a new instance of the checksum algorithm
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ChecksumAlgorithm clone();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* A Factory for getting checksum algorithms from checksum provider. Implemented
|
||||
* as a Singleton.
|
||||
*
|
||||
* 19.08.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class ChecksumFactory {
|
||||
/**
|
||||
* amount of cached instances/algorithm
|
||||
*/
|
||||
private static int MAX_CACHE_SIZE = 20;
|
||||
|
||||
private static ChecksumFactory self;
|
||||
|
||||
/**
|
||||
* Contains all available checksum algorithms (only one instance).
|
||||
*/
|
||||
private HashMap<String, ChecksumAlgorithm> algorithms;
|
||||
|
||||
/**
|
||||
* Contains all known checksum provider
|
||||
*/
|
||||
private HashMap<String, ChecksumProvider> knownProvider;
|
||||
|
||||
/**
|
||||
* Contains cached instances for all available checksum algorithms.
|
||||
*/
|
||||
private HashMap<String, ConcurrentLinkedQueue<ChecksumAlgorithm>> pool;
|
||||
|
||||
/**
|
||||
* creates a new ChecksumFactory
|
||||
*/
|
||||
private ChecksumFactory() {
|
||||
super();
|
||||
this.algorithms = new HashMap<String, ChecksumAlgorithm>();
|
||||
this.pool = new HashMap<String, ConcurrentLinkedQueue<ChecksumAlgorithm>>();
|
||||
this.knownProvider = new HashMap<String, ChecksumProvider>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instance of ChecksumFactory.
|
||||
*
|
||||
* @return the instance
|
||||
*/
|
||||
public static ChecksumFactory getInstance() {
|
||||
if (self == null) {
|
||||
self = new ChecksumFactory();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of a specific checksum algorithm, if supported.
|
||||
*
|
||||
* @param name
|
||||
* of the algorithm
|
||||
* @return algorithm object or null, if algorithm is not supported
|
||||
*/
|
||||
public ChecksumAlgorithm getAlgorithm(String name)
|
||||
throws NoSuchAlgorithmException {
|
||||
ConcurrentLinkedQueue<ChecksumAlgorithm> cache = pool.get(name);
|
||||
if (cache == null)
|
||||
throw new NoSuchAlgorithmException("algorithm " + name
|
||||
+ " not supported");
|
||||
|
||||
ChecksumAlgorithm algorithm = cache.poll();
|
||||
if (algorithm == null) { // cache is empty
|
||||
return algorithms.get(name).clone(); // create new instance
|
||||
} else {
|
||||
return algorithm; // return caches instance
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of a specific checksum algorithm for caching.
|
||||
*
|
||||
* @param instance
|
||||
* of the algorithm
|
||||
*/
|
||||
public void returnAlgorithm(ChecksumAlgorithm algorithm) {
|
||||
ConcurrentLinkedQueue<ChecksumAlgorithm> cache = pool.get(algorithm
|
||||
.getName());
|
||||
if (cache.size() < MAX_CACHE_SIZE) {
|
||||
algorithm.reset();
|
||||
cache.add(algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new provider to factory and adds all supported algorithms from the
|
||||
* provider to the algorithms-list. NOTE: Existing algorithms will be
|
||||
* overridden when the new provider contains the same algorithm (maybe
|
||||
* another implementation).
|
||||
*
|
||||
* @param provider
|
||||
*/
|
||||
public void addProvider(ChecksumProvider provider) {
|
||||
knownProvider.put(provider.getName(), provider);
|
||||
for (ChecksumAlgorithm algorithm : provider.getSupportedAlgorithms()) {
|
||||
addAlgorithm(algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new Algorithm to factory. NOTE: The same existing algorithm will
|
||||
* be overridden.
|
||||
*
|
||||
* @param algorithm
|
||||
*/
|
||||
public void addAlgorithm(ChecksumAlgorithm algorithm) {
|
||||
algorithms.put(algorithm.getName(), algorithm);
|
||||
pool.put(algorithm.getName(),
|
||||
new ConcurrentLinkedQueue<ChecksumAlgorithm>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a provider, but not the added algorithms.
|
||||
*
|
||||
* @param provider
|
||||
*/
|
||||
public void removeProvider(ChecksumProvider provider) {
|
||||
knownProvider.remove(provider.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an algorithm.
|
||||
*
|
||||
* @param algorithm
|
||||
*/
|
||||
public void removeAlgorithm(String algorithm) {
|
||||
algorithms.remove(algorithm);
|
||||
pool.remove(algorithm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* An abstract class which must be implemented by a checksum provider for
|
||||
* XtreemFS.
|
||||
*
|
||||
* 19.08.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public abstract class ChecksumProvider {
|
||||
/**
|
||||
* contains the supported algorithms
|
||||
*/
|
||||
protected HashMap<String, ChecksumAlgorithm> algorithms;
|
||||
|
||||
protected ChecksumProvider() {
|
||||
super();
|
||||
this.algorithms = new HashMap<String, ChecksumAlgorithm>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the provider.
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
/**
|
||||
* Returns all from this provider supported checksum algorithms.
|
||||
*
|
||||
* @return a collection with ChecksumAlgorithms
|
||||
*/
|
||||
public Collection<ChecksumAlgorithm> getSupportedAlgorithms() {
|
||||
return algorithms.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* adds an algorithm to the map
|
||||
*
|
||||
* @param newAlgorithm
|
||||
*/
|
||||
protected void addAlgorithm(ChecksumAlgorithm newAlgorithm) {
|
||||
this.algorithms.put(newAlgorithm.getName(), newAlgorithm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums;
|
||||
|
||||
/**
|
||||
* An interface for checksum algorithms, which are based on computations on
|
||||
* strings.
|
||||
*
|
||||
* 02.09.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public interface StringChecksumAlgorithm extends ChecksumAlgorithm {
|
||||
/**
|
||||
* Updates checksum with specified data.
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public void digest(String data);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums.algorithms;
|
||||
|
||||
/**
|
||||
* The Adler32 algorithm. It uses the Java internal implementation.
|
||||
*
|
||||
* 19.08.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Adler32 extends JavaChecksumAlgorithm {
|
||||
|
||||
public Adler32() {
|
||||
super(new java.util.zip.Adler32(), "Adler32");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#clone()
|
||||
*/
|
||||
@Override
|
||||
public Adler32 clone() {
|
||||
return new Adler32();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums.algorithms;
|
||||
|
||||
/**
|
||||
* The CRC32 algorithm. It uses the Java internal implementation.
|
||||
*
|
||||
* 19.08.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CRC32 extends JavaChecksumAlgorithm {
|
||||
|
||||
public CRC32() {
|
||||
super(new java.util.zip.CRC32(), "CRC32");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#clone()
|
||||
*/
|
||||
@Override
|
||||
public CRC32 clone() {
|
||||
return new CRC32();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums.algorithms;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
import org.xtreemfs.foundation.checksums.ChecksumAlgorithm;
|
||||
|
||||
/**
|
||||
* An abstract wrapper for Java internal checksums.
|
||||
*
|
||||
* 19.08.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
abstract public class JavaChecksumAlgorithm<RealJavaAlgorithm extends Checksum>
|
||||
implements ChecksumAlgorithm {
|
||||
/**
|
||||
* the class, which really implements the selected algorithm
|
||||
*/
|
||||
protected RealJavaAlgorithm realAlgorithm;
|
||||
|
||||
protected String name;
|
||||
|
||||
public JavaChecksumAlgorithm(RealJavaAlgorithm realAlgorithm, String name) {
|
||||
super();
|
||||
this.realAlgorithm = realAlgorithm;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#digest(java.nio.ByteBuffer)
|
||||
*/
|
||||
@Override
|
||||
public void update(ByteBuffer data) {
|
||||
byte[] array;
|
||||
|
||||
if (data.hasArray()) {
|
||||
array = data.array();
|
||||
} else {
|
||||
array = new byte[data.capacity()];
|
||||
final int oldPos = data.position();
|
||||
data.position(0);
|
||||
data.get(array);
|
||||
data.position(oldPos);
|
||||
}
|
||||
|
||||
realAlgorithm.update(array, 0, array.length);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#getName()
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#getValue()
|
||||
*/
|
||||
@Override
|
||||
public long getValue() {
|
||||
final long tmp = realAlgorithm.getValue();
|
||||
realAlgorithm.reset();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#reset()
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
realAlgorithm.reset();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#clone()
|
||||
*/
|
||||
@Override
|
||||
public abstract JavaChecksumAlgorithm<?> clone();
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums.algorithms;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.xtreemfs.foundation.checksums.StringChecksumAlgorithm;
|
||||
|
||||
/**
|
||||
* The Java algorithm, which is used for string.hashCode(). It uses the Java
|
||||
* internal implementation.
|
||||
*
|
||||
* 02.09.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class JavaHash implements StringChecksumAlgorithm {
|
||||
private Long hash = null;
|
||||
|
||||
private String name = "Java-Hash";
|
||||
|
||||
/**
|
||||
* Updates checksum with specified data.
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public void digest(String data) {
|
||||
this.hash = Long.valueOf(data.hashCode());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumAlgorithm#digest(java.nio.ByteBuffer)
|
||||
*/
|
||||
@Override
|
||||
public void update(ByteBuffer data) {
|
||||
byte[] array;
|
||||
|
||||
if (data.hasArray()) {
|
||||
array = data.array();
|
||||
} else {
|
||||
array = new byte[data.capacity()];
|
||||
final int oldPos = data.position();
|
||||
data.position(0);
|
||||
data.get(array);
|
||||
data.position(oldPos);
|
||||
}
|
||||
|
||||
this.hash = (long)new String(array).hashCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumAlgorithm#getName()
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumAlgorithm#getValue()
|
||||
*/
|
||||
@Override
|
||||
public long getValue() {
|
||||
long value;
|
||||
if (this.hash != null)
|
||||
value = this.hash;
|
||||
else
|
||||
value = 0;
|
||||
reset();
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumAlgorithm#reset()
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
hash = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#clone()
|
||||
*/
|
||||
@Override
|
||||
public JavaHash clone() {
|
||||
return new JavaHash();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums.algorithms;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.xtreemfs.foundation.checksums.StringChecksumAlgorithm;
|
||||
|
||||
/**
|
||||
* The SDBM algorithm.
|
||||
*
|
||||
* 02.09.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class SDBM implements StringChecksumAlgorithm {
|
||||
private Long hash = null;
|
||||
|
||||
private String name = "SDBM";
|
||||
|
||||
/**
|
||||
* Updates checksum with specified data.
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public void digest(String data) {
|
||||
this.hash = sdbmHash(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumAlgorithm#digest(java.nio.ByteBuffer)
|
||||
*/
|
||||
@Override
|
||||
public void update(ByteBuffer data) {
|
||||
byte[] array;
|
||||
|
||||
if (data.hasArray()) {
|
||||
array = data.array();
|
||||
} else {
|
||||
array = new byte[data.capacity()];
|
||||
final int oldPos = data.position();
|
||||
data.position(0);
|
||||
data.get(array);
|
||||
data.position(oldPos);
|
||||
}
|
||||
|
||||
this.hash = sdbmHash(new String(array));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumAlgorithm#getName()
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumAlgorithm#getValue()
|
||||
*/
|
||||
@Override
|
||||
public long getValue() {
|
||||
long value;
|
||||
if (this.hash != null)
|
||||
value = this.hash;
|
||||
else
|
||||
value = 0;
|
||||
reset();
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumAlgorithm#reset()
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
hash = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.common.checksum.ChecksumAlgorithm#clone()
|
||||
*/
|
||||
@Override
|
||||
public SDBM clone() {
|
||||
return new SDBM();
|
||||
}
|
||||
|
||||
/**
|
||||
* SDBM algorithm
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
protected static long sdbmHash(String str) {
|
||||
long hash = 0;
|
||||
for (int c : str.toCharArray()) {
|
||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.checksums.provider;
|
||||
|
||||
import org.xtreemfs.foundation.checksums.ChecksumProvider;
|
||||
|
||||
/**
|
||||
* A provider for Java internal checksums. offers the following algorithms:
|
||||
* Adler32, CRC32, MD5, Java-Hash
|
||||
*
|
||||
* 19.08.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class JavaChecksumProvider extends ChecksumProvider {
|
||||
private static String NAME = "Java Checksum Provider";
|
||||
|
||||
/**
|
||||
* creates a new JavaChecksumProvider
|
||||
*/
|
||||
public JavaChecksumProvider() {
|
||||
super();
|
||||
|
||||
addAlgorithm(new org.xtreemfs.foundation.checksums.algorithms.Adler32());
|
||||
addAlgorithm(new org.xtreemfs.foundation.checksums.algorithms.CRC32());
|
||||
/*try {
|
||||
addAlgorithm(new org.xtreemfs.foundation.checksums.algorithms.JavaMessageDigestAlgorithm(
|
||||
"MD5", "MD5"));
|
||||
addAlgorithm(new org.xtreemfs.foundation.checksums.algorithms.JavaMessageDigestAlgorithm(
|
||||
"SHA1", "SHA-1"));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this, e.getMessage()
|
||||
+ " in your java-installation");
|
||||
}*/
|
||||
addAlgorithm(new org.xtreemfs.foundation.checksums.algorithms.JavaHash());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.xtreemfs.foundation.checksums.ChecksumProvider#getName()
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Bjoern Kolbeck, Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.json;
|
||||
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.CharBuffer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class JSONCharBufferString implements JSONInput {
|
||||
|
||||
CharBuffer cb;
|
||||
|
||||
/** Creates a new instance of JSONCharBufferString */
|
||||
public JSONCharBufferString(CharBuffer cb) {
|
||||
assert (cb != null);
|
||||
|
||||
this.cb = cb;
|
||||
this.cb.position(0);
|
||||
}
|
||||
|
||||
public char read() throws JSONException {
|
||||
try {
|
||||
return cb.get();
|
||||
} catch(BufferUnderflowException ex) {
|
||||
throw new JSONException("Reached end of buffer");
|
||||
}
|
||||
}
|
||||
|
||||
public int skip(int skip) {
|
||||
try {
|
||||
|
||||
cb.position(cb.position()+skip);
|
||||
|
||||
return skip;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "JSONCharBufferString backed by "+cb.toString();
|
||||
}
|
||||
|
||||
public boolean hasMore() {
|
||||
return cb.hasRemaining();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Bjoern Kolbeck, Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.json;
|
||||
|
||||
/**
|
||||
* Thrown by the JSON parser and writer.
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class JSONException extends java.lang.Exception {
|
||||
|
||||
/***/
|
||||
private static final long serialVersionUID = 2422241603599209392L;
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>JSONException</code> without detail
|
||||
* message.
|
||||
*/
|
||||
public JSONException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>JSONException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param msg
|
||||
* the detail message.
|
||||
*/
|
||||
public JSONException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Bjoern Kolbeck, Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.json;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public interface JSONInput {
|
||||
|
||||
|
||||
/**
|
||||
* reads a single char
|
||||
*
|
||||
* @return the character at the current position is returned
|
||||
*/
|
||||
public char read() throws JSONException;
|
||||
|
||||
/**
|
||||
* It checks if there are more characters
|
||||
*/
|
||||
public boolean hasMore();
|
||||
|
||||
/**
|
||||
* Skips skip characters
|
||||
*
|
||||
* @param skip
|
||||
* num characters to skip
|
||||
* @return the number of characters skipped
|
||||
*/
|
||||
public int skip(int skip);
|
||||
|
||||
/**
|
||||
* Get a string representation.
|
||||
*
|
||||
* @return A string representation of the this JSONString.
|
||||
*/
|
||||
public String toString();
|
||||
|
||||
}
|
||||
329
java/foundation/src/org/xtreemfs/foundation/json/JSONParser.java
Normal file
329
java/foundation/src/org/xtreemfs/foundation/json/JSONParser.java
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Bjoern Kolbeck, Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON Parser routines. This parser accepts any value as top level element, not
|
||||
* just an object or array.
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class JSONParser {
|
||||
|
||||
/**
|
||||
* Creates a new instance of JSONParser
|
||||
*/
|
||||
public JSONParser() {
|
||||
}
|
||||
|
||||
private static String parseString(JSONInput input) throws JSONException {
|
||||
|
||||
boolean nonEscaped = true;
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
||||
while (input.hasMore()) {
|
||||
char ch = input.read();
|
||||
|
||||
if (nonEscaped) {
|
||||
if (ch == '\\') {
|
||||
nonEscaped = false;
|
||||
continue;
|
||||
}
|
||||
else if (ch == '"') {
|
||||
return str.toString();
|
||||
}
|
||||
else {
|
||||
str.append(ch);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ch == 'n') {
|
||||
str.append('\n');
|
||||
} else if (ch == 'r') {
|
||||
str.append('\r');
|
||||
} else if (ch == 't') {
|
||||
str.append('\t');
|
||||
} else {
|
||||
str.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
nonEscaped = true;
|
||||
}
|
||||
|
||||
throw new JSONException("[ E | JSONParser ] Unexpected end while parsing string");
|
||||
|
||||
}
|
||||
|
||||
private static Object parseNumber(JSONInput input) throws JSONException {
|
||||
|
||||
StringBuilder str = new StringBuilder();
|
||||
input.skip(-1);
|
||||
|
||||
boolean isFP = false;
|
||||
|
||||
while (input.hasMore()) {
|
||||
char ch = input.read();
|
||||
|
||||
if ((ch == '-') || (ch >= '0') && (ch <= '9')) {
|
||||
str.append(ch);
|
||||
} else if ((ch == '.') || (ch == 'E') || (ch == 'e')) {
|
||||
str.append(ch);
|
||||
isFP = true;
|
||||
} else {
|
||||
input.skip(-1);
|
||||
if (isFP)
|
||||
return new BigDecimal(str.toString());
|
||||
else
|
||||
return Long.valueOf(str.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (isFP)
|
||||
return new BigDecimal(str.toString());
|
||||
else
|
||||
return Long.valueOf(str.toString());
|
||||
}
|
||||
|
||||
private static Object parseArray(JSONInput input) throws JSONException {
|
||||
LinkedList<Object> arr = new LinkedList<Object>();
|
||||
|
||||
while (input.hasMore()) {
|
||||
char ch = input.read();
|
||||
|
||||
if (ch == ']') {
|
||||
return arr;
|
||||
} else if (ch == ',') {
|
||||
arr.add(parseJSON(input));
|
||||
} else if ((ch == ' ') || (ch == '\t')) {
|
||||
continue;
|
||||
} else {
|
||||
input.skip(-1);
|
||||
arr.add(parseJSON(input));
|
||||
}
|
||||
}
|
||||
|
||||
throw new JSONException("[ E | JSONParser ] Unexpected end while parsing array");
|
||||
}
|
||||
|
||||
private static Object parseObject(JSONInput input) throws JSONException {
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
while (input.hasMore()) {
|
||||
char ch = input.read();
|
||||
|
||||
if (ch == '}') {
|
||||
return map;
|
||||
}
|
||||
// skip all ws
|
||||
if ((ch == ' ') || (ch == '\t')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = parseString(input);
|
||||
ch = input.read();
|
||||
|
||||
while ((ch == ' ') || (ch == '\t')) {
|
||||
ch = input.read();
|
||||
}
|
||||
|
||||
if (ch != ':') {
|
||||
throw new JSONException("[ E | JSONParser ] Unexpected token '"
|
||||
+ ((char) ch) + "' or EOF. Expected : in Object.");
|
||||
}
|
||||
|
||||
while ((ch == ' ') || (ch == '\t')) {
|
||||
ch = input.read();
|
||||
}
|
||||
|
||||
Object value = parseJSON(input);
|
||||
map.put(name, value);
|
||||
ch = input.read();
|
||||
|
||||
while ((ch == ' ') || (ch == '\t')) {
|
||||
ch = input.read();
|
||||
}
|
||||
|
||||
if (ch == '}') {
|
||||
return map;
|
||||
}
|
||||
if (ch != ',') {
|
||||
throw new JSONException("[ E | JSONParser ] Unexpected token '"
|
||||
+ ((char) ch) + "' or EOF. Expected , or } in Object.");
|
||||
}
|
||||
}
|
||||
|
||||
throw new JSONException("[ E | JSONParser ] Unexpected end while parsing object");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON message.
|
||||
*
|
||||
* @return the objects encoded in input.
|
||||
* @attention This routine may cause a StackOverflow exception when parsing
|
||||
* incorrect, very deep or maliciously malformed JSON messages.
|
||||
* @param input
|
||||
* the JSON string
|
||||
* @throws org.xtreemos.wp34.mrc.utils.JSONException
|
||||
* if input is not valid JSON
|
||||
*/
|
||||
public static Object parseJSON(JSONInput input) throws JSONException {
|
||||
|
||||
while (input.hasMore()) {
|
||||
char ch = input.read();
|
||||
|
||||
if (ch == '[') {
|
||||
return parseArray(input);
|
||||
} else if (ch == '{') {
|
||||
return parseObject(input);
|
||||
} else if (ch == '"') {
|
||||
return parseString(input);
|
||||
} else if ((ch == '-') || ((ch >= '0') && (ch <= '9'))) {
|
||||
return parseNumber(input);
|
||||
} else if (ch == 't') {
|
||||
input.skip(3);
|
||||
return Boolean.valueOf(true);
|
||||
} else if (ch == 'f') {
|
||||
input.skip(4);
|
||||
return Boolean.valueOf(false);
|
||||
} else if (ch == 'n') {
|
||||
input.skip(3);
|
||||
return null;
|
||||
} else if ((ch == ' ') || (ch == '\t')) {
|
||||
continue;
|
||||
} else {
|
||||
throw new JSONException("[ E | JSONParser ] Unexpected token '"
|
||||
+ ((char) ch) + "' expected Object, Array or Value.");
|
||||
}
|
||||
}
|
||||
|
||||
throw new JSONException("[ E | JSONParser ] Unexpected end while parsing root element");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON encoded message from an object. Can handle Boolean,
|
||||
* Integer, Long, BigDecimal, List and Map.
|
||||
*
|
||||
* @param input
|
||||
* object to encode, objects can be nested.
|
||||
* @return a JSON encoded message
|
||||
* @throws org.xtreemos.wp34.mrc.utils.JSONException
|
||||
* if there are one or more objects it cannot encode
|
||||
*/
|
||||
public static String writeJSON(Object input) throws JSONException {
|
||||
return writeJSON(input,new StringBuilder()).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON encoded message from an object. Can handle Boolean,
|
||||
* Integer, Long, BigDecimal, List and Map.
|
||||
*
|
||||
* @param input
|
||||
* object to encode, objects can be nested.
|
||||
* @return a JSON encoded message
|
||||
* @throws org.xtreemos.wp34.mrc.utils.JSONException
|
||||
* if there are one or more objects it cannot encode
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static StringBuilder writeJSON(Object input, StringBuilder result) throws JSONException {
|
||||
|
||||
if (input == null) {
|
||||
return result.append("null");
|
||||
} else if (input instanceof Boolean) {
|
||||
return result.append(((Boolean) input).booleanValue() ? "true" : "false");
|
||||
} else if (input instanceof BigDecimal) {
|
||||
return result.append(((BigDecimal) input).toString());
|
||||
} else if (input instanceof Integer) {
|
||||
return result.append((Integer) input);
|
||||
} else if (input instanceof Long) {
|
||||
return result.append((Long) input);
|
||||
} else if (input instanceof String) {
|
||||
return writeJSONString(input,result);
|
||||
} else if (input instanceof Map) {
|
||||
return writeJSONObject(input,result);
|
||||
} else if (input instanceof Collection) {
|
||||
return writeJSONArray(input,result);
|
||||
} else {
|
||||
throw new JSONException(
|
||||
"[ E | JSONParser ] Unexpected Object type: "
|
||||
+ input.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static StringBuilder writeJSONObject(Object input, StringBuilder result) throws JSONException {
|
||||
Map<Object, Object> map = (Map) input;
|
||||
result.append("{");
|
||||
int i = 1;
|
||||
for (Object key : map.keySet()) {
|
||||
writeJSONString(key.toString(),result);
|
||||
result.append(":");
|
||||
writeJSON(map.get(key),result);
|
||||
if (i < map.size())
|
||||
result.append(",");
|
||||
i++;
|
||||
}
|
||||
return result.append("}");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static StringBuilder writeJSONArray(Object input, StringBuilder result) throws JSONException {
|
||||
Collection<Object> arr = (Collection<Object>) input;
|
||||
result.append("[");
|
||||
int i = 1;
|
||||
for (Object obj : arr) {
|
||||
writeJSON(obj,result);
|
||||
if (i < arr.size())
|
||||
result.append(",");
|
||||
i++;
|
||||
}
|
||||
return result.append("]");
|
||||
}
|
||||
|
||||
private static StringBuilder writeJSONString(Object input, StringBuilder result) {
|
||||
/*
|
||||
* This is 10 times faster than using str.replace
|
||||
*/
|
||||
final String str = input.toString();
|
||||
result.append("\"");
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char ch = str.charAt(i);
|
||||
|
||||
switch (ch) {
|
||||
case '\n' : result.append("\\n"); break;
|
||||
case '\r' : result.append("\\r"); break;
|
||||
case '\t' : result.append("\\t"); break;
|
||||
case '"' : result.append("\\\""); break;
|
||||
case '\\' : result.append("\\\\"); break;
|
||||
case '/' : result.append("\\/"); break;
|
||||
default: result.append(ch);
|
||||
}
|
||||
}
|
||||
result.append("\"");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static String toJSON(Object... args) throws JSONException {
|
||||
List<Object> argList = new ArrayList<Object>(args.length);
|
||||
for (Object arg : args)
|
||||
argList.add(arg);
|
||||
|
||||
return writeJSON(argList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Bjoern Kolbeck, Jan Stender,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.json;
|
||||
|
||||
|
||||
/**
|
||||
* This class is necessary because the StringReader cannot skip back once the
|
||||
* end is reached.
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class JSONString implements JSONInput {
|
||||
|
||||
String str;
|
||||
|
||||
int position;
|
||||
|
||||
/**
|
||||
* Creates a new instance of JSONString
|
||||
*
|
||||
* @param str
|
||||
* the JSON message
|
||||
*/
|
||||
public JSONString(String str) {
|
||||
this.str = str;
|
||||
position = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* reads a single char
|
||||
*
|
||||
* @return the character at the current position is returned
|
||||
*/
|
||||
public char read() throws JSONException {
|
||||
try {
|
||||
return str.charAt(position++);
|
||||
}
|
||||
catch (StringIndexOutOfBoundsException ex) {
|
||||
throw new JSONException("Reach the end of the string");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public boolean hasMore() {
|
||||
return position < str.length();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Skips skip characters
|
||||
*
|
||||
* @param skip
|
||||
* num characters to skip
|
||||
* @return the number of characters skipped
|
||||
*/
|
||||
public int skip(int skip) {
|
||||
if (((position + skip) < 0) || ((position + skip) >= str.length())) {
|
||||
return 0;
|
||||
} else {
|
||||
position = position + skip;
|
||||
return skip;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string representation.
|
||||
*
|
||||
* @return A string representation of the this JSONString.
|
||||
*/
|
||||
public String toString() {
|
||||
return "JSONString pos=" + position + " str=" + str;
|
||||
}
|
||||
|
||||
}
|
||||
274
java/foundation/src/org/xtreemfs/foundation/logging/Logging.java
Normal file
274
java/foundation/src/org/xtreemfs/foundation/logging/Logging.java
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.logging;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class Logging {
|
||||
|
||||
public enum Category {
|
||||
/**
|
||||
* enable logging for all categories (no real category)
|
||||
*/
|
||||
all,
|
||||
/**
|
||||
* logs messages pertaining to buffers
|
||||
*/
|
||||
buffer,
|
||||
/**
|
||||
* log messaages pertaining to service lifecycles (threads)
|
||||
*/
|
||||
lifecycle,
|
||||
/**
|
||||
* network-related log messages
|
||||
*/
|
||||
net,
|
||||
/**
|
||||
* authorization-related log messages
|
||||
*/
|
||||
auth,
|
||||
/**
|
||||
* log messages pertaining to the request flow through the stages
|
||||
*/
|
||||
stage,
|
||||
/**
|
||||
* log messages pertaining to any kind of request processing
|
||||
*/
|
||||
proc,
|
||||
/**
|
||||
*
|
||||
*/
|
||||
misc,
|
||||
/**
|
||||
* log messages pertaining storage on OSD or database access on MRC/DIR
|
||||
*/
|
||||
storage,
|
||||
/**
|
||||
* logs messages pertaining to replication
|
||||
*/
|
||||
replication,
|
||||
/**
|
||||
* logs messages from additional tools
|
||||
*/
|
||||
tool,
|
||||
/**
|
||||
* logs messages from tests
|
||||
*/
|
||||
test
|
||||
}
|
||||
|
||||
protected static final char ABBREV_LEVEL_INFO = 'I';
|
||||
|
||||
protected static final char ABBREV_LEVEL_DEBUG = 'D';
|
||||
|
||||
protected static final char ABBREV_LEVEL_WARN = 'W';
|
||||
|
||||
protected static final char ABBREV_LEVEL_ERROR = 'E';
|
||||
|
||||
protected static final char ABBREV_LEVEL_TRACE = 'T';
|
||||
|
||||
public static final int LEVEL_EMERG = 0;
|
||||
|
||||
public static final int LEVEL_ALERT = 1;
|
||||
|
||||
public static final int LEVEL_CRIT = 2;
|
||||
|
||||
public static final int LEVEL_ERROR = 3;
|
||||
|
||||
public static final int LEVEL_WARN = 4;
|
||||
|
||||
public static final int LEVEL_NOTICE = 5;
|
||||
|
||||
public static final int LEVEL_INFO = 6;
|
||||
|
||||
public static final int LEVEL_DEBUG = 7;
|
||||
|
||||
public static final String FORMAT_PATTERN = "[ %c | %-20s | %-15s | %3d | %15s ] %s";
|
||||
|
||||
private static PrintStream out = System.out;
|
||||
|
||||
protected static Logging instance;
|
||||
|
||||
protected static boolean tracingEnabled = false;
|
||||
|
||||
private final int level;
|
||||
|
||||
private final int catMask;
|
||||
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* Creates a new instance of Logging
|
||||
*/
|
||||
private Logging(int level, int catMask) {
|
||||
|
||||
if (level < 0)
|
||||
this.level = 0;
|
||||
else
|
||||
this.level = level;
|
||||
|
||||
this.catMask = catMask;
|
||||
|
||||
instance = this;
|
||||
|
||||
System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static void redirect(PrintStream out) {
|
||||
Logging.out = out;
|
||||
}
|
||||
|
||||
public static String truncateString(String string, int maxLength) {
|
||||
return (string.length() > maxLength) ?
|
||||
(string.substring(0, maxLength - 3) + "...") : string;
|
||||
}
|
||||
|
||||
public static void logMessage(int level, Category cat, Object me, String formatPattern, Object... args) {
|
||||
checkIfInitializedOrThrow();
|
||||
|
||||
// if the level is appropriate as well as the category, or the category
|
||||
// is 'all', log the message
|
||||
if (level <= instance.level && (cat == Category.all || (2 << cat.ordinal() & instance.catMask) > 0)) {
|
||||
|
||||
char levelName = getLevelName(level);
|
||||
|
||||
out.println(String.format(FORMAT_PATTERN, levelName,
|
||||
me == null ? "-" : truncateString(me instanceof Class ? ((Class) me).getSimpleName(): me.getClass().getSimpleName(), 20),
|
||||
truncateString(Thread.currentThread().getName(), 15),
|
||||
Thread.currentThread().getId(),
|
||||
getTimeStamp(),
|
||||
String.format(formatPattern, args)));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkIfInitializedOrThrow() {
|
||||
if (instance == null) {
|
||||
throw new RuntimeException(
|
||||
"Cannot log message because the logging is not initialized yet. Did you forget to call Logging.start(...) in your code?");
|
||||
}
|
||||
}
|
||||
|
||||
public static void logMessage(int level, Object me, String formatPattern, Object... args) {
|
||||
logMessage(level, Category.all, me, formatPattern, args);
|
||||
}
|
||||
|
||||
public static void logError(int level, Object me, Throwable msg) {
|
||||
checkIfInitializedOrThrow();
|
||||
|
||||
// if the level is appropriate, log the message
|
||||
if (level <= instance.level) {
|
||||
|
||||
char levelName = getLevelName(level);
|
||||
|
||||
out.println(String.format(FORMAT_PATTERN, levelName,
|
||||
me == null ? "-" : (me instanceof Class ? ((Class) me).getSimpleName(): me.getClass().getSimpleName()),
|
||||
Thread.currentThread().getName(), Thread.currentThread().getId(),
|
||||
getTimeStamp(), msg.toString()));
|
||||
for (StackTraceElement elem : msg.getStackTrace()) {
|
||||
out.println(" ... " + elem.toString());
|
||||
}
|
||||
if (msg.getCause() != null) {
|
||||
out.println(String.format(FORMAT_PATTERN, levelName, me == null ? "-" : me.getClass()
|
||||
.getSimpleName(), Thread.currentThread().getName(), Thread.currentThread().getId(),
|
||||
getTimeStamp(), "root cause: " + msg.getCause()));
|
||||
for (StackTraceElement elem : msg.getCause().getStackTrace()) {
|
||||
out.println(" ... " + elem.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void logUserError(int level, Category cat, Object me, Throwable msg) {
|
||||
checkIfInitializedOrThrow();
|
||||
|
||||
// if the level is appropriate as well as the category, or the category
|
||||
// is 'all', log the message
|
||||
if (level <= instance.level && (cat == Category.all || (2 << cat.ordinal() & instance.catMask) > 0)) {
|
||||
|
||||
char levelName = getLevelName(level);
|
||||
|
||||
out.println(String.format(FORMAT_PATTERN, levelName, me == null ? "-" : me.getClass()
|
||||
.getSimpleName(), Thread.currentThread().getName(), Thread.currentThread().getId(),
|
||||
getTimeStamp(), msg.toString()));
|
||||
for (StackTraceElement elem : msg.getStackTrace()) {
|
||||
out.println(" ... " + elem.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static char getLevelName(int level) {
|
||||
switch (level) {
|
||||
case LEVEL_EMERG:
|
||||
case LEVEL_ALERT:
|
||||
case LEVEL_CRIT:
|
||||
case LEVEL_ERROR:
|
||||
return ABBREV_LEVEL_ERROR;
|
||||
case LEVEL_WARN:
|
||||
return ABBREV_LEVEL_WARN;
|
||||
case LEVEL_NOTICE:
|
||||
case LEVEL_INFO:
|
||||
return ABBREV_LEVEL_INFO;
|
||||
case LEVEL_DEBUG:
|
||||
return ABBREV_LEVEL_DEBUG;
|
||||
default:
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void start(int level, Category... categories) {
|
||||
if (instance == null) {
|
||||
|
||||
int catMask = 0;
|
||||
for (Category cat : categories) {
|
||||
|
||||
if (cat == Category.all)
|
||||
catMask = -1;
|
||||
|
||||
catMask |= 2 << cat.ordinal();
|
||||
}
|
||||
|
||||
if(categories.length == 0)
|
||||
catMask = -1;
|
||||
|
||||
instance = new Logging(level, catMask);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDebug() {
|
||||
if (instance == null)
|
||||
return false;
|
||||
else
|
||||
return instance.level >= LEVEL_DEBUG;
|
||||
}
|
||||
|
||||
public static boolean isInfo() {
|
||||
if (instance == null)
|
||||
return false;
|
||||
else
|
||||
return instance.level >= LEVEL_INFO;
|
||||
}
|
||||
|
||||
public static boolean isNotice() {
|
||||
if (instance == null)
|
||||
return false;
|
||||
else
|
||||
return instance.level >= LEVEL_NOTICE;
|
||||
}
|
||||
|
||||
private static String getTimeStamp() {
|
||||
return dateFormat.format(new Date());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.logging;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
public static final char LEVEL_INFO = 'I';
|
||||
public static final char LEVEL_DEBUG = 'D';
|
||||
public static final char LEVEL_WARN = 'W';
|
||||
public static final char LEVEL_ERROR = 'E';
|
||||
|
||||
|
||||
public static void logMessage(char level, Object me, String msg) {
|
||||
if (me == null) {
|
||||
System.out.println(String.format("[ %c | %-20s | %3d ] %s",
|
||||
level,"?",Thread.currentThread().getId(),
|
||||
msg));
|
||||
} else {
|
||||
System.out.println(String.format("[ %c | %-20s | %3d ] %s",
|
||||
level,me.getClass().getSimpleName(),Thread.currentThread().getId(),
|
||||
msg));
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a new instance of Utils */
|
||||
public Utils() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.monitoring;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* The class provides the ability to monitor data. It could monitor multiple data for each key.<br>
|
||||
* NOTE: This class is thread-safe. <br>
|
||||
* 22.07.2009
|
||||
*/
|
||||
public class ListMonitoring<V> extends Monitoring<List<V>> {
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.monitoring;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* The class provides the ability to monitor data. The data must be added via push (put-method). For getting the
|
||||
* contained data poll (get-method) and push (via listeners) is supported.<br>
|
||||
* NOTE: This class is thread-safe. <br>
|
||||
* 22.07.2009
|
||||
*/
|
||||
public class Monitoring<V> {
|
||||
/**
|
||||
* contains the monitored data
|
||||
*/
|
||||
protected ConcurrentHashMap<String, V> datasets;
|
||||
|
||||
/**
|
||||
* contains the registered listeners
|
||||
*/
|
||||
protected ConcurrentHashMap<String, List<MonitoringListener<V>>> listeners;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Monitoring() {
|
||||
datasets = new ConcurrentHashMap<String, V>();
|
||||
listeners = new ConcurrentHashMap<String, List<MonitoringListener<V>>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the value to the given key and overwrites the old value. Notifies all associated listeners.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
* @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public V put(String key, V value) {
|
||||
assert (key != null && value != null);
|
||||
|
||||
V oldValue = datasets.put(key, value);
|
||||
|
||||
// check if listeners are registered
|
||||
if (listeners.containsKey(key)) {
|
||||
MonitoringEvent<V> event = new MonitoringEvent<V>(this, key, value);
|
||||
for (MonitoringListener<V> listener : listeners.get(key))
|
||||
listener.valueAddedOrChanged(event);
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @return
|
||||
* @see java.util.HashMap#get(java.lang.Object)
|
||||
*/
|
||||
public V get(String key) {
|
||||
return datasets.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @return
|
||||
* @see java.util.HashMap#containsKey(java.lang.Object)
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
return datasets.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see java.util.HashMap#entrySet()
|
||||
*/
|
||||
public Set<Entry<String, V>> entrySet() {
|
||||
return datasets.entrySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @return
|
||||
* @see java.util.HashMap#remove(java.lang.Object)
|
||||
*/
|
||||
public V remove(String key) {
|
||||
return datasets.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see java.util.HashMap#size()
|
||||
*/
|
||||
public int size() {
|
||||
return datasets.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see java.util.AbstractMap#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return datasets.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener for the specified key. If the value of the key changes, the associated listeners
|
||||
* will be notified.
|
||||
*
|
||||
* @param key
|
||||
* @param listener
|
||||
*/
|
||||
public void registerListener(String key, MonitoringListener<V> listener) {
|
||||
List<MonitoringListener<V>> list = listeners.get(key);
|
||||
if (list == null) {
|
||||
list = new CopyOnWriteArrayList<MonitoringListener<V>>();
|
||||
listeners.put(key, list);
|
||||
}
|
||||
list.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given listener for the given key.
|
||||
*
|
||||
* @param key
|
||||
* @param listener
|
||||
*/
|
||||
public void unregisterListener(String key, MonitoringListener<V> listener) {
|
||||
List<MonitoringListener<V>> list = listeners.get(key);
|
||||
if (list != null) {
|
||||
int index = list.indexOf(listener);
|
||||
if (index != -1)
|
||||
list.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* system-wide stuff
|
||||
*/
|
||||
private static boolean monitoringEnabled = false;
|
||||
|
||||
/**
|
||||
* enables monitoring for the whole system
|
||||
*/
|
||||
public static void enable() {
|
||||
monitoringEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the monitoringEnabled
|
||||
*/
|
||||
public static boolean isEnabled() {
|
||||
return monitoringEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.monitoring;
|
||||
|
||||
import java.util.EventObject;
|
||||
|
||||
/**
|
||||
* An event which is created, when a value has changed. It piggybacks the key and new value, too. <br>
|
||||
* 22.07.2009
|
||||
*/
|
||||
public class MonitoringEvent<V> extends EventObject {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* the key of the changed value
|
||||
*/
|
||||
String key;
|
||||
/**
|
||||
* the changed value
|
||||
*/
|
||||
V newValue;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class.
|
||||
*
|
||||
* @param source
|
||||
* @param key
|
||||
* @param newValue
|
||||
*/
|
||||
public MonitoringEvent(Object source, String key, V newValue) {
|
||||
super(source);
|
||||
this.key = key;
|
||||
this.newValue = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the key
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value
|
||||
*/
|
||||
public V getNewValue() {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010, Konrad-Zuse-Zentrum fuer Informationstechnik Berlin
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* Neither the name of the Konrad-Zuse-Zentrum fuer Informationstechnik Berlin
|
||||
* nor the names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* AUTHORS: Christian Lorenz (ZIB)
|
||||
*/
|
||||
package org.xtreemfs.foundation.monitoring;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
* A simple listener for notification, if the value of the target has changed. <br>
|
||||
* 22.07.2009
|
||||
*/
|
||||
public interface MonitoringListener<V> extends EventListener {
|
||||
/**
|
||||
* Invoked when a value of the monitored data has changed.
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
public void valueAddedOrChanged(MonitoringEvent<V> event);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.monitoring;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* <br>
|
||||
* 17.08.2009
|
||||
*/
|
||||
public class MonitoringLog implements MonitoringListener<Double> {
|
||||
private static MonitoringLog instance;
|
||||
|
||||
private long monitoringStartTime;
|
||||
|
||||
public static synchronized void initialize(String filepath) throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new MonitoringLog();
|
||||
|
||||
instance.monitoringStartTime = System.currentTimeMillis();
|
||||
|
||||
// file to write to
|
||||
// (new File(filepath)).getParentFile().mkdirs();
|
||||
// instance.out = new FileWriter(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueAddedOrChanged(MonitoringEvent<Double> event) {
|
||||
monitor(event.getKey(), event.getNewValue().toString());
|
||||
}
|
||||
|
||||
public static synchronized void monitor(String key, String value) {
|
||||
long time = (System.currentTimeMillis() - instance.monitoringStartTime) / 1000;
|
||||
System.out.println("[" + time + "s]\t" + key + "\t:\t" + value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void registerFor(Monitoring monitoring, String... keys) {
|
||||
for (String key : keys)
|
||||
monitoring.registerListener(key, instance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.monitoring;
|
||||
|
||||
/**
|
||||
* The class provides the ability to monitor numeric data. It also provides methods some methods for special
|
||||
* cases than only overwriting the old value.<br>
|
||||
* NOTE: This class is thread-safe. <br>
|
||||
* 22.07.2009
|
||||
*/
|
||||
public class NumberMonitoring extends Monitoring<Double> {
|
||||
/**
|
||||
* Saves the value only if the new value is smaller than the old one.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Double putSmaller(String key, Double value) {
|
||||
Double oldValue = super.get(key);
|
||||
if (oldValue != null) {
|
||||
if (oldValue > value)
|
||||
return super.put(key, value);
|
||||
else
|
||||
return value;
|
||||
} else
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the value only if the new value is larger than the old one.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Double putLarger(String key, Double value) {
|
||||
Double oldValue = super.get(key);
|
||||
if (oldValue != null) {
|
||||
if (oldValue < value)
|
||||
return super.put(key, value);
|
||||
else
|
||||
return value;
|
||||
} else
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the average of the old and new value.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Double putAverage(String key, Double value) {
|
||||
Double oldValue = super.get(key);
|
||||
if (oldValue != null) {
|
||||
return super.put(key, (oldValue + value) / 2);
|
||||
} else
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the old value about new value.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Double putIncreaseFor(String key, Double value) {
|
||||
Double oldValue = super.get(key);
|
||||
if (oldValue != null) {
|
||||
return super.put(key, oldValue + value);
|
||||
} else
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the old value about new value.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Double putDecreaseFor(String key, Double value) {
|
||||
Double oldValue = super.get(key);
|
||||
if (oldValue != null) {
|
||||
return super.put(key, oldValue - value);
|
||||
} else
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special method for Longs.
|
||||
*
|
||||
* @see org.xtreemfs.foundation.monitoring.Monitoring#put(java.lang.String, java.lang.Object)
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Long putLong(String key, Long value) {
|
||||
Double oldValue = super.put(key, value.doubleValue());
|
||||
return (oldValue == null) ? null : oldValue.longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Special method for Longs. Saves the value only if the new value is smaller than the old one.
|
||||
*
|
||||
* @see org.xtreemfs.foundation.monitoring.NumberMonitoring#putSmaller(java.lang.String, java.lang.Double)
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Long putSmallerLong(String key, Long value) {
|
||||
Double oldValue = this.putSmaller(key, value.doubleValue());
|
||||
return (oldValue == null) ? null : oldValue.longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Special method for Longs. Saves the value only if the new value is larger than the old one.
|
||||
*
|
||||
* @see org.xtreemfs.foundation.monitoring.NumberMonitoring#putLarger(java.lang.String, java.lang.Double)
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Long putLargerLong(String key, Long value) {
|
||||
Double oldValue = this.putLarger(key, value.doubleValue());
|
||||
return (oldValue == null) ? null : oldValue.longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Special method for Longs. Saves the average of the old and new value.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Long putAverageLong(String key, Long value) {
|
||||
Double oldValue = super.get(key);
|
||||
if (oldValue != null) {
|
||||
this.put(key, (oldValue + value) / 2d).longValue();
|
||||
return oldValue.longValue();
|
||||
} else
|
||||
return this.putLong(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the old value about new value.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Long putIncreaseForLong(String key, Long value) {
|
||||
Double oldValue = this.putIncreaseFor(key, value.doubleValue());
|
||||
return (oldValue == null) ? null : oldValue.longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the old value about new value.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Long putDecreaseForLong(String key, Long value) {
|
||||
Double oldValue = this.putDecreaseFor(key, value.doubleValue());
|
||||
return (oldValue == null) ? null : oldValue.longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Long getLong(String key) {
|
||||
Double value = super.get(key);
|
||||
return (value == null) ? null : value.longValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class Schemes {
|
||||
|
||||
public static final String SCHEME_PBRPC = "pbrpc";
|
||||
public static final String SCHEME_PBRPCG = "pbrpcg";
|
||||
public static final String SCHEME_PBRPCS = "pbrpcs";
|
||||
public static final String SCHEME_PBRPCU = "pbrpcu";
|
||||
|
||||
public static String getScheme(boolean sslEnabled, boolean gridSSL) {
|
||||
if (sslEnabled) {
|
||||
if (gridSSL) {
|
||||
return SCHEME_PBRPCG;
|
||||
} else {
|
||||
return SCHEME_PBRPCS;
|
||||
}
|
||||
} else {
|
||||
return SCHEME_PBRPC;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Christian Lorenz, Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.channels;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
/**
|
||||
* A abstraction of the SocketChannel
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class ChannelIO {
|
||||
|
||||
protected final SocketChannel channel;
|
||||
|
||||
protected Certificate[] certs;
|
||||
|
||||
protected Object attachment;
|
||||
|
||||
public ChannelIO(SocketChannel channel) {
|
||||
this.channel = channel;
|
||||
this.certs = null;
|
||||
attachment = null;
|
||||
}
|
||||
|
||||
public SelectableChannel configureBlocking(boolean block)
|
||||
throws IOException {
|
||||
return channel.configureBlocking(block);
|
||||
}
|
||||
|
||||
public boolean connect(SocketAddress remote) throws IOException {
|
||||
return this.channel.connect(remote);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
channel.socket().close();
|
||||
channel.close();
|
||||
}
|
||||
|
||||
public boolean isBlocking() {
|
||||
return channel.isBlocking();
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
public SelectionKey keyFor(Selector sel) {
|
||||
return channel.keyFor(sel);
|
||||
}
|
||||
|
||||
public int read(ByteBuffer dst) throws IOException, NotYetConnectedException {
|
||||
return channel.read(dst);
|
||||
}
|
||||
|
||||
public SelectionKey register(Selector sel, int ops, Object att)
|
||||
throws ClosedChannelException {
|
||||
return channel.register(sel, ops, att);
|
||||
}
|
||||
|
||||
public Socket socket() {
|
||||
return channel.socket();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return channel.toString();
|
||||
}
|
||||
|
||||
public int validOps() {
|
||||
return channel.validOps();
|
||||
}
|
||||
|
||||
public int write(ByteBuffer src) throws IOException, NotYetConnectedException {
|
||||
return channel.write(src);
|
||||
}
|
||||
|
||||
public long write(ByteBuffer[] src) throws IOException, NotYetConnectedException {
|
||||
return channel.write(src);
|
||||
}
|
||||
|
||||
public boolean finishConnect() throws IOException {
|
||||
return this.channel.finishConnect();
|
||||
}
|
||||
|
||||
public boolean isConnectionPending() {
|
||||
return this.channel.isConnectionPending();
|
||||
}
|
||||
|
||||
/**
|
||||
* does the handshake if needed
|
||||
* @param key
|
||||
* @return true, if handshake is completed
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean doHandshake(SelectionKey key) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the channel for closing
|
||||
* this can take more than 1 call
|
||||
* @param key
|
||||
* @return true, if channel is ready for closing
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean shutdown(SelectionKey key) throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* is channel in closing-procedure?
|
||||
* @return
|
||||
*/
|
||||
public boolean isShutdownInProgress() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* is there remaining data in channel-buffers, which must be flushed?
|
||||
* @return
|
||||
*/
|
||||
public boolean isFlushed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Certificate[] getCerts() {
|
||||
return certs;
|
||||
}
|
||||
|
||||
public Object getAttachment() {
|
||||
return attachment;
|
||||
}
|
||||
|
||||
public void setAttachment(Object attachment) {
|
||||
this.attachment = attachment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Christian Lorenz, Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.channels;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
|
||||
import org.xtreemfs.foundation.SSLOptions;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
|
||||
/**
|
||||
* A secure abstraction of the SocketChannel (by using SSL)
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class SSLChannelIO extends ChannelIO {
|
||||
|
||||
/**
|
||||
* Number of Threads in the ThreadPool which will be used for the
|
||||
* time-consuming tasks
|
||||
*/
|
||||
// public static int EXECUTOR_THREADS = 4;
|
||||
/**
|
||||
* used SSLEngine for this channel
|
||||
*/
|
||||
protected final SSLEngine sslEngine;
|
||||
|
||||
/**
|
||||
* contains the read data encrypted by ssl
|
||||
*/
|
||||
protected ReusableBuffer inNetBuffer, inReadBuffer;
|
||||
|
||||
/**
|
||||
* contains the written data encrypted by ssl
|
||||
*/
|
||||
protected ReusableBuffer outNetBuffer;
|
||||
|
||||
/**
|
||||
* an empty buffer for e.g. handshaking and shutdown; it will never contain
|
||||
* data
|
||||
*/
|
||||
protected ReusableBuffer dummyBuffer;
|
||||
|
||||
/**
|
||||
* the last SSLEngine-status
|
||||
*/
|
||||
protected HandshakeStatus handshakeStatus;
|
||||
|
||||
protected boolean handshakeComplete;
|
||||
|
||||
protected int keyOpsBeforeHandshake = -1;
|
||||
|
||||
/**
|
||||
* true, if shutdown was called at least one time
|
||||
*/
|
||||
protected boolean shutdownInProgress;
|
||||
|
||||
/**
|
||||
* cipher suites without symmetric encryption, wich are supported by the
|
||||
* SSLEngine in Java6
|
||||
*/
|
||||
protected static String[] supportedCipherSuitesWithoutEncryption = null;
|
||||
|
||||
/**
|
||||
* SSL-Channel is used by client
|
||||
*/
|
||||
protected boolean clientMode;
|
||||
|
||||
/**
|
||||
* for asynchronious execution of time-consuming tasks only one executor for
|
||||
* ALL SSLChannelIOs
|
||||
*/
|
||||
// private static ExecutorService executor = null;
|
||||
private boolean closed = false;
|
||||
|
||||
private boolean shutdownComplete = false;
|
||||
|
||||
/**
|
||||
* creates a SSLChannelIO
|
||||
*
|
||||
* @param channel
|
||||
* channel, which should be protected by SSL
|
||||
* @param sslOptions
|
||||
* the Options for the SSL-Connection
|
||||
* @param clientMode
|
||||
* true, if you are a client; false, if you are a server
|
||||
* @throws SSLException
|
||||
*/
|
||||
public SSLChannelIO(SocketChannel channel, SSLOptions sslOptions, boolean clientMode) throws SSLException {
|
||||
super(channel);
|
||||
// initialize SSLEngine for a server
|
||||
sslEngine = sslOptions.getSSLContext().createSSLEngine();
|
||||
sslEngine.setUseClientMode(clientMode);
|
||||
sslEngine.setNeedClientAuth(true);
|
||||
|
||||
List<String> enabledProtocols = new ArrayList<String>();
|
||||
for (String protocol : sslEngine.getSupportedProtocols()) {
|
||||
if (sslOptions.isSSLEngineProtocolSupported(protocol) && !enabledProtocols.contains(protocol)) {
|
||||
enabledProtocols.add(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
String[] enabledProtocolsArray = enabledProtocols.toArray(new String[enabledProtocols.size()]);
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "Enabling the following protocols: %s",
|
||||
Arrays.toString(enabledProtocolsArray));
|
||||
}
|
||||
sslEngine.setEnabledProtocols(enabledProtocolsArray);
|
||||
|
||||
if (clientMode) {
|
||||
// the first call for a client is wrap()
|
||||
sslEngine.beginHandshake();
|
||||
handshakeStatus = HandshakeStatus.NEED_WRAP;
|
||||
} else {
|
||||
// the first call for a server is unwrap()
|
||||
sslEngine.beginHandshake();
|
||||
handshakeStatus = HandshakeStatus.NEED_UNWRAP;
|
||||
}
|
||||
|
||||
handshakeComplete = false;
|
||||
shutdownInProgress = false;
|
||||
|
||||
int netBufSize = sslEngine.getSession().getPacketBufferSize();
|
||||
inNetBuffer = BufferPool.allocate(netBufSize);
|
||||
inReadBuffer = BufferPool.allocate(sslEngine.getSession().getApplicationBufferSize() * 2);
|
||||
outNetBuffer = BufferPool.allocate(netBufSize);
|
||||
dummyBuffer = BufferPool.allocate(netBufSize);
|
||||
|
||||
|
||||
if (sslOptions.isAuthenticationWithoutEncryption()) { // only
|
||||
// authentication
|
||||
// without
|
||||
// protecting
|
||||
// data?
|
||||
// enable only cipher suites without encryption
|
||||
|
||||
if (supportedCipherSuitesWithoutEncryption == null) { // runs only
|
||||
// first time
|
||||
// a
|
||||
// SSLChannelIO
|
||||
// without Encryption is created
|
||||
// find all supported cipher suites without symmetric encryption
|
||||
ArrayList<String> cipherSuites = new ArrayList<String>();
|
||||
for (String cipherSuite : sslEngine.getSupportedCipherSuites()) {
|
||||
if (cipherSuite.contains("WITH_NULL")) {
|
||||
cipherSuites.add(cipherSuite);
|
||||
}
|
||||
}
|
||||
supportedCipherSuitesWithoutEncryption = new String[cipherSuites.size()];
|
||||
supportedCipherSuitesWithoutEncryption = cipherSuites
|
||||
.toArray(supportedCipherSuitesWithoutEncryption);
|
||||
}
|
||||
sslEngine.setEnabledCipherSuites(supportedCipherSuitesWithoutEncryption);
|
||||
} else // enable all supported cipher suites
|
||||
{
|
||||
sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites());
|
||||
}
|
||||
|
||||
// only initialize the first time an SSLChannelIO is created
|
||||
/*
|
||||
* if(executor==null) executor =
|
||||
* Executors.newFixedThreadPool(EXECUTOR_THREADS);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
int returnValue = 0;
|
||||
if (!shutdownInProgress) {
|
||||
if (handshakeComplete) {
|
||||
if (inReadBuffer.remaining() == inReadBuffer.capacity()) {
|
||||
if (channel.read(inNetBuffer.getBuffer()) == -1) {
|
||||
return -1;
|
||||
}
|
||||
inNetBuffer.flip(); // ready for being read
|
||||
inDataAvail: while (inNetBuffer.hasRemaining()) {
|
||||
SSLEngineResult result = sslEngine.unwrap(inNetBuffer.getBuffer(), inReadBuffer
|
||||
.getBuffer());
|
||||
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
// returnValue += result.bytesProduced();
|
||||
// FIXME: if client does't close the connection
|
||||
// after receiving close_notify =>
|
||||
// decomment it
|
||||
if (sslEngine.isInboundDone()) // received
|
||||
// close_notify
|
||||
{
|
||||
close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BUFFER_UNDERFLOW: {
|
||||
// needed more data in inNetBuffer, maybe nexttime
|
||||
// inNetBuffer.compact();
|
||||
break inDataAvail;
|
||||
// return returnValue;
|
||||
}
|
||||
case BUFFER_OVERFLOW: {
|
||||
// needed more space in dst
|
||||
throw new IOException(
|
||||
"BufferOverflow in the SSLEngine: Destination-Buffer is too small.");
|
||||
}
|
||||
case CLOSED: {
|
||||
throw new IOException("The SSLEngine is already closed.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in an undefined state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
inNetBuffer.compact(); // ready for reading from channel
|
||||
}
|
||||
inReadBuffer.flip();
|
||||
if (dst.remaining() >= inReadBuffer.remaining()) {
|
||||
returnValue += inReadBuffer.remaining();
|
||||
dst.put(inReadBuffer.getBuffer());
|
||||
} else {
|
||||
while (inReadBuffer.hasRemaining() && dst.hasRemaining()) {
|
||||
dst.put(inReadBuffer.get());
|
||||
returnValue++;
|
||||
}
|
||||
}
|
||||
inReadBuffer.compact();
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} warning: maybe more bytes would be consumed from src-buffer
|
||||
* than will be written to channel (returned value)
|
||||
*/
|
||||
@Override
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
int returnValue = 0;
|
||||
if (!shutdownInProgress) {
|
||||
if (handshakeComplete) {
|
||||
SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.getBuffer());
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
tryFlush();
|
||||
|
||||
break;
|
||||
}
|
||||
case BUFFER_OVERFLOW: {
|
||||
// needed more space in outNetBuffer
|
||||
// two reasons for overflow:
|
||||
// 1. buffer is too small
|
||||
// 2. buffer is nearly full
|
||||
tryFlush();
|
||||
/*
|
||||
* throw new IOException(
|
||||
* "BufferOverflow in SSLEngine. Buffer for SSLEngine-generated data is too small."
|
||||
* );
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case CLOSED: {
|
||||
throw new IOException("The SSLEngine is already closed.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos state.");
|
||||
}
|
||||
}
|
||||
returnValue = result.bytesConsumed();
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
/**
|
||||
* {@inheritDoc} warning: maybe more bytes would be consumed from src-buffer
|
||||
* than will be written to channel (returned value)
|
||||
*/
|
||||
@Override
|
||||
public long write(ByteBuffer[] src) throws IOException {
|
||||
int returnValue = 0;
|
||||
if (!shutdownInProgress) {
|
||||
if (handshakeComplete) {
|
||||
SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.getBuffer());
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
tryFlush();
|
||||
|
||||
break;
|
||||
}
|
||||
case BUFFER_OVERFLOW: {
|
||||
// needed more space in outNetBuffer
|
||||
// two reasons for overflow:
|
||||
// 1. buffer is too small
|
||||
// 2. buffer is nearly full
|
||||
tryFlush();
|
||||
/*
|
||||
* throw new IOException(
|
||||
* "BufferOverflow in SSLEngine. Buffer for SSLEngine-generated data is too small."
|
||||
* );
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case CLOSED: {
|
||||
throw new IOException("The SSLEngine is already closed.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos state.");
|
||||
}
|
||||
}
|
||||
returnValue = result.bytesConsumed();
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isShutdownInProgress() {
|
||||
return this.shutdownInProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean shutdown(SelectionKey key) throws IOException, CancelledKeyException {
|
||||
if (!handshakeComplete) { // no SSL connection is established => simple
|
||||
// close
|
||||
shutdownInProgress = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shutdownComplete) {
|
||||
return shutdownComplete;
|
||||
}
|
||||
|
||||
if (!shutdownInProgress) { // initiate shutdown
|
||||
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"shutdown SSL connection of %s:%d", channel.socket().getInetAddress().toString(), channel
|
||||
.socket().getPort());
|
||||
|
||||
sslEngine.closeOutbound();
|
||||
shutdownInProgress = true;
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ); // don't
|
||||
// wait
|
||||
// for
|
||||
// the
|
||||
// close_notify-reply
|
||||
}
|
||||
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
if (tryFlush() && sslEngine.isOutboundDone()) { // shutdown complete
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
shutdownComplete = true;
|
||||
}
|
||||
|
||||
if (!sslEngine.isOutboundDone()) {
|
||||
// Get close message
|
||||
SSLEngineResult result = sslEngine.wrap(dummyBuffer.getBuffer(), outNetBuffer.getBuffer());
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
throw new IOException("This should not happen.");
|
||||
}
|
||||
case BUFFER_OVERFLOW: {
|
||||
// needed more space in outNetBuffer
|
||||
// two reasons for overflow:
|
||||
// 1. buffer is too small
|
||||
// 2. buffer is nearly full
|
||||
tryFlush();
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
/*
|
||||
* throw new IOException(
|
||||
* "BufferOverflow in SSLEngine. Buffer for SSLEngine-generated data is too small."
|
||||
* );
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case CLOSED: {
|
||||
if (tryFlush() && sslEngine.isOutboundDone()) {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
shutdownComplete = true;
|
||||
} else {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return shutdownComplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
try {
|
||||
sslEngine.closeInbound();
|
||||
sslEngine.closeOutbound();
|
||||
} catch (SSLException e) {
|
||||
// ignore it
|
||||
}
|
||||
// free buffers
|
||||
BufferPool.free(inNetBuffer);
|
||||
inNetBuffer = null;
|
||||
BufferPool.free(inReadBuffer);
|
||||
inReadBuffer = null;
|
||||
BufferPool.free(outNetBuffer);
|
||||
BufferPool.free(dummyBuffer);
|
||||
shutdownInProgress = true;
|
||||
closed = true;
|
||||
} catch (Throwable th) {
|
||||
System.out.println("CANNOT CLOSE DUE TO: " + th);
|
||||
throw new IOException(th);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (inNetBuffer != null) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.misc, this, "buffers not freed!");
|
||||
BufferPool.free(inNetBuffer);
|
||||
inNetBuffer = null;
|
||||
BufferPool.free(outNetBuffer);
|
||||
BufferPool.free(dummyBuffer);
|
||||
}
|
||||
if (!closed) {
|
||||
System.out.println("CONNECTION WAS NOT CLOSED PROPERLY: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the outNetBuffer-data to the channel. After write, the buffer is
|
||||
* empty or ready for add new data
|
||||
*
|
||||
* @return true, if write was successful; false, if buffer is not empty
|
||||
* @throws IOException
|
||||
*/
|
||||
protected boolean tryFlush() throws IOException {
|
||||
// if (outNetBuffer.hasRemaining()) { // flush the buffer
|
||||
channel.write(outNetBuffer.getBuffer());
|
||||
if (outNetBuffer.hasRemaining()) {
|
||||
outNetBuffer.compact();
|
||||
return false;
|
||||
} else {
|
||||
outNetBuffer.compact();
|
||||
}
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} warning: the function manipulates the SelectionKey Ops, so
|
||||
* don't do anything in your programm beetween first call of this function
|
||||
* until the function returns true
|
||||
*/
|
||||
@Override
|
||||
public boolean doHandshake(SelectionKey key) throws IOException, CancelledKeyException {
|
||||
if (handshakeComplete || shutdownInProgress) { // quick return
|
||||
return handshakeComplete;
|
||||
}
|
||||
|
||||
if (keyOpsBeforeHandshake == -1) {
|
||||
keyOpsBeforeHandshake = key.interestOps();
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ & ~SelectionKey.OP_WRITE);
|
||||
}
|
||||
|
||||
if (!handshakeComplete) {
|
||||
// Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
// "SSL-handshake next step: "+handshakeStatus);
|
||||
|
||||
SSLEngineResult result;
|
||||
switch (handshakeStatus) {
|
||||
case NEED_UNWRAP: {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
if (channel.read(inNetBuffer.getBuffer()) == -1) {
|
||||
throw new IOException("End of stream has reached.");
|
||||
}
|
||||
|
||||
boolean underflow = false;
|
||||
do { // read all read data in buffer
|
||||
// Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
// "SSL-handshake doing: unwrap");
|
||||
inNetBuffer.flip(); // ready for being read
|
||||
result = sslEngine.unwrap(inNetBuffer.getBuffer(), dummyBuffer.getBuffer());
|
||||
inNetBuffer.compact(); // ready for reading from channel
|
||||
|
||||
handshakeStatus = result.getHandshakeStatus();
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
analyseHandshakeStatus(key, handshakeStatus);
|
||||
break;
|
||||
}
|
||||
case BUFFER_UNDERFLOW: {
|
||||
// needed more data in inNetBuffer, maybe nexttime
|
||||
underflow = true;
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||
break;
|
||||
}
|
||||
case CLOSED: {
|
||||
throw new IOException("The SSLEngine is already closed.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos state.");
|
||||
}
|
||||
}
|
||||
} while (bufferRemaining(inNetBuffer) != 0 && handshakeStatus == HandshakeStatus.NEED_UNWRAP
|
||||
&& !underflow);
|
||||
break;
|
||||
}
|
||||
case NEED_WRAP: {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
|
||||
// Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
// "SSL-handshake doing: wrap");
|
||||
|
||||
result = sslEngine.wrap(dummyBuffer.getBuffer(), outNetBuffer.getBuffer());
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
|
||||
handshakeStatus = result.getHandshakeStatus();
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
tryFlush();
|
||||
|
||||
analyseHandshakeStatus(key, handshakeStatus);
|
||||
break;
|
||||
}
|
||||
case BUFFER_OVERFLOW: {
|
||||
// needed more space in outNetBuffer
|
||||
// two reasons for overflow:
|
||||
// 1. buffer is too small
|
||||
// 2. buffer is nearly full
|
||||
tryFlush();
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
/*
|
||||
* throw new IOException(
|
||||
* "BufferOverflow in SSLEngine. Buffer for SSLEngine-generated data is too small."
|
||||
* );
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case CLOSED: {
|
||||
throw new IOException("The SSLEngine is already closed.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos state.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FINISHED: {
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
if (tryFlush()) {
|
||||
handshakeFinished(key);
|
||||
} else {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NEED_TASK: {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
doTasks(key);
|
||||
break;
|
||||
}
|
||||
case NOT_HANDSHAKING: {
|
||||
// TODO: Exception or maybe handshakeComplete = true?
|
||||
throw new IOException("The SSLEngine is not handshaking.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos handshake-state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return handshakeComplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isFlushed() {
|
||||
return bufferRemaining(outNetBuffer) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* finishing operations for handshake
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
private void handshakeFinished(SelectionKey key) throws CancelledKeyException {
|
||||
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "SSL-handshake for %s:%d finished",
|
||||
channel.socket().getInetAddress().toString(), channel.socket().getPort());
|
||||
|
||||
// all handshake-data processed and sent
|
||||
handshakeComplete = true;
|
||||
inNetBuffer.clear();
|
||||
outNetBuffer.clear();
|
||||
key.interestOps(keyOpsBeforeHandshake);
|
||||
try {
|
||||
this.certs = sslEngine.getSession().getPeerCertificates();
|
||||
} catch (SSLPeerUnverifiedException ex) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.auth, this, OutputUtils.stackTraceToString(ex));
|
||||
this.certs = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* @param handshakeStatus
|
||||
* @throws IOException
|
||||
*/
|
||||
private void analyseHandshakeStatus(SelectionKey key, HandshakeStatus handshakeStatus) throws IOException, CancelledKeyException {
|
||||
switch (handshakeStatus) {
|
||||
case NEED_UNWRAP: {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ & ~SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
case NEED_WRAP: {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
case NEED_TASK: {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
doTasks(key);
|
||||
break;
|
||||
}
|
||||
case FINISHED: {
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
if (tryFlush()) {
|
||||
handshakeFinished(key);
|
||||
} else {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NOT_HANDSHAKING: {
|
||||
// TODO: Exception or maybe handshakeComplete = true?
|
||||
throw new IOException("The SSLEngine is not handshaking.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos handshake-state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks the remaining data of the buffer (only for internal buffers)
|
||||
*
|
||||
* @param buffer
|
||||
* @return
|
||||
*/
|
||||
private int bufferRemaining(ReusableBuffer buffer) {
|
||||
buffer.flip(); // ready for being read
|
||||
int tmp = buffer.remaining();
|
||||
buffer.compact(); // ready for being read
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* runs the time-consuming tasks
|
||||
*
|
||||
* @param key
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void doTasks(final SelectionKey key) throws CancelledKeyException {
|
||||
// Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
// "SSL-handshake doing: doing task");
|
||||
|
||||
final int tmp = key.interestOps();
|
||||
// clear all interests, so no one other than this thread can modify the
|
||||
// selector
|
||||
key.interestOps(0);
|
||||
|
||||
/*
|
||||
* executor.execute(new Runnable(){ public void run() {
|
||||
*/
|
||||
// TODO: running in a different thread
|
||||
Runnable run;
|
||||
while ((run = sslEngine.getDelegatedTask()) != null) {
|
||||
run.run();
|
||||
}
|
||||
|
||||
switch (handshakeStatus = sslEngine.getHandshakeStatus()) {
|
||||
case NEED_WRAP: {
|
||||
key.interestOps(tmp | SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
case NEED_UNWRAP: {
|
||||
// need to read from channel
|
||||
key.interestOps(tmp | SelectionKey.OP_READ);
|
||||
break;
|
||||
}
|
||||
case FINISHED: {
|
||||
// should not happen
|
||||
handshakeFinished(key);
|
||||
break;
|
||||
}
|
||||
case NEED_TASK: {
|
||||
// should not happen
|
||||
doTasks(key);
|
||||
break;
|
||||
}
|
||||
case NOT_HANDSHAKING: {
|
||||
// should not happen
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.auth, this,
|
||||
"Exception in worker-thread: The SSLEngine is not handshaking.");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.auth, this,
|
||||
"Exception in worker-thread: The SSLEngine is in a curiuos handshake-state.");
|
||||
assert (false);
|
||||
// throw new
|
||||
// IOException("The SSLEngine is in a curiuos handshake-state.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* key.selector().wakeup(); } });
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Christian Lorenz, Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.channels;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
|
||||
import org.xtreemfs.foundation.SSLOptions;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
|
||||
/**
|
||||
* A secure abstraction of the SocketChannel (by using SSL)
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class SSLHandshakeOnlyChannelIO extends ChannelIO {
|
||||
|
||||
/**
|
||||
* Number of Threads in the ThreadPool which will be used for the
|
||||
* time-consuming tasks
|
||||
*/
|
||||
// public static int EXECUTOR_THREADS = 4;
|
||||
/**
|
||||
* used SSLEngine for this channel
|
||||
*/
|
||||
protected final SSLEngine sslEngine;
|
||||
|
||||
/**
|
||||
* contains the read data encrypted by ssl
|
||||
*/
|
||||
protected ReusableBuffer inNetBuffer, inReadBuffer;
|
||||
|
||||
/**
|
||||
* contains the written data encrypted by ssl
|
||||
*/
|
||||
protected ReusableBuffer outNetBuffer;
|
||||
|
||||
/**
|
||||
* an empty buffer for e.g. handshaking and shutdown; it will never contain
|
||||
* data
|
||||
*/
|
||||
protected ReusableBuffer dummyBuffer;
|
||||
|
||||
/**
|
||||
* the last SSLEngine-status
|
||||
*/
|
||||
protected HandshakeStatus handshakeStatus;
|
||||
|
||||
protected boolean handshakeComplete;
|
||||
|
||||
protected int keyOpsBeforeHandshake = -1;
|
||||
|
||||
/**
|
||||
* true, if shutdown was called at least one time
|
||||
*/
|
||||
protected boolean shutdownInProgress;
|
||||
|
||||
/**
|
||||
* cipher suites without symmetric encryption, wich are supported by the
|
||||
* SSLEngine in Java6
|
||||
*/
|
||||
protected static String[] supportedCipherSuitesWithoutEncryption = null;
|
||||
|
||||
/**
|
||||
* SSL-Channel is used by client
|
||||
*/
|
||||
protected boolean clientMode;
|
||||
|
||||
/**
|
||||
* for asynchronious execution of time-consuming tasks only one executor for
|
||||
* ALL SSLChannelIOs
|
||||
*/
|
||||
// private static ExecutorService executor = null;
|
||||
private boolean closed = false;
|
||||
|
||||
private boolean shutdownComplete = false;
|
||||
|
||||
/**
|
||||
* creates a SSLChannelIO
|
||||
*
|
||||
* @param channel
|
||||
* channel, which should be protected by SSL
|
||||
* @param sslOptions
|
||||
* the Options for the SSL-Connection
|
||||
* @param clientMode
|
||||
* true, if you are a client; false, if you are a server
|
||||
* @throws SSLException
|
||||
*/
|
||||
public SSLHandshakeOnlyChannelIO(SocketChannel channel, SSLOptions sslOptions, boolean clientMode) throws SSLException {
|
||||
super(channel);
|
||||
// initialize SSLEngine for a server
|
||||
sslEngine = sslOptions.getSSLContext().createSSLEngine();
|
||||
sslEngine.setUseClientMode(clientMode);
|
||||
sslEngine.setNeedClientAuth(true);
|
||||
|
||||
if (clientMode) {
|
||||
// the first call for a client is wrap()
|
||||
sslEngine.beginHandshake();
|
||||
handshakeStatus = HandshakeStatus.NEED_WRAP;
|
||||
} else {
|
||||
// the first call for a server is unwrap()
|
||||
sslEngine.beginHandshake();
|
||||
handshakeStatus = HandshakeStatus.NEED_UNWRAP;
|
||||
}
|
||||
|
||||
handshakeComplete = false;
|
||||
shutdownInProgress = false;
|
||||
|
||||
int netBufSize = sslEngine.getSession().getPacketBufferSize();
|
||||
inNetBuffer = BufferPool.allocate(netBufSize);
|
||||
inReadBuffer = BufferPool.allocate(sslEngine.getSession().getApplicationBufferSize() * 2);
|
||||
outNetBuffer = BufferPool.allocate(netBufSize);
|
||||
dummyBuffer = BufferPool.allocate(netBufSize);
|
||||
|
||||
sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols());
|
||||
if (sslOptions.isAuthenticationWithoutEncryption()) { // only
|
||||
// authentication
|
||||
// without
|
||||
// protecting
|
||||
// data?
|
||||
// enable only cipher suites without encryption
|
||||
|
||||
if (supportedCipherSuitesWithoutEncryption == null) { // runs only
|
||||
// first time
|
||||
// a
|
||||
// SSLChannelIO
|
||||
// without Encryption is created
|
||||
// find all supported cipher suites without symmetric encryption
|
||||
ArrayList<String> cipherSuites = new ArrayList<String>();
|
||||
for (String cipherSuite : sslEngine.getSupportedCipherSuites()) {
|
||||
if (cipherSuite.contains("WITH_NULL")) {
|
||||
cipherSuites.add(cipherSuite);
|
||||
}
|
||||
}
|
||||
supportedCipherSuitesWithoutEncryption = new String[cipherSuites.size()];
|
||||
supportedCipherSuitesWithoutEncryption = cipherSuites
|
||||
.toArray(supportedCipherSuitesWithoutEncryption);
|
||||
}
|
||||
sslEngine.setEnabledCipherSuites(supportedCipherSuitesWithoutEncryption);
|
||||
} else // enable all supported cipher suites
|
||||
{
|
||||
sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites());
|
||||
}
|
||||
|
||||
// only initialize the first time an SSLChannelIO is created
|
||||
/*
|
||||
* if(executor==null) executor =
|
||||
* Executors.newFixedThreadPool(EXECUTOR_THREADS);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
int returnValue = 0;
|
||||
if (!shutdownInProgress) {
|
||||
if (handshakeComplete) {
|
||||
/*if (inNetBuffer.hasRemaining()) {
|
||||
//read something which is still in the buffer after the handshake
|
||||
while (dst.hasRemaining() && inNetBuffer.hasRemaining())
|
||||
dst.put(inNetBuffer.get());
|
||||
} else {*/
|
||||
returnValue = channel.read(dst);
|
||||
// }
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} warning: maybe more bytes would be consumed from src-buffer
|
||||
* than will be written to channel (returned value)
|
||||
*/
|
||||
@Override
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
int returnValue = 0;
|
||||
if (!shutdownInProgress) {
|
||||
if (handshakeComplete) {
|
||||
return channel.write(src);
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
/**
|
||||
* {@inheritDoc} warning: maybe more bytes would be consumed from src-buffer
|
||||
* than will be written to channel (returned value)
|
||||
*/
|
||||
@Override
|
||||
public long write(ByteBuffer[] src) throws IOException {
|
||||
int returnValue = 0;
|
||||
if (!shutdownInProgress) {
|
||||
if (handshakeComplete) {
|
||||
return channel.write(src);
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isShutdownInProgress() {
|
||||
return this.shutdownInProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean shutdown(SelectionKey key) throws IOException, CancelledKeyException {
|
||||
if (!handshakeComplete) { // no SSL connection is established => simple
|
||||
// close
|
||||
shutdownInProgress = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shutdownComplete) {
|
||||
return shutdownComplete;
|
||||
}
|
||||
|
||||
if (!shutdownInProgress) { // initiate shutdown
|
||||
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"shutdown SSL connection of %s:%d", channel.socket().getInetAddress().toString(), channel
|
||||
.socket().getPort());
|
||||
|
||||
sslEngine.closeOutbound();
|
||||
shutdownInProgress = true;
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ); // don't
|
||||
// wait
|
||||
// for
|
||||
// the
|
||||
// close_notify-reply
|
||||
}
|
||||
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
if (tryFlush() && sslEngine.isOutboundDone()) { // shutdown complete
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
shutdownComplete = true;
|
||||
}
|
||||
|
||||
if (!sslEngine.isOutboundDone()) {
|
||||
// Get close message
|
||||
SSLEngineResult result = sslEngine.wrap(dummyBuffer.getBuffer(), outNetBuffer.getBuffer());
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
throw new IOException("This should not happen.");
|
||||
}
|
||||
case BUFFER_OVERFLOW: {
|
||||
// needed more space in outNetBuffer
|
||||
// two reasons for overflow:
|
||||
// 1. buffer is too small
|
||||
// 2. buffer is nearly full
|
||||
tryFlush();
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
/*
|
||||
* throw new IOException(
|
||||
* "BufferOverflow in SSLEngine. Buffer for SSLEngine-generated data is too small."
|
||||
* );
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case CLOSED: {
|
||||
if (tryFlush() && sslEngine.isOutboundDone()) {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
shutdownComplete = true;
|
||||
} else {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return shutdownComplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
try {
|
||||
sslEngine.closeInbound();
|
||||
sslEngine.closeOutbound();
|
||||
} catch (SSLException e) {
|
||||
// ignore it
|
||||
}
|
||||
// free buffers
|
||||
BufferPool.free(inNetBuffer);
|
||||
inNetBuffer = null;
|
||||
BufferPool.free(inReadBuffer);
|
||||
inReadBuffer = null;
|
||||
BufferPool.free(outNetBuffer);
|
||||
BufferPool.free(dummyBuffer);
|
||||
shutdownInProgress = true;
|
||||
closed = true;
|
||||
} catch (Throwable th) {
|
||||
System.out.println("CANNOT CLOSE DUE TO: " + th);
|
||||
throw new IOException(th);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (inNetBuffer != null) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.misc, this, "buffers not freed!");
|
||||
BufferPool.free(inNetBuffer);
|
||||
inNetBuffer = null;
|
||||
BufferPool.free(outNetBuffer);
|
||||
BufferPool.free(dummyBuffer);
|
||||
}
|
||||
if (!closed) {
|
||||
System.out.println("CONNECTION WAS NOT CLOSED PROPERLY: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the outNetBuffer-data to the channel. After write, the buffer is
|
||||
* empty or ready for add new data
|
||||
*
|
||||
* @return true, if write was successful; false, if buffer is not empty
|
||||
* @throws IOException
|
||||
*/
|
||||
protected boolean tryFlush() throws IOException {
|
||||
// if (outNetBuffer.hasRemaining()) { // flush the buffer
|
||||
channel.write(outNetBuffer.getBuffer());
|
||||
if (outNetBuffer.hasRemaining()) {
|
||||
outNetBuffer.compact();
|
||||
return false;
|
||||
} else {
|
||||
outNetBuffer.compact();
|
||||
}
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} warning: the function manipulates the SelectionKey Ops, so
|
||||
* don't do anything in your programm beetween first call of this function
|
||||
* until the function returns true
|
||||
*/
|
||||
@Override
|
||||
public boolean doHandshake(SelectionKey key) throws IOException, CancelledKeyException {
|
||||
if (handshakeComplete || shutdownInProgress) { // quick return
|
||||
return handshakeComplete;
|
||||
}
|
||||
|
||||
if (keyOpsBeforeHandshake == -1) {
|
||||
keyOpsBeforeHandshake = key.interestOps();
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ & ~SelectionKey.OP_WRITE);
|
||||
}
|
||||
|
||||
if (!handshakeComplete) {
|
||||
// Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
// "SSL-handshake next step: "+handshakeStatus);
|
||||
|
||||
SSLEngineResult result;
|
||||
switch (handshakeStatus) {
|
||||
case NEED_UNWRAP: {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
if (channel.read(inNetBuffer.getBuffer()) == -1) {
|
||||
throw new IOException("End of stream has reached.");
|
||||
}
|
||||
|
||||
boolean underflow = false;
|
||||
do { // read all read data in buffer
|
||||
// Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
// "SSL-handshake doing: unwrap");
|
||||
inNetBuffer.flip(); // ready for being read
|
||||
result = sslEngine.unwrap(inNetBuffer.getBuffer(), dummyBuffer.getBuffer());
|
||||
inNetBuffer.compact(); // ready for reading from channel
|
||||
|
||||
handshakeStatus = result.getHandshakeStatus();
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
analyseHandshakeStatus(key, handshakeStatus);
|
||||
break;
|
||||
}
|
||||
case BUFFER_UNDERFLOW: {
|
||||
// needed more data in inNetBuffer, maybe nexttime
|
||||
underflow = true;
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||
break;
|
||||
}
|
||||
case CLOSED: {
|
||||
throw new IOException("The SSLEngine is already closed.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos state.");
|
||||
}
|
||||
}
|
||||
} while (bufferRemaining(inNetBuffer) != 0 && handshakeStatus == HandshakeStatus.NEED_UNWRAP
|
||||
&& !underflow);
|
||||
break;
|
||||
}
|
||||
case NEED_WRAP: {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
|
||||
// Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
// "SSL-handshake doing: wrap");
|
||||
|
||||
result = sslEngine.wrap(dummyBuffer.getBuffer(), outNetBuffer.getBuffer());
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
|
||||
handshakeStatus = result.getHandshakeStatus();
|
||||
switch (result.getStatus()) {
|
||||
case OK: {
|
||||
tryFlush();
|
||||
|
||||
analyseHandshakeStatus(key, handshakeStatus);
|
||||
break;
|
||||
}
|
||||
case BUFFER_OVERFLOW: {
|
||||
// needed more space in outNetBuffer
|
||||
// two reasons for overflow:
|
||||
// 1. buffer is too small
|
||||
// 2. buffer is nearly full
|
||||
tryFlush();
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
/*
|
||||
* throw new IOException(
|
||||
* "BufferOverflow in SSLEngine. Buffer for SSLEngine-generated data is too small."
|
||||
* );
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case CLOSED: {
|
||||
throw new IOException("The SSLEngine is already closed.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos state.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FINISHED: {
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
if (tryFlush()) {
|
||||
handshakeFinished(key);
|
||||
} else {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NEED_TASK: {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
doTasks(key);
|
||||
break;
|
||||
}
|
||||
case NOT_HANDSHAKING: {
|
||||
// TODO: Exception or maybe handshakeComplete = true?
|
||||
throw new IOException("The SSLEngine is not handshaking.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos handshake-state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return handshakeComplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isFlushed() {
|
||||
return bufferRemaining(outNetBuffer) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* finishing operations for handshake
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
private void handshakeFinished(SelectionKey key) throws CancelledKeyException {
|
||||
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "SSL-handshake for %s:%d finished",
|
||||
channel.socket().getInetAddress().toString(), channel.socket().getPort());
|
||||
|
||||
// all handshake-data processed and sent
|
||||
handshakeComplete = true;
|
||||
inNetBuffer.clear();
|
||||
outNetBuffer.clear();
|
||||
key.interestOps(keyOpsBeforeHandshake);
|
||||
try {
|
||||
this.certs = sslEngine.getSession().getPeerCertificates();
|
||||
} catch (SSLPeerUnverifiedException ex) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.auth, this, OutputUtils.stackTraceToString(ex));
|
||||
this.certs = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* @param handshakeStatus
|
||||
* @throws IOException
|
||||
*/
|
||||
private void analyseHandshakeStatus(SelectionKey key, HandshakeStatus handshakeStatus) throws IOException, CancelledKeyException {
|
||||
switch (handshakeStatus) {
|
||||
case NEED_UNWRAP: {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ & ~SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
case NEED_WRAP: {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
case NEED_TASK: {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
doTasks(key);
|
||||
break;
|
||||
}
|
||||
case FINISHED: {
|
||||
outNetBuffer.flip(); // ready for writing to channel
|
||||
if (tryFlush()) {
|
||||
handshakeFinished(key);
|
||||
} else {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NOT_HANDSHAKING: {
|
||||
// TODO: Exception or maybe handshakeComplete = true?
|
||||
throw new IOException("The SSLEngine is not handshaking.");
|
||||
}
|
||||
default: {
|
||||
throw new IOException("The SSLEngine is in a curiuos handshake-state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks the remaining data of the buffer (only for internal buffers)
|
||||
*
|
||||
* @param buffer
|
||||
* @return
|
||||
*/
|
||||
private int bufferRemaining(ReusableBuffer buffer) {
|
||||
buffer.flip(); // ready for being read
|
||||
int tmp = buffer.remaining();
|
||||
buffer.compact(); // ready for being read
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* runs the time-consuming tasks
|
||||
*
|
||||
* @param key
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void doTasks(final SelectionKey key) throws CancelledKeyException {
|
||||
// Logging.logMessage(Logging.LEVEL_DEBUG, this,
|
||||
// "SSL-handshake doing: doing task");
|
||||
|
||||
final int tmp = key.interestOps();
|
||||
// clear all interests, so no one other than this thread can modify the
|
||||
// selector
|
||||
key.interestOps(0);
|
||||
|
||||
/*
|
||||
* executor.execute(new Runnable(){ public void run() {
|
||||
*/
|
||||
// TODO: running in a different thread
|
||||
Runnable run;
|
||||
while ((run = sslEngine.getDelegatedTask()) != null) {
|
||||
run.run();
|
||||
}
|
||||
|
||||
switch (handshakeStatus = sslEngine.getHandshakeStatus()) {
|
||||
case NEED_WRAP: {
|
||||
key.interestOps(tmp | SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
case NEED_UNWRAP: {
|
||||
// need to read from channel
|
||||
key.interestOps(tmp | SelectionKey.OP_READ);
|
||||
break;
|
||||
}
|
||||
case FINISHED: {
|
||||
// should not happen
|
||||
handshakeFinished(key);
|
||||
break;
|
||||
}
|
||||
case NEED_TASK: {
|
||||
// should not happen
|
||||
doTasks(key);
|
||||
break;
|
||||
}
|
||||
case NOT_HANDSHAKING: {
|
||||
// should not happen
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.auth, this,
|
||||
"Exception in worker-thread: The SSLEngine is not handshaking.");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.auth, this,
|
||||
"Exception in worker-thread: The SSLEngine is in a curiuos handshake-state.");
|
||||
assert (false);
|
||||
// throw new
|
||||
// IOException("The SSLEngine is in a curiuos handshake-state.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* key.selector().wakeup(); } });
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.client;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.RPCHeader.ErrorResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class PBRPCException extends IOException {
|
||||
|
||||
final ErrorResponse response;
|
||||
|
||||
final String message;
|
||||
|
||||
public PBRPCException(String ioError) {
|
||||
super();
|
||||
this.message = ioError;
|
||||
response = null;
|
||||
}
|
||||
|
||||
public PBRPCException(ErrorResponse response) {
|
||||
super();
|
||||
this.response = response;
|
||||
this.message = null;
|
||||
}
|
||||
|
||||
public RPC.ErrorType getErrorType() {
|
||||
if (response != null)
|
||||
return response.getErrorType();
|
||||
else
|
||||
return RPC.ErrorType.IO_ERROR;
|
||||
}
|
||||
|
||||
public RPC.POSIXErrno getPOSIXErrno() {
|
||||
if (response != null)
|
||||
return response.getPosixErrno();
|
||||
else
|
||||
return RPC.POSIXErrno.POSIX_ERROR_NONE;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
if (response != null)
|
||||
return response.getErrorMessage();
|
||||
else
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public String getDebugInfo() {
|
||||
if (response != null)
|
||||
return response.getDebugInfo();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getRedirectToServerUUID() {
|
||||
if (response != null)
|
||||
return response.getRedirectToServerUuid();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
public ErrorResponse getErrorResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (response != null) {
|
||||
return response.getErrorType().toString()+"/"+response.getPosixErrno().toString()+": "+response.getErrorMessage()+" / "+response.getDebugInfo();
|
||||
} else {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.client;
|
||||
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.Auth;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.AuthType;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.UserCredentials;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCAuthentication {
|
||||
|
||||
public static final Auth authNone;
|
||||
|
||||
public static final UserCredentials userService;
|
||||
|
||||
static {
|
||||
authNone = Auth.newBuilder().setAuthType(AuthType.AUTH_NONE).build();
|
||||
userService = UserCredentials.newBuilder().setUsername("srv").addGroups("xtreemfs").build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.client;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.xtreemfs.foundation.TimeSync;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.ChannelIO;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCNIOSocketServerConnection.ReceiveState;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.RecordMarker;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCClientConnection {
|
||||
|
||||
public static final int RETRY_RESET_IN_MS = 500;
|
||||
|
||||
/** max wait is one minute */
|
||||
public static final int MAX_RETRY_WAIT_MS = 1000*60;
|
||||
|
||||
private ChannelIO channel;
|
||||
|
||||
private final Map<Integer,RPCClientRequest> requests;
|
||||
|
||||
private final List<RPCClientRequest> sendQueue;
|
||||
|
||||
private long lastUsed;
|
||||
|
||||
private long nextReconnectTime;
|
||||
|
||||
private int numConnectAttempts;
|
||||
|
||||
private final ByteBuffer requestRecordMarker;
|
||||
|
||||
private final ByteBuffer responseRecordMarker;
|
||||
|
||||
private ReusableBuffer[] responseBuffers;
|
||||
|
||||
private ByteBuffer[] requestBuffers;
|
||||
|
||||
private RPCClientRequest pendingRequest;
|
||||
|
||||
private ReceiveState receiveState;
|
||||
|
||||
private final InetSocketAddress endpoint;
|
||||
|
||||
volatile long bytesRX, bytesTX;
|
||||
|
||||
|
||||
public RPCClientConnection(InetSocketAddress endpoint) {
|
||||
requests = new HashMap<Integer, RPCClientRequest>();
|
||||
lastUsed = TimeSync.getLocalSystemTime();
|
||||
numConnectAttempts = 0;
|
||||
nextReconnectTime = 0;
|
||||
sendQueue = new LinkedList<RPCClientRequest>();
|
||||
requestRecordMarker = ByteBuffer.allocateDirect(RecordMarker.HDR_SIZE);
|
||||
responseRecordMarker = ByteBuffer.allocateDirect(RecordMarker.HDR_SIZE);
|
||||
this.endpoint = endpoint;
|
||||
receiveState = ReceiveState.RECORD_MARKER;
|
||||
bytesTX = 0;
|
||||
bytesRX = 0;
|
||||
}
|
||||
|
||||
public void freeBuffers() {
|
||||
if (responseBuffers != null) {
|
||||
for (ReusableBuffer buf: responseBuffers)
|
||||
BufferPool.free(buf);
|
||||
}
|
||||
for (RPCClientRequest rq : sendQueue) {
|
||||
rq.freeBuffers();
|
||||
}
|
||||
for (RPCClientRequest rq : requests.values()) {
|
||||
rq.freeBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isConnected() {
|
||||
return channel != null;
|
||||
}
|
||||
|
||||
void connectFailed() {
|
||||
numConnectAttempts++;
|
||||
long waitt = Math.round(RETRY_RESET_IN_MS*Math.pow(2,this.numConnectAttempts));
|
||||
if (waitt > MAX_RETRY_WAIT_MS) {
|
||||
waitt = MAX_RETRY_WAIT_MS;
|
||||
}
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"next reconnect possible after %d s, attempt = %d", (waitt / 1000), this.numConnectAttempts);
|
||||
this.nextReconnectTime = System.currentTimeMillis()+waitt;
|
||||
}
|
||||
|
||||
boolean canReconnect() {
|
||||
return (this.nextReconnectTime < System.currentTimeMillis());
|
||||
}
|
||||
|
||||
void setChannel(ChannelIO channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
void connected() {
|
||||
numConnectAttempts = 0;
|
||||
lastUsed = TimeSync.getLocalSystemTime();
|
||||
}
|
||||
|
||||
void useConnection() {
|
||||
lastUsed = TimeSync.getLocalSystemTime();
|
||||
}
|
||||
|
||||
long getLastUsed() {
|
||||
return lastUsed;
|
||||
}
|
||||
|
||||
ChannelIO getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
RPCClientRequest getRequest(int callId) {
|
||||
return requests.remove(callId);
|
||||
}
|
||||
|
||||
void addRequest(int callId, RPCClientRequest rq) {
|
||||
requests.put(callId,rq);
|
||||
}
|
||||
|
||||
void removeRequest(int callId) {
|
||||
requests.remove(callId);
|
||||
}
|
||||
|
||||
Map<Integer,RPCClientRequest> getRequests() {
|
||||
return this.requests;
|
||||
}
|
||||
|
||||
List<RPCClientRequest> getSendQueue() {
|
||||
return sendQueue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the requestFragHdr
|
||||
*/
|
||||
ByteBuffer getRequestRecordMarker() {
|
||||
return requestRecordMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the responseFragHdr
|
||||
*/
|
||||
ByteBuffer getResponseRecordMarker() {
|
||||
return responseRecordMarker;
|
||||
}
|
||||
|
||||
public String getEndpointString() {
|
||||
String endpointString = "unknown";
|
||||
try {
|
||||
endpointString = endpoint.toString();
|
||||
} catch (Exception ex2) {
|
||||
}
|
||||
|
||||
return endpointString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the requestBuffers
|
||||
*/
|
||||
public ByteBuffer[] getRequestBuffers() {
|
||||
return requestBuffers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestBuffers the requestBuffers to set
|
||||
*/
|
||||
public void setRequestBuffers(ByteBuffer[] requestBuffers) {
|
||||
this.requestBuffers = requestBuffers;
|
||||
}
|
||||
|
||||
public RPCClientRequest getPendingRequest() {
|
||||
return pendingRequest;
|
||||
}
|
||||
|
||||
public void setPendingRequest(RPCClientRequest pendingRequest) {
|
||||
this.pendingRequest = pendingRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the receiveState
|
||||
*/
|
||||
public ReceiveState getReceiveState() {
|
||||
return receiveState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param receiveState the receiveState to set
|
||||
*/
|
||||
public void setReceiveState(ReceiveState receiveState) {
|
||||
this.receiveState = receiveState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the responseBuffers
|
||||
*/
|
||||
public ReusableBuffer[] getResponseBuffers() {
|
||||
return responseBuffers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param responseBuffers the responseBuffers to set
|
||||
*/
|
||||
public void setResponseBuffers(ReusableBuffer[] responseBuffers) {
|
||||
this.responseBuffers = responseBuffers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.client;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.xtreemfs.foundation.TimeSync;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferOutputStream;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.Auth;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.UserCredentials;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.RecordMarker;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCClientRequest<ReturnType extends Message> {
|
||||
|
||||
private final RPC.RPCHeader requestHeader;
|
||||
private RPC.RPCHeader responseHeader;
|
||||
final ReusableBuffer[] buffers;
|
||||
final int hdrLen;
|
||||
final int msgLen;
|
||||
final int dataLen;
|
||||
|
||||
private final RPCResponse response;
|
||||
|
||||
private long timeQueued;
|
||||
private long bytesWritten;
|
||||
|
||||
|
||||
RPCClientRequest(Auth authHeader, UserCredentials uCreds, int callId, int interfaceId, int procId, Message message, ReusableBuffer data, RPCResponse<ReturnType> response) throws IOException {
|
||||
if (uCreds == null) {
|
||||
throw new IOException("No UserCredentials object given (null). Make sure it's set.");
|
||||
}
|
||||
if (authHeader == null) {
|
||||
throw new IOException("No Auth object given (null). Make sure it's set.");
|
||||
}
|
||||
|
||||
RPC.RPCHeader.RequestHeader rqHdr = RPC.RPCHeader.RequestHeader.newBuilder().setAuthData(authHeader).setUserCreds(uCreds).
|
||||
setInterfaceId(interfaceId).setProcId(procId).build();
|
||||
requestHeader = RPC.RPCHeader.newBuilder().setCallId(callId).setMessageType(RPC.MessageType.RPC_REQUEST).setRequestHeader(rqHdr).build();
|
||||
this.response = response;
|
||||
|
||||
ReusableBufferOutputStream os = new ReusableBufferOutputStream(ReusableBufferOutputStream.BUFF_SIZE);
|
||||
requestHeader.writeTo(os);
|
||||
hdrLen = os.length();
|
||||
if (message != null) {
|
||||
message.writeTo(os);
|
||||
msgLen = os.length()-hdrLen;
|
||||
} else {
|
||||
msgLen = 0;
|
||||
}
|
||||
if (data != null) {
|
||||
os.appendBuffer(data);
|
||||
dataLen = data.limit();
|
||||
} else {
|
||||
dataLen = 0;
|
||||
}
|
||||
assert(hdrLen > 0);
|
||||
assert(msgLen >= 0);
|
||||
assert(dataLen >= 0);
|
||||
os.flip();
|
||||
buffers = os.getBuffers();
|
||||
}
|
||||
|
||||
public ReusableBuffer[] getBuffers() {
|
||||
return buffers;
|
||||
}
|
||||
|
||||
public ByteBuffer[] packBuffers(ByteBuffer recordMarker) {
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "sending record marker: %d/%d/%d", hdrLen,msgLen,dataLen);
|
||||
}
|
||||
recordMarker.putInt(hdrLen);
|
||||
recordMarker.putInt(msgLen);
|
||||
recordMarker.putInt(dataLen);
|
||||
recordMarker.flip();
|
||||
ByteBuffer[] arr = new ByteBuffer[buffers.length+1];
|
||||
arr[0] = recordMarker;
|
||||
for (int i = 0; i < buffers.length; i++) {
|
||||
arr[i+1] = buffers[i].getBuffer();
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this, "send buffer #%d: %s", i+1,buffers[i]);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public void freeBuffers() {
|
||||
for (int i = 0; i < buffers.length; i++) {
|
||||
BufferPool.free(buffers[i]);
|
||||
buffers[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* duration of request from sending the request until the response
|
||||
* was received completeley.
|
||||
* @return duration in ns
|
||||
*/
|
||||
public long getDuration() {
|
||||
if (RPCNIOSocketClient.ENABLE_STATISTICS) {
|
||||
return 0;
|
||||
} else {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the requestHeader
|
||||
*/
|
||||
public RPC.RPCHeader getRequestHeader() {
|
||||
return requestHeader;
|
||||
}
|
||||
|
||||
void queued() {
|
||||
this.timeQueued = TimeSync.getLocalSystemTime();
|
||||
}
|
||||
|
||||
long getTimeQueued() {
|
||||
return this.timeQueued;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the responseHeader
|
||||
*/
|
||||
public RPC.RPCHeader getResponseHeader() {
|
||||
return responseHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param responseHeader the responseHeader to set
|
||||
*/
|
||||
public void setResponseHeader(RPC.RPCHeader responseHeader) {
|
||||
this.responseHeader = responseHeader;
|
||||
}
|
||||
|
||||
public RPCResponse<ReturnType> getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void recordBytesWritten(long bytesWritten) {
|
||||
this.bytesWritten += bytesWritten;
|
||||
if (this.bytesWritten > RecordMarker.HDR_SIZE + hdrLen + dataLen + msgLen) {
|
||||
String errorMessage = "Too many bytes written (expected: "
|
||||
+ (RecordMarker.HDR_SIZE + hdrLen + dataLen + msgLen)
|
||||
+ ", actual: "
|
||||
+ this.bytesWritten
|
||||
+ ") for message "
|
||||
+ requestHeader;
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, this, errorMessage);
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkEnoughBytesSent() {
|
||||
if (bytesWritten != RecordMarker.HDR_SIZE + hdrLen + dataLen + msgLen) {
|
||||
String errorMessage = "Not enough bytes written (expected: "
|
||||
+ (RecordMarker.HDR_SIZE + hdrLen + dataLen + msgLen)
|
||||
+ ", actual: "
|
||||
+ bytesWritten
|
||||
+ ") for message "
|
||||
+ requestHeader;
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, this, errorMessage);
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,806 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
* Copyright (c) 2013 by Bjoern Kolbeck.
|
||||
* Copyright (c) 2014 by Quobyte Inc.
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.xtreemfs.foundation.LifeCycleThread;
|
||||
import org.xtreemfs.foundation.SSLOptions;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.ChannelIO;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.SSLChannelIO;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.SSLHandshakeOnlyChannelIO;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.Auth;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.UserCredentials;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCNIOSocketServer;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCNIOSocketServerConnection;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferInputStream;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCNIOSocketClient extends LifeCycleThread {
|
||||
|
||||
public static boolean ENABLE_STATISTICS = false;
|
||||
|
||||
|
||||
/**
|
||||
* Maximum tries to reconnect to the server
|
||||
*/
|
||||
public static final int MAX_RECONNECT = 4;
|
||||
|
||||
/**
|
||||
* milliseconds between two timeout checks
|
||||
*/
|
||||
public static final int TIMEOUT_GRANULARITY = 250;
|
||||
|
||||
private final Map<InetSocketAddress, RPCClientConnection> connections;
|
||||
|
||||
private final int requestTimeout;
|
||||
|
||||
private final int connectionTimeout;
|
||||
|
||||
private long lastCheck;
|
||||
|
||||
private final Selector selector;
|
||||
|
||||
private volatile boolean quit;
|
||||
|
||||
private final SSLOptions sslOptions;
|
||||
|
||||
private final AtomicInteger transactionId;
|
||||
|
||||
private final ConcurrentLinkedQueue<RPCClientConnection> toBeEstablished;
|
||||
|
||||
private final int sendBufferSize;
|
||||
|
||||
private final int receiveBufferSize;
|
||||
|
||||
private final SocketAddress localBindPoint;
|
||||
|
||||
|
||||
/**
|
||||
* on some platforms (e.g. FreeBSD 7.2 with openjdk6) Selector.select(int timeout)
|
||||
* returns immediately. If this problem is detected, the thread waits 25ms after each
|
||||
* invocation to avoid excessive CPU consumption. See also issue #75
|
||||
*/
|
||||
private boolean brokenSelect;
|
||||
|
||||
public RPCNIOSocketClient(SSLOptions sslOptions, int requestTimeout, int connectionTimeout)
|
||||
throws IOException {
|
||||
this(sslOptions, requestTimeout, connectionTimeout, -1, -1, null, "", false);
|
||||
}
|
||||
|
||||
public RPCNIOSocketClient(SSLOptions sslOptions, int requestTimeout, int connectionTimeout, String threadName)
|
||||
throws IOException {
|
||||
this(sslOptions, requestTimeout, connectionTimeout, -1, -1, null, threadName, false);
|
||||
}
|
||||
|
||||
public RPCNIOSocketClient(SSLOptions sslOptions, int requestTimeout, int connectionTimeout,
|
||||
int sendBufferSize, int receiveBufferSize, SocketAddress localBindPoint) throws IOException {
|
||||
this(sslOptions, requestTimeout, connectionTimeout, sendBufferSize, receiveBufferSize, localBindPoint, "", false);
|
||||
}
|
||||
|
||||
public RPCNIOSocketClient(SSLOptions sslOptions, int requestTimeout, int connectionTimeout, String threadName, boolean startAsDaemon) throws IOException {
|
||||
this(sslOptions, requestTimeout, connectionTimeout, -1, -1, null, threadName, startAsDaemon);
|
||||
}
|
||||
|
||||
public RPCNIOSocketClient(SSLOptions sslOptions, int requestTimeout, int connectionTimeout,
|
||||
int sendBufferSize, int receiveBufferSize, SocketAddress localBindPoint, String threadName) throws IOException {
|
||||
this(sslOptions, requestTimeout, connectionTimeout, sendBufferSize, receiveBufferSize, localBindPoint, threadName, false);
|
||||
}
|
||||
|
||||
public RPCNIOSocketClient(SSLOptions sslOptions, int requestTimeout, int connectionTimeout,
|
||||
int sendBufferSize, int receiveBufferSize, SocketAddress localBindPoint, String threadName, boolean startAsDaemon) throws IOException {
|
||||
super(threadName);
|
||||
setDaemon(startAsDaemon);
|
||||
if (requestTimeout >= connectionTimeout - TIMEOUT_GRANULARITY * 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"request timeout must be smaller than connection timeout less " + TIMEOUT_GRANULARITY * 2
|
||||
+ "ms");
|
||||
}
|
||||
this.requestTimeout = requestTimeout;
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
this.sendBufferSize = sendBufferSize;
|
||||
this.receiveBufferSize = receiveBufferSize;
|
||||
this.localBindPoint = localBindPoint;
|
||||
connections = new HashMap<InetSocketAddress, RPCClientConnection>();
|
||||
selector = Selector.open();
|
||||
this.sslOptions = sslOptions;
|
||||
quit = false;
|
||||
transactionId = new AtomicInteger((int) (Math.random() * 1e6 + 1.0));
|
||||
toBeEstablished = new ConcurrentLinkedQueue<RPCClientConnection>();
|
||||
|
||||
if (this.localBindPoint != null && Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"RPC Client '%s': Using the following address for outgoing connections: %s", threadName, this.localBindPoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void sendRequest(InetSocketAddress server, Auth auth, UserCredentials uCred, int interface_id, int proc_id, Message message, ReusableBuffer data,
|
||||
RPCResponse response, boolean highPriority) {
|
||||
try {
|
||||
RPCClientRequest rq = new RPCClientRequest(auth, uCred, transactionId.incrementAndGet(), interface_id, proc_id, message, data, response);
|
||||
internalSendRequest(server, rq, highPriority);
|
||||
} catch (Throwable e) { // CancelledKeyException, RuntimeException (caused by missing TimeSyncThread)
|
||||
//e.printStackTrace();
|
||||
response.requestFailed(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void internalSendRequest(InetSocketAddress server, RPCClientRequest request, boolean highPriority) {
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "sending request %s no %d", request
|
||||
.toString(), transactionId.get());
|
||||
}
|
||||
// get connection
|
||||
RPCClientConnection con = null;
|
||||
synchronized (connections) {
|
||||
con = connections.get(server);
|
||||
if (con == null) {
|
||||
con = new RPCClientConnection(server);
|
||||
connections.put(server, con);
|
||||
}
|
||||
}
|
||||
synchronized (con) {
|
||||
boolean isEmpty = con.getSendQueue().isEmpty();
|
||||
request.queued();
|
||||
con.useConnection();
|
||||
if (highPriority)
|
||||
con.getSendQueue().add(0, request);
|
||||
else
|
||||
con.getSendQueue().add(request);
|
||||
|
||||
if (!con.isConnected()) {
|
||||
establishConnection(server, con);
|
||||
|
||||
} else {
|
||||
if (isEmpty) {
|
||||
final SelectionKey key = con.getChannel().keyFor(selector);
|
||||
if (key != null) {
|
||||
try {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
} catch (CancelledKeyException e) {
|
||||
// Ignore it since the timeout mechanism will deal with it.
|
||||
}
|
||||
}
|
||||
selector.wakeup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
brokenSelect = false;
|
||||
// Doesn't work properly, should be a replaced with a better way to detect
|
||||
// a broken selector on FreeBSD.
|
||||
/*try {
|
||||
long now = System.currentTimeMillis();
|
||||
int numKeys = selector.select(100);
|
||||
long duration = System.currentTimeMillis()-now;
|
||||
if ((duration < 10) && (numKeys == 0)) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this,"detected broken select(int timeout)!");
|
||||
brokenSelect = true;
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, this,"could not check Selector for broken select(int timeout): "+th);
|
||||
}*/
|
||||
|
||||
notifyStarted();
|
||||
lastCheck = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
while (!quit) {
|
||||
if (!toBeEstablished.isEmpty()) {
|
||||
while (true) {
|
||||
RPCClientConnection con = toBeEstablished.poll();
|
||||
if (con == null) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
con.getChannel().register(selector,
|
||||
SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE | SelectionKey.OP_READ, con);
|
||||
} catch (ClosedChannelException ex) {
|
||||
closeConnection(con.getChannel().keyFor(selector), ex.toString());
|
||||
}
|
||||
}
|
||||
toBeEstablished.clear();
|
||||
}
|
||||
|
||||
int numKeys = 0;
|
||||
try {
|
||||
numKeys = selector.select(TIMEOUT_GRANULARITY);
|
||||
} catch (CancelledKeyException ex) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this, "Exception while selecting: %s",
|
||||
ex.toString());
|
||||
continue;
|
||||
} catch (IOException ex) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this, "Exception while selecting: %s",
|
||||
ex.toString());
|
||||
continue;
|
||||
}
|
||||
if (numKeys > 0) {
|
||||
// fetch events
|
||||
Set<SelectionKey> keys = selector.selectedKeys();
|
||||
Iterator<SelectionKey> iter = keys.iterator();
|
||||
|
||||
// process all events
|
||||
while (iter.hasNext()) {
|
||||
try {
|
||||
SelectionKey key = iter.next();
|
||||
|
||||
// remove key from the list
|
||||
iter.remove();
|
||||
|
||||
if (key.isConnectable()) {
|
||||
connectConnection(key);
|
||||
}
|
||||
if (key.isReadable()) {
|
||||
readConnection(key);
|
||||
}
|
||||
if (key.isWritable()) {
|
||||
writeConnection(key);
|
||||
}
|
||||
} catch (CancelledKeyException ex) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numKeys == 0 && brokenSelect) {
|
||||
|
||||
try {
|
||||
sleep(25);
|
||||
} catch (InterruptedException ex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
checkForTimers();
|
||||
} catch (ConcurrentModificationException ce) {
|
||||
Logging.logMessage(Logging.LEVEL_CRIT, this,
|
||||
OutputUtils.getThreadDump());
|
||||
}
|
||||
}
|
||||
} catch (Throwable thr) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.net, this, "PBRPC Client CRASHED!");
|
||||
notifyCrashed(thr);
|
||||
}
|
||||
|
||||
synchronized (connections) {
|
||||
for (RPCClientConnection con : connections.values()) {
|
||||
synchronized (con) {
|
||||
for (RPCClientRequest rq : con.getSendQueue()) {
|
||||
rq.getResponse().requestFailed("RPC cancelled due to client shutdown");
|
||||
rq.freeBuffers();
|
||||
}
|
||||
for (RPCClientRequest rq : con.getRequests().values()) {
|
||||
rq.getResponse().requestFailed("RPC cancelled due to client shutdown");
|
||||
rq.freeBuffers();
|
||||
}
|
||||
try {
|
||||
if (con.getChannel() != null)
|
||||
con.getChannel().close();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyStopped();
|
||||
}
|
||||
|
||||
private void establishConnection(InetSocketAddress server, RPCClientConnection con) {
|
||||
|
||||
if (con.canReconnect()) {
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "connect to %s", server
|
||||
.toString());
|
||||
}
|
||||
ChannelIO channel;
|
||||
try {
|
||||
if (sslOptions == null) { // no SSL
|
||||
channel = new ChannelIO(SocketChannel.open());
|
||||
} else {
|
||||
if (sslOptions.isFakeSSLMode()) {
|
||||
channel = new SSLHandshakeOnlyChannelIO(SocketChannel.open(), sslOptions, true);
|
||||
} else {
|
||||
channel = new SSLChannelIO(SocketChannel.open(), sslOptions, true);
|
||||
}
|
||||
}
|
||||
channel.configureBlocking(false);
|
||||
channel.socket().setTcpNoDelay(true);
|
||||
if (localBindPoint != null) {
|
||||
channel.socket().bind(localBindPoint);
|
||||
}
|
||||
|
||||
if (sendBufferSize != -1) {
|
||||
channel.socket().setSendBufferSize(sendBufferSize);
|
||||
if (channel.socket().getSendBufferSize() != sendBufferSize) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"could not set socket send buffer size to " + sendBufferSize
|
||||
+ ", using default size of " + channel.socket().getSendBufferSize());
|
||||
}
|
||||
}
|
||||
|
||||
if (receiveBufferSize != -1) {
|
||||
channel.socket().setReceiveBufferSize(receiveBufferSize);
|
||||
if (channel.socket().getReceiveBufferSize() != receiveBufferSize) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"could not set socket receive buffer size to " + receiveBufferSize
|
||||
+ ", using default size of " + channel.socket().getReceiveBufferSize());
|
||||
}
|
||||
} else {
|
||||
channel.socket().setReceiveBufferSize(256 * 1024);
|
||||
}
|
||||
|
||||
channel.connect(server);
|
||||
con.setChannel(channel);
|
||||
toBeEstablished.add(con);
|
||||
selector.wakeup();
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "connection created");
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "socket send buffer size: %d",
|
||||
channel.socket().getSendBufferSize());
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "socket receive buffer size: %d",
|
||||
channel.socket().getReceiveBufferSize());
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "local bind point: %s", channel
|
||||
.socket().getLocalAddress());
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
if (ex.getClass() == java.net.SocketException.class && ex.getMessage().equals("Invalid argument")) {
|
||||
Logging.logMessage(
|
||||
Logging.LEVEL_ERROR,
|
||||
Category.net,
|
||||
this,
|
||||
"FAILED TO USE THE FOLLOWING ADDRESS FOR OUTGOING REQUESTS: %s. Make sure that the hostname is correctly spelled in the configuration and it resolves to the correct IP.",
|
||||
localBindPoint);
|
||||
}
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "cannot contact server %s",
|
||||
con.getEndpointString());
|
||||
}
|
||||
con.connectFailed();
|
||||
for (RPCClientRequest rq : con.getSendQueue()) {
|
||||
rq.getResponse().requestFailed("sending RPC failed: server '"+con.getEndpointString()+"' not reachable ("+ex+")");
|
||||
rq.freeBuffers();
|
||||
}
|
||||
con.getSendQueue().clear();
|
||||
|
||||
}
|
||||
} else {
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"reconnect to server still blocked locally to avoid flooding (server: %s)", con.getEndpointString());
|
||||
}
|
||||
synchronized (con) {
|
||||
for (RPCClientRequest rq : con.getSendQueue()) {
|
||||
rq.getResponse().requestFailed("sending RPC failed: reconnecting to the server '"+con.getEndpointString()+"' was blocked locally to avoid flooding");
|
||||
rq.freeBuffers();
|
||||
}
|
||||
con.getSendQueue().clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void readConnection(SelectionKey key) {
|
||||
final RPCClientConnection con = (RPCClientConnection) key.attachment();
|
||||
final ChannelIO channel = con.getChannel();
|
||||
|
||||
try {
|
||||
|
||||
if (!channel.isShutdownInProgress()) {
|
||||
if (channel.doHandshake(key)) {
|
||||
|
||||
while (true) {
|
||||
ByteBuffer buf = null;
|
||||
switch (con.getReceiveState()) {
|
||||
case RECORD_MARKER: {
|
||||
buf = con.getResponseRecordMarker(); break;
|
||||
}
|
||||
case RPC_MESSAGE: {
|
||||
buf = con.getResponseBuffers()[1].getBuffer(); break;
|
||||
}
|
||||
case RPC_HEADER: {
|
||||
buf = con.getResponseBuffers()[0].getBuffer(); break;
|
||||
}
|
||||
case DATA: {
|
||||
buf = con.getResponseBuffers()[2].getBuffer(); break;
|
||||
}
|
||||
}
|
||||
|
||||
// read fragment header
|
||||
final int numBytesRead = RPCNIOSocketServer.readData(key, channel, buf);
|
||||
if (numBytesRead == -1) {
|
||||
// connection closed
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"client closed connection (EOF): %s", channel.socket()
|
||||
.getRemoteSocketAddress().toString());
|
||||
}
|
||||
closeConnection(key,"server ("+channel.socket()
|
||||
.getRemoteSocketAddress().toString()+") closed connection");
|
||||
return;
|
||||
}
|
||||
if (buf.hasRemaining()) {
|
||||
// not enough data...
|
||||
break;
|
||||
}
|
||||
|
||||
switch (con.getReceiveState()) {
|
||||
case RECORD_MARKER: {
|
||||
buf.position(0);
|
||||
final int hdrLen = buf.getInt();
|
||||
final int msgLen = buf.getInt();
|
||||
final int dataLen = buf.getInt();
|
||||
|
||||
if ((hdrLen <= 0) || (hdrLen >= RPCNIOSocketServer.MAX_FRAGMENT_SIZE)
|
||||
|| (msgLen < 0) || (msgLen >= RPCNIOSocketServer.MAX_FRAGMENT_SIZE)
|
||||
|| (dataLen < 0) || (dataLen >= RPCNIOSocketServer.MAX_FRAGMENT_SIZE)) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.net, this,
|
||||
"invalid record marker size (%d/%d/%d) received, closing connection to client %s",
|
||||
hdrLen,msgLen,dataLen,channel.socket()
|
||||
.getRemoteSocketAddress().toString());
|
||||
closeConnection(key,"received invalid record marker from server ("+channel.socket()
|
||||
.getRemoteSocketAddress().toString()+"), closed connection");
|
||||
return;
|
||||
}
|
||||
final ReusableBuffer[] buffers = new ReusableBuffer[]{BufferPool.allocate(hdrLen),
|
||||
((msgLen > 0) ? BufferPool.allocate(msgLen) : null),
|
||||
((dataLen > 0) ? BufferPool.allocate(dataLen) : null) };
|
||||
con.setResponseBuffers(buffers);
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.RPC_HEADER);
|
||||
continue;
|
||||
}
|
||||
|
||||
case RPC_HEADER: {
|
||||
if (con.getResponseBuffers()[1] != null) {
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.RPC_MESSAGE);
|
||||
continue;
|
||||
} else if (con.getResponseBuffers()[2] != null) {
|
||||
// this is necessary, because we may receive some default
|
||||
// instance of a message (empty) with data attached BUG #188
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.DATA);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
case RPC_MESSAGE: {
|
||||
if (con.getResponseBuffers()[2] != null) {
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.DATA);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//assemble ServerRequest
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.RECORD_MARKER);
|
||||
con.getResponseRecordMarker().clear();
|
||||
|
||||
//assemble response...
|
||||
assembleResponse(key, con);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// simply close the connection
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, OutputUtils
|
||||
.stackTraceToString(ex));
|
||||
}
|
||||
closeConnection(key, "server closed connection ("+ex+")");
|
||||
} catch (NotYetConnectedException e) {
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, OutputUtils
|
||||
.stackTraceToString(e));
|
||||
}
|
||||
closeConnection(key, "server closed connection: "+e);
|
||||
}
|
||||
}
|
||||
|
||||
private void assembleResponse(SelectionKey key, RPCClientConnection con) throws IOException {
|
||||
|
||||
try {
|
||||
ReusableBuffer[] receiveBuffers = con.getResponseBuffers();
|
||||
receiveBuffers[0].flip();
|
||||
if (receiveBuffers[1] != null)
|
||||
receiveBuffers[1].flip();
|
||||
if (receiveBuffers[2] != null)
|
||||
receiveBuffers[2].flip();
|
||||
|
||||
|
||||
ReusableBufferInputStream rbis = new ReusableBufferInputStream(receiveBuffers[0]);
|
||||
final RPC.RPCHeader header = RPC.RPCHeader.parseFrom(rbis);
|
||||
BufferPool.free(receiveBuffers[0]);
|
||||
|
||||
RPCClientRequest rq = con.getRequest(header.getCallId());
|
||||
if (rq == null) {
|
||||
// Might happen when a request timed out before a response was
|
||||
// sent.
|
||||
BufferPool.free(receiveBuffers[1]);
|
||||
BufferPool.free(receiveBuffers[2]);
|
||||
con.setResponseBuffers(null);
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"received response for unknown request callId=%d",
|
||||
header.getCallId());
|
||||
return;
|
||||
}
|
||||
RPCResponse response = rq.getResponse();
|
||||
rq.setResponseHeader(header);
|
||||
con.setResponseBuffers(null);
|
||||
|
||||
response.responseAvailable(rq, receiveBuffers[1], receiveBuffers[2]);
|
||||
} catch (IOException ex) {
|
||||
closeConnection(key,"invalid response received: "+ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void writeConnection(SelectionKey key) {
|
||||
final RPCClientConnection con = (RPCClientConnection) key.attachment();
|
||||
final ChannelIO channel = con.getChannel();
|
||||
|
||||
try {
|
||||
|
||||
if (!channel.isShutdownInProgress()) {
|
||||
if (channel.doHandshake(key)) {
|
||||
|
||||
while (true) {
|
||||
ByteBuffer[] buffers = con.getRequestBuffers();
|
||||
RPCClientRequest send = con.getPendingRequest();
|
||||
if (buffers == null) {
|
||||
assert(send == null);
|
||||
synchronized (con) {
|
||||
if (con.getSendQueue().isEmpty()) {
|
||||
// no more responses, stop writing...
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
send = con.getSendQueue().remove(0);
|
||||
}
|
||||
assert(send != null);
|
||||
con.getRequestRecordMarker().clear();
|
||||
buffers = send.packBuffers(con.getRequestRecordMarker());
|
||||
con.setRequestBuffers(buffers);
|
||||
con.setPendingRequest(send);
|
||||
}
|
||||
|
||||
assert(buffers != null);
|
||||
final long numBytesWritten = channel.write(buffers);
|
||||
if (numBytesWritten == -1) {
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this,
|
||||
"client closed connection (EOF): %s", channel.socket()
|
||||
.getRemoteSocketAddress().toString());
|
||||
}
|
||||
// connection closed
|
||||
closeConnection(key, "server unexpectedly closed connection (EOF)");
|
||||
return;
|
||||
}
|
||||
// Detect if the client writes outside of the fragment.
|
||||
send.recordBytesWritten(numBytesWritten);
|
||||
|
||||
if (buffers[buffers.length-1].hasRemaining()) {
|
||||
// not enough data...
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
|
||||
//remove from queue
|
||||
synchronized (con) {
|
||||
con.addRequest(send.getRequestHeader().getCallId(), send);
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"sent request %d to %s", send.getRequestHeader().getCallId(), con.getEndpointString());
|
||||
}
|
||||
}
|
||||
send.checkEnoughBytesSent();
|
||||
con.setRequestBuffers(null);
|
||||
con.setPendingRequest(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CancelledKeyException ex) {
|
||||
// simply close the connection
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, OutputUtils
|
||||
.stackTraceToString(ex));
|
||||
}
|
||||
closeConnection(key, "server closed connection: "+ex);
|
||||
} catch (IOException ex) {
|
||||
// simply close the connection
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, OutputUtils
|
||||
.stackTraceToString(ex));
|
||||
}
|
||||
closeConnection(key, "server closed connection: "+ex);
|
||||
} catch (NotYetConnectedException e) {
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, OutputUtils
|
||||
.stackTraceToString(e));
|
||||
}
|
||||
closeConnection(key, "server closed connection: "+e);
|
||||
}
|
||||
}
|
||||
|
||||
private void connectConnection(SelectionKey key) {
|
||||
final RPCClientConnection con = (RPCClientConnection) key.attachment();
|
||||
final ChannelIO channel = con.getChannel();
|
||||
|
||||
try {
|
||||
if (channel.isConnectionPending()) {
|
||||
channel.finishConnect();
|
||||
}
|
||||
synchronized (con) {
|
||||
if (!con.getSendQueue().isEmpty()) {
|
||||
key.interestOps(SelectionKey.OP_WRITE | SelectionKey.OP_READ);
|
||||
}
|
||||
}
|
||||
con.connected();
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "connected from %s to %s", con
|
||||
.getChannel().socket().getLocalSocketAddress().toString(), con.getEndpointString());
|
||||
}
|
||||
} catch (CancelledKeyException ex) {
|
||||
con.connectFailed();
|
||||
closeConnection(key, "server '" + con.getEndpointString() + "' not reachable ("+ex+")");
|
||||
} catch (IOException ex) {
|
||||
con.connectFailed();
|
||||
closeConnection(key, "server '" + con.getEndpointString() + "' not reachable ("+ex+")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void closeConnection(SelectionKey key, String errorMessage) {
|
||||
final RPCClientConnection con = (RPCClientConnection) key.attachment();
|
||||
final ChannelIO channel = con.getChannel();
|
||||
|
||||
List<RPCClientRequest> cancelRq = new LinkedList<RPCClientRequest>();
|
||||
synchronized (con) {
|
||||
// remove the connection from the selector and close socket
|
||||
try {
|
||||
key.cancel();
|
||||
channel.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
cancelRq.addAll(con.getRequests().values());
|
||||
cancelRq.addAll(con.getSendQueue());
|
||||
con.getRequests().clear();
|
||||
con.getSendQueue().clear();
|
||||
con.setChannel(null);
|
||||
}
|
||||
|
||||
// notify listeners
|
||||
for (RPCClientRequest rq : cancelRq) {
|
||||
rq.getResponse().requestFailed("sending RPC failed: "+errorMessage);
|
||||
rq.freeBuffers();
|
||||
}
|
||||
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "closing connection to %s", con
|
||||
.getEndpointString());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForTimers() {
|
||||
// poor man's timer
|
||||
long now = System.currentTimeMillis();
|
||||
if (now >= lastCheck + TIMEOUT_GRANULARITY) {
|
||||
// check for timed out requests
|
||||
synchronized (connections) {
|
||||
Iterator<RPCClientConnection> conIter = connections.values().iterator();
|
||||
while (conIter.hasNext()) {
|
||||
final RPCClientConnection con = conIter.next();
|
||||
|
||||
if (con.getLastUsed() < (now - connectionTimeout)) {
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"removing idle connection");
|
||||
}
|
||||
try {
|
||||
conIter.remove();
|
||||
closeConnection(con.getChannel().keyFor(selector), null);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
} else {
|
||||
// check for request timeout
|
||||
List<RPCClientRequest> cancelRq = new LinkedList<RPCClientRequest>();
|
||||
synchronized (con) {
|
||||
Iterator<RPCClientRequest> iter = con.getRequests().values().iterator();
|
||||
while (iter.hasNext()) {
|
||||
final RPCClientRequest rq = iter.next();
|
||||
if (rq.getTimeQueued() + requestTimeout < now) {
|
||||
cancelRq.add(rq);
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
iter = con.getSendQueue().iterator();
|
||||
while (iter.hasNext()) {
|
||||
final RPCClientRequest rq = iter.next();
|
||||
if (rq.getTimeQueued() + requestTimeout < now) {
|
||||
cancelRq.add(rq);
|
||||
iter.remove();
|
||||
} else {
|
||||
// requests are ordered :-)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (RPCClientRequest rq : cancelRq) {
|
||||
rq.getResponse().requestFailed("sending RPC failed: request timed out");
|
||||
rq.freeBuffers();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
lastCheck = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
this.quit = true;
|
||||
this.interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes received and transferred from/to a server.
|
||||
* @param server
|
||||
* @return an array with the number of bytes received [0] and sent [1]
|
||||
*/
|
||||
public long[] getTransferStats(InetSocketAddress server) {
|
||||
RPCClientConnection con = null;
|
||||
synchronized (connections) {
|
||||
con = connections.get(server);
|
||||
}
|
||||
if (con == null)
|
||||
return null;
|
||||
else
|
||||
return new long[]{con.bytesRX,con.bytesTX};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.client;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import java.io.IOException;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferInputStream;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.MessageType;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.RPCHeader.ErrorResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCResponse<V extends Message> implements RPCResponseListener<V> {
|
||||
|
||||
private static final boolean TRACE_DUPLICATE_RESPONSES = false;
|
||||
|
||||
private RPCClientRequest request;
|
||||
|
||||
private RPCResponseAvailableListener<V> listener;
|
||||
|
||||
private String errorMessage;
|
||||
|
||||
private boolean failed;
|
||||
|
||||
private final V responsePrototype;
|
||||
|
||||
private Object attachment;
|
||||
|
||||
private ReusableBuffer message, data;
|
||||
|
||||
|
||||
public RPCResponse(V responsePrototype) {
|
||||
failed = false;
|
||||
this.responsePrototype = responsePrototype;
|
||||
}
|
||||
|
||||
public void freeBuffers() {
|
||||
if (request != null)
|
||||
request.freeBuffers();
|
||||
}
|
||||
|
||||
public void registerListener(RPCResponseAvailableListener<V> listener) {
|
||||
synchronized (this) {
|
||||
this.listener = listener;
|
||||
|
||||
if (request != null || failed) {
|
||||
//do notification
|
||||
listener.responseAvailable(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public V get() throws IOException, InterruptedException {
|
||||
waitForResult();
|
||||
if (failed) {
|
||||
throw new IOException(errorMessage);
|
||||
} else {
|
||||
if (request.getResponseHeader().getMessageType() == MessageType.RPC_RESPONSE_SUCCESS) {
|
||||
if (responsePrototype != null) {
|
||||
if (message != null) {
|
||||
ReusableBufferInputStream rbis = new ReusableBufferInputStream(message);
|
||||
V responseObject = (V) responsePrototype.newBuilderForType().mergeFrom(rbis).build();
|
||||
assert(responseObject != null);
|
||||
BufferPool.free(message);
|
||||
message = null;
|
||||
return responseObject;
|
||||
} else {
|
||||
return (V) responsePrototype.getDefaultInstanceForType();
|
||||
}
|
||||
} else {
|
||||
if (message != null)
|
||||
throw new RuntimeException("specify response prototype for null message!");
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
ErrorResponse err = request.getResponseHeader().getErrorResponse();
|
||||
throw new PBRPCException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setAttachment(Object attachment) {
|
||||
this.attachment = attachment;
|
||||
}
|
||||
|
||||
public Object getAttachment() {
|
||||
return this.attachment;
|
||||
}
|
||||
|
||||
public ReusableBuffer getData() throws InterruptedException {
|
||||
waitForResult();
|
||||
return data;
|
||||
}
|
||||
|
||||
public void waitForResult() throws InterruptedException {
|
||||
synchronized (this) {
|
||||
if (request == null && !failed)
|
||||
this.wait();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void responseAvailable(RPCClientRequest<V> request, ReusableBuffer message, ReusableBuffer data) {
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "response received");
|
||||
synchronized (this) {
|
||||
/*if (TRACE_DUPLICATE_RESPONSES) {
|
||||
if (responseTrace != null) {
|
||||
StringBuffer strace = new StringBuffer();
|
||||
for (int i = responseTrace.length-1; i >= 0; i--) {
|
||||
strace.append("\t");
|
||||
strace.append(responseTrace[i].toString());
|
||||
}
|
||||
throw new RuntimeException("response already set:\n"+strace.toString());
|
||||
} else {
|
||||
responseTrace = Thread.currentThread().getStackTrace();
|
||||
}
|
||||
}*/
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.request = request;
|
||||
if (listener != null)
|
||||
listener.responseAvailable(this);
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFailed(String errorMessage) {
|
||||
synchronized (this) {
|
||||
this.failed = true;
|
||||
this.errorMessage = errorMessage;
|
||||
if (listener != null)
|
||||
listener.responseAvailable(this);
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* duration of request from sending the request until the response
|
||||
* was received completeley.
|
||||
* @return duration in ns
|
||||
*/
|
||||
public long getDuration() {
|
||||
return request.getDuration();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.client;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public interface RPCResponseAvailableListener<V extends Message> {
|
||||
|
||||
public void responseAvailable(RPCResponse<V> r);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.client;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public interface RPCResponseListener<V extends Message> {
|
||||
|
||||
public void responseAvailable(RPCClientRequest<V> request, ReusableBuffer message, ReusableBuffer data);
|
||||
|
||||
public void requestFailed(String reason);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: include/PBRPC.proto
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.generatedinterfaces;
|
||||
|
||||
public final class PBRPC {
|
||||
private PBRPC() {}
|
||||
public static void registerAllExtensions(
|
||||
com.google.protobuf.ExtensionRegistry registry) {
|
||||
registry.add(org.xtreemfs.foundation.pbrpc.generatedinterfaces.PBRPC.procId);
|
||||
registry.add(org.xtreemfs.foundation.pbrpc.generatedinterfaces.PBRPC.dataIn);
|
||||
registry.add(org.xtreemfs.foundation.pbrpc.generatedinterfaces.PBRPC.dataOut);
|
||||
registry.add(org.xtreemfs.foundation.pbrpc.generatedinterfaces.PBRPC.interfaceId);
|
||||
}
|
||||
public static final int PROC_ID_FIELD_NUMBER = 50001;
|
||||
/**
|
||||
* <code>extend .google.protobuf.MethodOptions { ... }</code>
|
||||
*/
|
||||
public static final
|
||||
com.google.protobuf.GeneratedMessage.GeneratedExtension<
|
||||
com.google.protobuf.DescriptorProtos.MethodOptions,
|
||||
java.lang.Integer> procId = com.google.protobuf.GeneratedMessage
|
||||
.newFileScopedGeneratedExtension(
|
||||
java.lang.Integer.class,
|
||||
null);
|
||||
public static final int DATA_IN_FIELD_NUMBER = 50004;
|
||||
/**
|
||||
* <code>extend .google.protobuf.MethodOptions { ... }</code>
|
||||
*/
|
||||
public static final
|
||||
com.google.protobuf.GeneratedMessage.GeneratedExtension<
|
||||
com.google.protobuf.DescriptorProtos.MethodOptions,
|
||||
java.lang.Boolean> dataIn = com.google.protobuf.GeneratedMessage
|
||||
.newFileScopedGeneratedExtension(
|
||||
java.lang.Boolean.class,
|
||||
null);
|
||||
public static final int DATA_OUT_FIELD_NUMBER = 50003;
|
||||
/**
|
||||
* <code>extend .google.protobuf.MethodOptions { ... }</code>
|
||||
*/
|
||||
public static final
|
||||
com.google.protobuf.GeneratedMessage.GeneratedExtension<
|
||||
com.google.protobuf.DescriptorProtos.MethodOptions,
|
||||
java.lang.Boolean> dataOut = com.google.protobuf.GeneratedMessage
|
||||
.newFileScopedGeneratedExtension(
|
||||
java.lang.Boolean.class,
|
||||
null);
|
||||
public static final int INTERFACE_ID_FIELD_NUMBER = 50002;
|
||||
/**
|
||||
* <code>extend .google.protobuf.ServiceOptions { ... }</code>
|
||||
*/
|
||||
public static final
|
||||
com.google.protobuf.GeneratedMessage.GeneratedExtension<
|
||||
com.google.protobuf.DescriptorProtos.ServiceOptions,
|
||||
java.lang.Integer> interfaceId = com.google.protobuf.GeneratedMessage
|
||||
.newFileScopedGeneratedExtension(
|
||||
java.lang.Integer.class,
|
||||
null);
|
||||
|
||||
public static com.google.protobuf.Descriptors.FileDescriptor
|
||||
getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
private static com.google.protobuf.Descriptors.FileDescriptor
|
||||
descriptor;
|
||||
static {
|
||||
java.lang.String[] descriptorData = {
|
||||
"\n\023include/PBRPC.proto\022\016xtreemfs.pbrpc\032 g" +
|
||||
"oogle/protobuf/descriptor.proto:1\n\007proc_" +
|
||||
"id\022\036.google.protobuf.MethodOptions\030\321\206\003 \001" +
|
||||
"(\007:1\n\007data_in\022\036.google.protobuf.MethodOp" +
|
||||
"tions\030\324\206\003 \001(\010:2\n\010data_out\022\036.google.proto" +
|
||||
"buf.MethodOptions\030\323\206\003 \001(\010:7\n\014interface_i" +
|
||||
"d\022\037.google.protobuf.ServiceOptions\030\322\206\003 \001" +
|
||||
"(\007B3\n1org.xtreemfs.foundation.pbrpc.gene" +
|
||||
"ratedinterfaces"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
public com.google.protobuf.ExtensionRegistry assignDescriptors(
|
||||
com.google.protobuf.Descriptors.FileDescriptor root) {
|
||||
descriptor = root;
|
||||
procId.internalInit(descriptor.getExtensions().get(0));
|
||||
dataIn.internalInit(descriptor.getExtensions().get(1));
|
||||
dataOut.internalInit(descriptor.getExtensions().get(2));
|
||||
interfaceId.internalInit(descriptor.getExtensions().get(3));
|
||||
return null;
|
||||
}
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor
|
||||
.internalBuildGeneratedFileFrom(descriptorData,
|
||||
new com.google.protobuf.Descriptors.FileDescriptor[] {
|
||||
com.google.protobuf.DescriptorProtos.getDescriptor(),
|
||||
}, assigner);
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(outer_class_scope)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
//automatically generated from Ping.proto at Thu Dec 11 16:01:46 CET 2014
|
||||
//(c) 2014. See LICENSE file for details.
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.generatedinterfaces;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.net.InetSocketAddress;
|
||||
import com.google.protobuf.Message;
|
||||
import com.google.protobuf.ByteString;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.Auth;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.UserCredentials;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCNIOSocketClient;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCResponse;
|
||||
|
||||
public class PingServiceClient {
|
||||
|
||||
private RPCNIOSocketClient client;
|
||||
private InetSocketAddress defaultServer;
|
||||
|
||||
public PingServiceClient(RPCNIOSocketClient client, InetSocketAddress defaultServer) {
|
||||
this.client = client;
|
||||
this.defaultServer = defaultServer;
|
||||
}
|
||||
|
||||
public RPCResponse<Ping.PingResponse> doPing(InetSocketAddress server, Auth authHeader, UserCredentials userCreds, Ping.PingRequest input, ReusableBuffer data) throws IOException {
|
||||
if (server == null) server = defaultServer;
|
||||
if (server == null) throw new IllegalArgumentException("defaultServer must be set in constructor if you want to pass null as server in calls");
|
||||
RPCResponse<Ping.PingResponse> response = new RPCResponse<Ping.PingResponse>(Ping.PingResponse.getDefaultInstance());
|
||||
client.sendRequest(server, authHeader, userCreds, 1, 1, input, data, response, false);
|
||||
return response;
|
||||
}
|
||||
|
||||
public RPCResponse<Ping.PingResponse> doPing(InetSocketAddress server, Auth authHeader, UserCredentials userCreds, String text, boolean sendError, ReusableBuffer data) throws IOException {
|
||||
final Ping.PingRequest msg = Ping.PingRequest.newBuilder().setText(text).setSendError(sendError).build();
|
||||
return doPing(server, authHeader, userCreds,msg, data);
|
||||
}
|
||||
|
||||
public RPCResponse emptyPing(InetSocketAddress server, Auth authHeader, UserCredentials userCreds, Ping.Ping_emptyRequest input) throws IOException {
|
||||
if (server == null) server = defaultServer;
|
||||
if (server == null) throw new IllegalArgumentException("defaultServer must be set in constructor if you want to pass null as server in calls");
|
||||
RPCResponse response = new RPCResponse(null);
|
||||
client.sendRequest(server, authHeader, userCreds, 1, 2, input, null, response, false);
|
||||
return response;
|
||||
}
|
||||
|
||||
public RPCResponse emptyPing(InetSocketAddress server, Auth authHeader, UserCredentials userCreds) throws IOException {
|
||||
|
||||
return emptyPing(server, authHeader, userCreds,null);
|
||||
}
|
||||
|
||||
public boolean clientIsAlive() {
|
||||
return client.isAlive();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//automatically generated from Ping.proto at Thu Dec 11 16:01:46 CET 2014
|
||||
//(c) 2014. See LICENSE file for details.
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.generatedinterfaces;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
public class PingServiceConstants {
|
||||
|
||||
public static final int INTERFACE_ID = 1;
|
||||
public static final int PROC_ID_DOPING = 1;
|
||||
public static final int PROC_ID_EMPTYPING = 2;
|
||||
|
||||
public static Message getRequestMessage(int procId) {
|
||||
switch (procId) {
|
||||
case 1: return Ping.PingRequest.getDefaultInstance();
|
||||
case 2: return null;
|
||||
default: throw new RuntimeException("unknown procedure id");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Message getResponseMessage(int procId) {
|
||||
switch (procId) {
|
||||
case 1: return Ping.PingResponse.getDefaultInstance();
|
||||
case 2: return null;
|
||||
default: throw new RuntimeException("unknown procedure id");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,787 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.BindException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.xtreemfs.foundation.LifeCycleThread;
|
||||
import org.xtreemfs.foundation.SSLOptions;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.ChannelIO;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.SSLChannelIO;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.SSLHandshakeOnlyChannelIO;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCNIOSocketServer extends LifeCycleThread implements RPCServerInterface {
|
||||
|
||||
/**
|
||||
* Maximum number of record fragments supported.
|
||||
*/
|
||||
public static final int MAX_FRAGMENTS = 1;
|
||||
|
||||
/**
|
||||
* Maximum fragment size to accept. If the size is larger, the connection is
|
||||
* closed.
|
||||
*/
|
||||
public static final int MAX_FRAGMENT_SIZE = 1024 * 1024 * 32;
|
||||
|
||||
/**
|
||||
* the server socket
|
||||
*/
|
||||
private final ServerSocketChannel socket;
|
||||
|
||||
/**
|
||||
* Selector for server socket
|
||||
*/
|
||||
private final Selector selector;
|
||||
|
||||
/**
|
||||
* If set to true thei main loop will exit upon next invocation
|
||||
*/
|
||||
private volatile boolean quit;
|
||||
|
||||
/**
|
||||
* The receiver that gets all incoming requests.
|
||||
*/
|
||||
private volatile RPCServerRequestListener receiver;
|
||||
|
||||
/**
|
||||
* sslOptions if SSL is enabled, null otherwise
|
||||
*/
|
||||
private final SSLOptions sslOptions;
|
||||
|
||||
/**
|
||||
* Connection count
|
||||
*/
|
||||
private final AtomicInteger numConnections;
|
||||
|
||||
/**
|
||||
* Number of requests received but not answered
|
||||
*/
|
||||
private long pendingRequests;
|
||||
|
||||
/**
|
||||
* Port on which the server listens for incoming connections.
|
||||
*/
|
||||
private final int bindPort;
|
||||
|
||||
private final List<RPCNIOSocketServerConnection> connections;
|
||||
|
||||
/**
|
||||
* maximum number of pending client requests to allow
|
||||
*/
|
||||
private final int maxClientQLength;
|
||||
|
||||
/**
|
||||
* if the Q was full we need at least CLIENT_Q_THR spaces before we start
|
||||
* reading from the client again. This is to prevent it from oscillating
|
||||
*/
|
||||
private final int clientQThreshold;
|
||||
|
||||
public static final int DEFAULT_MAX_CLIENT_Q_LENGTH = 100;
|
||||
|
||||
public RPCNIOSocketServer(int bindPort, InetAddress bindAddr, RPCServerRequestListener rl,
|
||||
SSLOptions sslOptions) throws IOException {
|
||||
this(bindPort, bindAddr, rl, sslOptions, -1);
|
||||
}
|
||||
|
||||
public RPCNIOSocketServer(int bindPort, InetAddress bindAddr, RPCServerRequestListener rl,
|
||||
SSLOptions sslOptions, int receiveBufferSize) throws IOException {
|
||||
this(bindPort, bindAddr, rl, sslOptions, receiveBufferSize, DEFAULT_MAX_CLIENT_Q_LENGTH);
|
||||
}
|
||||
|
||||
public RPCNIOSocketServer(int bindPort, InetAddress bindAddr, RPCServerRequestListener rl,
|
||||
SSLOptions sslOptions, int receiveBufferSize,
|
||||
int maxClientQLength) throws IOException {
|
||||
super("PBRPCSrv@" + bindPort);
|
||||
|
||||
// open server socket
|
||||
socket = ServerSocketChannel.open();
|
||||
socket.configureBlocking(false);
|
||||
|
||||
if (receiveBufferSize != -1) {
|
||||
socket.socket().setReceiveBufferSize(receiveBufferSize);
|
||||
try {
|
||||
if (socket.socket().getReceiveBufferSize() != receiveBufferSize) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"could not set socket receive buffer size to " + receiveBufferSize
|
||||
+ ", using default size of " + socket.socket().getReceiveBufferSize());
|
||||
}
|
||||
} catch (SocketException exc) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this,
|
||||
"could not check whether receive buffer size was successfully set to %d bytes", receiveBufferSize);
|
||||
}
|
||||
} else {
|
||||
socket.socket().setReceiveBufferSize(256 * 1024);
|
||||
}
|
||||
|
||||
socket.socket().setReuseAddress(true);
|
||||
try {
|
||||
socket.socket().bind(
|
||||
bindAddr == null ? new InetSocketAddress(bindPort) : new InetSocketAddress(bindAddr, bindPort));
|
||||
} catch (BindException e) {
|
||||
// Rethrow exception with the failed port number.
|
||||
throw new BindException(e.getMessage() + ". Port number: " + bindPort);
|
||||
}
|
||||
this.bindPort = bindPort;
|
||||
|
||||
// create a selector and register socket
|
||||
selector = Selector.open();
|
||||
socket.register(selector, SelectionKey.OP_ACCEPT);
|
||||
|
||||
// server is ready to accept connections now
|
||||
|
||||
this.receiver = rl;
|
||||
|
||||
this.sslOptions = sslOptions;
|
||||
|
||||
this.numConnections = new AtomicInteger(0);
|
||||
|
||||
this.connections = new LinkedList<RPCNIOSocketServerConnection>();
|
||||
|
||||
this.maxClientQLength = maxClientQLength;
|
||||
this.clientQThreshold = (maxClientQLength/2 >= 0) ? maxClientQLength/2 : 0;
|
||||
if (maxClientQLength <= 1) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, this, "max client queue length is 1, pipelining is disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the server and close all connections.
|
||||
*/
|
||||
@Override
|
||||
public void shutdown() {
|
||||
this.quit = true;
|
||||
this.interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a response.
|
||||
*
|
||||
* @param request
|
||||
* the request
|
||||
*/
|
||||
@Override
|
||||
public void sendResponse(RPCServerRequest request, RPCServerResponse response) {
|
||||
assert (response != null);
|
||||
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "response sent");
|
||||
final RPCNIOSocketServerConnection connection = (RPCNIOSocketServerConnection)request.getConnection();
|
||||
try {
|
||||
request.freeBuffers();
|
||||
} catch (AssertionError ex) {
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this, "Caught an AssertionError while trying to free buffers:");
|
||||
Logging.logError(Logging.LEVEL_INFO, this, ex);
|
||||
}
|
||||
}
|
||||
assert (connection.getServer() == this);
|
||||
|
||||
if (!connection.isConnectionClosed()) {
|
||||
synchronized (connection) {
|
||||
boolean isEmpty = connection.getPendingResponses().isEmpty();
|
||||
connection.addPendingResponse(response);
|
||||
if (isEmpty) {
|
||||
final SelectionKey key = connection.getChannel().keyFor(selector);
|
||||
if (key != null) {
|
||||
try {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
} catch (CancelledKeyException e) {
|
||||
// Ignore it since the timeout mechanism will deal with it.
|
||||
}
|
||||
}
|
||||
selector.wakeup();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ignore and free bufers
|
||||
response.freeBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
notifyStarted();
|
||||
|
||||
if (Logging.isInfo()) {
|
||||
String sslMode = "";
|
||||
if (sslOptions != null) {
|
||||
if (sslOptions.isFakeSSLMode()) {
|
||||
sslMode = "GRID SSL mode enabled (SSL handshake only)";
|
||||
} else {
|
||||
sslMode = "SSL enabled (" + sslOptions.getSSLProtocol() + ")";
|
||||
}
|
||||
}
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this, "PBRPC Srv %d ready %s", bindPort,sslMode);
|
||||
}
|
||||
|
||||
try {
|
||||
while (!quit) {
|
||||
// try to select events...
|
||||
int numKeys = 0;
|
||||
try {
|
||||
numKeys = selector.select();
|
||||
} catch (CancelledKeyException ex) {
|
||||
// who cares
|
||||
} catch (IOException ex) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"Exception while selecting: %s", ex.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (numKeys > 0) {
|
||||
// fetch events
|
||||
Set<SelectionKey> keys = selector.selectedKeys();
|
||||
Iterator<SelectionKey> iter = keys.iterator();
|
||||
|
||||
// process all events
|
||||
while (iter.hasNext()) {
|
||||
SelectionKey key = iter.next();
|
||||
|
||||
// remove key from the list
|
||||
iter.remove();
|
||||
try {
|
||||
|
||||
if (key.isAcceptable()) {
|
||||
acceptConnection(key);
|
||||
}
|
||||
if (key.isReadable()) {
|
||||
readConnection(key);
|
||||
}
|
||||
if (key.isWritable()) {
|
||||
writeConnection(key);
|
||||
}
|
||||
} catch (CancelledKeyException ex) {
|
||||
// nobody cares...
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (RPCNIOSocketServerConnection con : connections) {
|
||||
try {
|
||||
con.getChannel().close();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// close socket
|
||||
selector.close();
|
||||
socket.close();
|
||||
|
||||
if (Logging.isInfo())
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this,
|
||||
"PBRPC Server %d shutdown complete", bindPort);
|
||||
|
||||
notifyStopped();
|
||||
} catch (Throwable thr) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.net, this, "PBRPC Server %d CRASHED!", bindPort);
|
||||
notifyCrashed(thr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* read data from a readable connection
|
||||
*
|
||||
* @param key
|
||||
* a readable key
|
||||
*/
|
||||
private void readConnection(SelectionKey key) {
|
||||
|
||||
final RPCNIOSocketServerConnection con = (RPCNIOSocketServerConnection) key.attachment();
|
||||
final ChannelIO channel = con.getChannel();
|
||||
|
||||
try {
|
||||
|
||||
if (!channel.isShutdownInProgress()) {
|
||||
if (channel.doHandshake(key)) {
|
||||
while (true) {
|
||||
if (con.getOpenRequests().get() > maxClientQLength) {
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"client sent too many requests... not accepting new requests from %s, q=%d", con
|
||||
.getChannel().socket().getRemoteSocketAddress().toString(), con.getOpenRequests().get());
|
||||
return;
|
||||
}
|
||||
|
||||
ByteBuffer buf = null;
|
||||
switch (con.getReceiveState()) {
|
||||
case RECORD_MARKER: {
|
||||
buf = con.getReceiveRecordMarker(); break;
|
||||
}
|
||||
case RPC_MESSAGE: {
|
||||
buf = con.getReceiveBuffers()[1].getBuffer(); break;
|
||||
}
|
||||
case RPC_HEADER: {
|
||||
buf = con.getReceiveBuffers()[0].getBuffer(); break;
|
||||
}
|
||||
case DATA: {
|
||||
buf = con.getReceiveBuffers()[2].getBuffer(); break;
|
||||
}
|
||||
}
|
||||
|
||||
// read fragment header
|
||||
final int numBytesRead = readData(key, channel, buf);
|
||||
if (numBytesRead == -1) {
|
||||
// connection closed
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"client closed connection (EOF): %s", channel.socket()
|
||||
.getRemoteSocketAddress().toString());
|
||||
}
|
||||
closeConnection(key);
|
||||
return;
|
||||
}
|
||||
if (buf.hasRemaining()) {
|
||||
// not enough data...
|
||||
break;
|
||||
}
|
||||
|
||||
switch (con.getReceiveState()) {
|
||||
case RECORD_MARKER: {
|
||||
buf.position(0);
|
||||
final int hdrLen = buf.getInt();
|
||||
final int msgLen = buf.getInt();
|
||||
final int dataLen = buf.getInt();
|
||||
|
||||
if ((hdrLen <= 0) || (hdrLen >= MAX_FRAGMENT_SIZE)
|
||||
|| (msgLen < 0) || (msgLen >= MAX_FRAGMENT_SIZE)
|
||||
|| (dataLen < 0) || (dataLen >= MAX_FRAGMENT_SIZE)) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.net, this,
|
||||
"invalid record marker size (%d/%d/%d) received, closing connection to client %s",
|
||||
hdrLen,msgLen,dataLen,channel.socket()
|
||||
.getRemoteSocketAddress().toString());
|
||||
closeConnection(key);
|
||||
return;
|
||||
}
|
||||
final ReusableBuffer[] buffers = new ReusableBuffer[]{BufferPool.allocate(hdrLen),
|
||||
((msgLen > 0) ? BufferPool.allocate(msgLen) : null),
|
||||
((dataLen > 0) ? BufferPool.allocate(dataLen) : null) };
|
||||
con.setReceiveBuffers(buffers);
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.RPC_HEADER);
|
||||
continue;
|
||||
}
|
||||
|
||||
case RPC_HEADER: {
|
||||
if (con.getReceiveBuffers()[1] != null) {
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.RPC_MESSAGE);
|
||||
continue;
|
||||
} else {
|
||||
if (con.getReceiveBuffers()[2] != null) {
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.DATA);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
case RPC_MESSAGE: {
|
||||
if (con.getReceiveBuffers()[2] != null) {
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.DATA);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//assemble ServerRequest
|
||||
con.setReceiveState(RPCNIOSocketServerConnection.ReceiveState.RECORD_MARKER);
|
||||
con.getReceiveRecordMarker().clear();
|
||||
|
||||
ReusableBuffer[] receiveBuffers = con.getReceiveBuffers();
|
||||
receiveBuffers[0].flip();
|
||||
if (receiveBuffers[1] != null)
|
||||
receiveBuffers[1].flip();
|
||||
if (receiveBuffers[2] != null)
|
||||
receiveBuffers[2].flip();
|
||||
con.setReceiveBuffers(null);
|
||||
|
||||
RPCServerRequest rq = null;
|
||||
try {
|
||||
rq = new RPCServerRequest(con, receiveBuffers[0], receiveBuffers[1], receiveBuffers[2]);
|
||||
} catch (IOException ex) {
|
||||
// close connection if the header cannot be parsed
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.net,this,"invalid PBRPC header received: "+ex);
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logError(Logging.LEVEL_DEBUG, this,ex);
|
||||
}
|
||||
closeConnection(key);
|
||||
BufferPool.free(receiveBuffers[1]);
|
||||
BufferPool.free(receiveBuffers[2]);
|
||||
return;
|
||||
}
|
||||
// request is
|
||||
// complete... send to receiver
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, rq
|
||||
.toString());
|
||||
}
|
||||
con.getOpenRequests().incrementAndGet();
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"request received");
|
||||
pendingRequests++;
|
||||
if (!receiveRequest(key, rq, con)) {
|
||||
closeConnection(key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CancelledKeyException ex) {
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this,
|
||||
"client closed connection (CancelledKeyException): %s", channel.socket().getRemoteSocketAddress()
|
||||
.toString());
|
||||
}
|
||||
closeConnection(key);
|
||||
} catch (ClosedByInterruptException ex) {
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this,
|
||||
"client closed connection (EOF): %s", channel.socket().getRemoteSocketAddress()
|
||||
.toString());
|
||||
}
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"connection to %s closed by remote peer", con.getChannel().socket()
|
||||
.getRemoteSocketAddress().toString());
|
||||
}
|
||||
closeConnection(key);
|
||||
} catch (IOException ex) {
|
||||
// simply close the connection
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, OutputUtils
|
||||
.stackTraceToString(ex));
|
||||
}
|
||||
closeConnection(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write data to a writeable connection
|
||||
*
|
||||
* @param key
|
||||
* the writable key
|
||||
*/
|
||||
private void writeConnection(SelectionKey key) {
|
||||
|
||||
final RPCNIOSocketServerConnection con = (RPCNIOSocketServerConnection) key.attachment();
|
||||
final ChannelIO channel = con.getChannel();
|
||||
|
||||
try {
|
||||
|
||||
if (!channel.isShutdownInProgress()) {
|
||||
if (channel.doHandshake(key)) {
|
||||
|
||||
while (true) {
|
||||
|
||||
// final ByteBuffer fragmentHeader =
|
||||
// con.getSendFragHdr();
|
||||
|
||||
ByteBuffer[] response = con.getSendBuffers();
|
||||
if (response == null) {
|
||||
synchronized (con) {
|
||||
RPCServerResponse rq = con.getPendingResponses().peek();
|
||||
if (rq == null) {
|
||||
// no more responses, stop writing...
|
||||
con.setSendBuffers(null);
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
response = rq.packBuffers(con.getSendFragHdr());
|
||||
con.setSendBuffers(response);
|
||||
con.setExpectedRecordSize(rq.getRpcMessageSize());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if (fragmentHeader.hasRemaining()) { final int
|
||||
* numBytesWritten = writeData(key, channel,
|
||||
* fragmentHeader); if (numBytesWritten == -1) {
|
||||
* //connection closed closeConnection(key); return; }
|
||||
* if (fragmentHeader.hasRemaining()) { //not enough
|
||||
* data... break; } //finished sending... send fragment
|
||||
* data now... } else {
|
||||
*/
|
||||
// send fragment data
|
||||
assert(response != null);
|
||||
final long numBytesWritten = channel.write(response);
|
||||
if (numBytesWritten == -1) {
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this,
|
||||
"client closed connection (EOF): %s", channel.socket()
|
||||
.getRemoteSocketAddress().toString());
|
||||
}
|
||||
// connection closed
|
||||
closeConnection(key);
|
||||
return;
|
||||
}
|
||||
con.recordBytesSent(numBytesWritten);
|
||||
|
||||
if (response[response.length-1].hasRemaining()) {
|
||||
// not enough data...
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
con.checkEnoughBytesSent();
|
||||
// finished sending fragment
|
||||
// clean up :-) request finished
|
||||
pendingRequests--;
|
||||
RPCServerResponse rq = con.getPendingResponses().poll();
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"sent response for %s", rq.toString());
|
||||
}
|
||||
rq.freeBuffers();
|
||||
con.setSendBuffers(null);
|
||||
con.getSendFragHdr().clear();
|
||||
int numRq = con.getOpenRequests().decrementAndGet();
|
||||
|
||||
if ((key.interestOps() & SelectionKey.OP_READ) == 0) {
|
||||
if (numRq < clientQThreshold) {
|
||||
// read from client again
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"client allowed to send data again: %s, q=%d", con.getChannel().socket()
|
||||
.getRemoteSocketAddress().toString(), numRq);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CancelledKeyException ex) {
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this,
|
||||
"client closed connection (CancelledKeyException): %s", channel.socket().getRemoteSocketAddress()
|
||||
.toString());
|
||||
}
|
||||
closeConnection(key);
|
||||
} catch (ClosedByInterruptException ex) {
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this,
|
||||
"client closed connection (EOF): %s", channel.socket().getRemoteSocketAddress()
|
||||
.toString());
|
||||
}
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"connection to %s closed by remote peer", con.getChannel().socket()
|
||||
.getRemoteSocketAddress().toString());
|
||||
}
|
||||
closeConnection(key);
|
||||
} catch (IOException ex) {
|
||||
// simply close the connection
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, OutputUtils
|
||||
.stackTraceToString(ex));
|
||||
}
|
||||
closeConnection(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the socket, ensures that SSL connection is ready
|
||||
*
|
||||
* @param key
|
||||
* the SelectionKey
|
||||
* @param channel
|
||||
* the channel to read from
|
||||
* @param buf
|
||||
* the buffer to read to
|
||||
* @return number of bytes read, -1 on EOF
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static int readData(SelectionKey key, ChannelIO channel, ByteBuffer buf) throws IOException {
|
||||
return channel.read(buf);
|
||||
/*
|
||||
* if (!channel.isShutdownInProgress()) { if (channel.doHandshake(key))
|
||||
* { return channel.read(buf); } else { return 0; } } else { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
public static int writeData(SelectionKey key, ChannelIO channel, ByteBuffer buf) throws IOException {
|
||||
return channel.write(buf);
|
||||
/*
|
||||
* if (!channel.isShutdownInProgress()) { if (channel.doHandshake(key))
|
||||
* { return channel.write(buf); } else { return 0; } } else { return 0;
|
||||
* }
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* close a connection
|
||||
*
|
||||
* @param key
|
||||
* matching key
|
||||
*/
|
||||
private void closeConnection(SelectionKey key) {
|
||||
final RPCNIOSocketServerConnection con = (RPCNIOSocketServerConnection) key.attachment();
|
||||
final ChannelIO channel = con.getChannel();
|
||||
|
||||
// remove the connection from the selector and close socket
|
||||
try {
|
||||
connections.remove(con);
|
||||
con.setConnectionClosed(true);
|
||||
key.cancel();
|
||||
channel.close();
|
||||
} catch (Exception ex) {
|
||||
} finally {
|
||||
// adjust connection count and make sure buffers are freed
|
||||
numConnections.decrementAndGet();
|
||||
con.freeBuffers();
|
||||
}
|
||||
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "closing connection to %s", channel
|
||||
.socket().getRemoteSocketAddress().toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* accept a new incomming connection
|
||||
*
|
||||
* @param key
|
||||
* the acceptable key
|
||||
*/
|
||||
private void acceptConnection(SelectionKey key) {
|
||||
SocketChannel client = null;
|
||||
RPCNIOSocketServerConnection con = null;
|
||||
ChannelIO channelIO = null;
|
||||
// FIXME: Better exception handling!
|
||||
|
||||
try {
|
||||
|
||||
// accept that connection
|
||||
client = socket.accept();
|
||||
|
||||
if (sslOptions == null) {
|
||||
channelIO = new ChannelIO(client);
|
||||
} else {
|
||||
if (sslOptions.isFakeSSLMode()) {
|
||||
channelIO = new SSLHandshakeOnlyChannelIO(client, sslOptions, false);
|
||||
} else {
|
||||
channelIO = new SSLChannelIO(client, sslOptions, false);
|
||||
}
|
||||
}
|
||||
con = new RPCNIOSocketServerConnection(this,channelIO);
|
||||
|
||||
// and configure it to be non blocking
|
||||
// IMPORTANT!
|
||||
client.configureBlocking(false);
|
||||
client.register(selector, SelectionKey.OP_READ, con);
|
||||
client.socket().setTcpNoDelay(true);
|
||||
|
||||
numConnections.incrementAndGet();
|
||||
|
||||
this.connections.add(con);
|
||||
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "connect from client at %s",
|
||||
client.socket().getRemoteSocketAddress().toString());
|
||||
}
|
||||
|
||||
} catch (ClosedChannelException ex) {
|
||||
if (Logging.isInfo()) {
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this,
|
||||
"client closed connection during accept");
|
||||
}
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"cannot establish connection: %s", ex.toString());
|
||||
if (channelIO != null) {
|
||||
try {
|
||||
channelIO.close();
|
||||
} catch (IOException ex2) {
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"cannot establish connection: %s", ex.toString());
|
||||
if (channelIO != null) {
|
||||
try {
|
||||
channelIO.close();
|
||||
} catch (IOException ex2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* @param request
|
||||
* @param con
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
private boolean receiveRequest(SelectionKey key, RPCServerRequest request, RPCNIOSocketServerConnection con) {
|
||||
try {
|
||||
request.getHeader();
|
||||
|
||||
receiver.receiveRecord(request);
|
||||
return true;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, Category.net,this,"invalid PBRPC header received: "+ex);
|
||||
if (Logging.isDebug()) {
|
||||
Logging.logError(Logging.LEVEL_DEBUG, this,ex);
|
||||
}
|
||||
return false;
|
||||
//closeConnection(key);
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumConnections() {
|
||||
return this.numConnections.get();
|
||||
}
|
||||
|
||||
public long getPendingRequests() {
|
||||
return this.pendingRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the listener. Handle with care.
|
||||
*
|
||||
* @param rl
|
||||
*/
|
||||
public void updateRequestDispatcher(RPCServerRequestListener rl) {
|
||||
this.receiver = rl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.ChannelIO;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.RecordMarker;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCNIOSocketServerConnection implements RPCServerConnectionInterface {
|
||||
|
||||
public enum ReceiveState {
|
||||
RECORD_MARKER,
|
||||
RPC_HEADER,
|
||||
RPC_MESSAGE,
|
||||
DATA
|
||||
};
|
||||
|
||||
private final AtomicInteger openRequests;
|
||||
|
||||
private Queue<RPCServerResponse> pendingResponses;
|
||||
|
||||
private final ChannelIO channel;
|
||||
|
||||
private final ByteBuffer receiveRecordMarker;
|
||||
|
||||
private final ByteBuffer sendFragHdr;
|
||||
|
||||
private ReusableBuffer[] receiveBuffers;
|
||||
|
||||
private ReceiveState receiveState;
|
||||
|
||||
private ByteBuffer[] sendBuffers;
|
||||
|
||||
private volatile boolean connectionClosed;
|
||||
|
||||
private SocketAddress clientAddress;
|
||||
|
||||
private RPCServerInterface server;
|
||||
|
||||
private long bytesSent;
|
||||
|
||||
private int expectedRecordSize;
|
||||
|
||||
public RPCNIOSocketServerConnection(RPCServerInterface server, ChannelIO channel) {
|
||||
assert(server != null);
|
||||
assert(channel != null);
|
||||
this.channel = channel;
|
||||
this.openRequests = new AtomicInteger(0);
|
||||
this.pendingResponses = new ConcurrentLinkedQueue<RPCServerResponse>();
|
||||
this.connectionClosed = false;
|
||||
this.receiveRecordMarker = ByteBuffer.allocate(RecordMarker.HDR_SIZE);
|
||||
this.sendFragHdr = ByteBuffer.allocate(RecordMarker.HDR_SIZE);
|
||||
this.receiveState = ReceiveState.RECORD_MARKER;
|
||||
this.server = server;
|
||||
try {
|
||||
this.clientAddress = channel.socket().getRemoteSocketAddress();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the receiveState
|
||||
*/
|
||||
public ReceiveState getReceiveState() {
|
||||
return receiveState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param receiveState the receiveState to set
|
||||
*/
|
||||
public void setReceiveState(ReceiveState receiveState) {
|
||||
this.receiveState = receiveState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RPCServerInterface getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getSender() {
|
||||
return clientAddress;
|
||||
}
|
||||
|
||||
public void freeBuffers() {
|
||||
if (receiveBuffers != null) {
|
||||
for (ReusableBuffer buffer : receiveBuffers)
|
||||
BufferPool.free(buffer);
|
||||
}
|
||||
for (RPCServerResponse r : pendingResponses) {
|
||||
r.freeBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the openRequests
|
||||
*/
|
||||
public AtomicInteger getOpenRequests() {
|
||||
return openRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the channel
|
||||
*/
|
||||
@Override
|
||||
public ChannelIO getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the connectionClosed
|
||||
*/
|
||||
public boolean isConnectionClosed() {
|
||||
return connectionClosed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param connectionClosed the connectionClosed to set
|
||||
*/
|
||||
public void setConnectionClosed(boolean connectionClosed) {
|
||||
this.connectionClosed = connectionClosed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the pendingResponses
|
||||
*/
|
||||
public Queue<RPCServerResponse> getPendingResponses() {
|
||||
return pendingResponses;
|
||||
}
|
||||
|
||||
public void addPendingResponse(RPCServerResponse rq) {
|
||||
this.pendingResponses.add(rq);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fragmentHeader
|
||||
*/
|
||||
ByteBuffer getReceiveRecordMarker() {
|
||||
return receiveRecordMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fragmentHeader
|
||||
*/
|
||||
ByteBuffer getSendFragHdr() {
|
||||
return sendFragHdr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the receive
|
||||
*/
|
||||
ReusableBuffer[] getReceiveBuffers() {
|
||||
return receiveBuffers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param receive the receive to set
|
||||
*/
|
||||
void setReceiveBuffers(ReusableBuffer[] receive) {
|
||||
this.receiveBuffers = receive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the send
|
||||
*/
|
||||
ByteBuffer[] getSendBuffers() {
|
||||
return sendBuffers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param send the send to set
|
||||
*/
|
||||
void setSendBuffers(ByteBuffer[] send) {
|
||||
this.sendBuffers = send;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the clientAddress
|
||||
*/
|
||||
public SocketAddress getClientAddress() {
|
||||
return clientAddress;
|
||||
}
|
||||
|
||||
public void setExpectedRecordSize(int expectedRecordSize) {
|
||||
this.expectedRecordSize = expectedRecordSize;
|
||||
bytesSent = 0;
|
||||
}
|
||||
|
||||
public void recordBytesSent(long bytesSent) {
|
||||
this.bytesSent += bytesSent;
|
||||
if (this.bytesSent > expectedRecordSize) {
|
||||
String errorMessage = "Too many bytes written (expected: "
|
||||
+ expectedRecordSize
|
||||
+ ", actual: "
|
||||
+ this.bytesSent
|
||||
+ ") in connection to "
|
||||
+ clientAddress;
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, this, errorMessage);
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkEnoughBytesSent() {
|
||||
if (bytesSent != expectedRecordSize) {
|
||||
String errorMessage = "Incorrect record length sent (expected: "
|
||||
+ expectedRecordSize
|
||||
+ ", actual: "
|
||||
+ bytesSent
|
||||
+ ") in connection to "
|
||||
+ clientAddress;
|
||||
Logging.logMessage(Logging.LEVEL_ERROR, this, errorMessage);
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import org.xtreemfs.foundation.pbrpc.channels.ChannelIO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public interface RPCServerConnectionInterface {
|
||||
|
||||
public RPCServerInterface getServer();
|
||||
|
||||
public SocketAddress getSender();
|
||||
|
||||
public ChannelIO getChannel();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public interface RPCServerInterface {
|
||||
|
||||
public void sendResponse(RPCServerRequest request, RPCServerResponse response);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferInputStream;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCServerRequest {
|
||||
|
||||
private RPC.RPCHeader header;
|
||||
private ReusableBuffer message;
|
||||
private ReusableBuffer data;
|
||||
private final RPCServerConnectionInterface connection;
|
||||
|
||||
public RPCServerRequest(RPCServerConnectionInterface connection, ReusableBuffer headerBuffer, ReusableBuffer message, ReusableBuffer data) throws IOException {
|
||||
try {
|
||||
ReusableBufferInputStream rbis = new ReusableBufferInputStream(headerBuffer);
|
||||
header = RPC.RPCHeader.parseFrom(rbis);
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.connection = connection;
|
||||
} finally {
|
||||
BufferPool.free(headerBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
public RPCServerRequest(RPCServerConnectionInterface connection, RPC.RPCHeader header, ReusableBuffer message) {
|
||||
this.header = header;
|
||||
this.message = message;
|
||||
this.data = null;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
public RPC.RPCHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the message
|
||||
*/
|
||||
public ReusableBuffer getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the data
|
||||
*/
|
||||
public ReusableBuffer getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void freeBuffers() {
|
||||
BufferPool.free(message);
|
||||
BufferPool.free(data);
|
||||
}
|
||||
|
||||
public void sendError(RPC.ErrorType type, RPC.POSIXErrno errno, String message, String debugInfo) {
|
||||
RPC.RPCHeader.ErrorResponse resp = RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(type).setPosixErrno(errno).setErrorMessage(message).setDebugInfo(debugInfo).build();
|
||||
sendError(resp);
|
||||
}
|
||||
|
||||
public void sendError(RPC.ErrorType type, RPC.POSIXErrno errno, String message) {
|
||||
RPC.RPCHeader.ErrorResponse resp = RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(type).setPosixErrno(errno).setErrorMessage(message).build();
|
||||
sendError(resp);
|
||||
}
|
||||
|
||||
public void sendRedirect(String target_uuid) {
|
||||
RPC.RPCHeader.ErrorResponse resp = RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.REDIRECT).setPosixErrno(RPC.POSIXErrno.POSIX_ERROR_NONE).setRedirectToServerUuid(target_uuid).build();
|
||||
sendError(resp);
|
||||
}
|
||||
|
||||
public void sendError(RPC.RPCHeader.ErrorResponse error) {
|
||||
try {
|
||||
RPC.RPCHeader rqHdr = getHeader();
|
||||
RPC.RPCHeader respHdr = RPC.RPCHeader.newBuilder().setCallId(rqHdr.getCallId()).setMessageType(RPC.MessageType.RPC_RESPONSE_ERROR).setErrorResponse(error).build();
|
||||
RPCServerResponse response = new RPCServerResponse(respHdr, null, null);
|
||||
getConnection().getServer().sendResponse(this, response);
|
||||
} catch (IOException ex) {
|
||||
Logging.logError(Logging.LEVEL_ERROR, this, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendResponse(Message message, ReusableBuffer data) throws IOException {
|
||||
RPC.RPCHeader rqHdr = getHeader();
|
||||
RPC.RPCHeader respHdr = RPC.RPCHeader.newBuilder().setCallId(rqHdr.getCallId()).setMessageType(RPC.MessageType.RPC_RESPONSE_SUCCESS).build();
|
||||
RPCServerResponse response = new RPCServerResponse(respHdr, message, data);
|
||||
getConnection().getServer().sendResponse(this, response);
|
||||
}
|
||||
|
||||
public SocketAddress getSenderAddress() {
|
||||
return connection.getSender();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the connection
|
||||
*/
|
||||
public RPCServerConnectionInterface getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
try {
|
||||
RPC.RPCHeader hdr = getHeader();
|
||||
String headerContent = "";
|
||||
if (hdr.getMessageType() == RPC.MessageType.RPC_REQUEST) {
|
||||
headerContent = ",proc="+hdr.getRequestHeader().getProcId()+",interf="+hdr.getRequestHeader().getInterfaceId();
|
||||
}
|
||||
return this.getClass().getCanonicalName()+": callid="+hdr.getCallId()+", type="+hdr.getMessageType()+headerContent;
|
||||
} catch (Exception ex) {
|
||||
return this.getClass().getCanonicalName()+": unparseable data: "+ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public interface RPCServerRequestListener {
|
||||
|
||||
public void receiveRecord(RPCServerRequest rq);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferOutputStream;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.RecordMarker;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCServerResponse {
|
||||
|
||||
final int callId;
|
||||
|
||||
final ReusableBuffer[] buffers;
|
||||
final int hdrLen;
|
||||
final int msgLen;
|
||||
final int dataLen;
|
||||
|
||||
public RPCServerResponse(RPC.RPCHeader header, Message message, ReusableBuffer data) throws IOException {
|
||||
ReusableBufferOutputStream os = new ReusableBufferOutputStream(ReusableBufferOutputStream.BUFF_SIZE);
|
||||
callId = header.getCallId();
|
||||
|
||||
hdrLen = header.getSerializedSize();
|
||||
msgLen = (message != null) ? message.getSerializedSize() : 0;
|
||||
dataLen = (data != null) ? data.capacity() : 0;
|
||||
|
||||
assert(hdrLen > 0);
|
||||
assert(msgLen >= 0);
|
||||
assert(dataLen >= 0);
|
||||
|
||||
RecordMarker rm = new RecordMarker(hdrLen, msgLen, dataLen);
|
||||
rm.writeFragmentHeader(os);
|
||||
header.writeTo(os);
|
||||
if (message != null) {
|
||||
message.writeTo(os);
|
||||
}
|
||||
if (data != null) {
|
||||
data.position(data.limit());
|
||||
os.appendBuffer(data);
|
||||
}
|
||||
os.flip();
|
||||
buffers = os.getBuffers();
|
||||
}
|
||||
|
||||
public ReusableBuffer[] getBuffers() {
|
||||
return buffers;
|
||||
}
|
||||
|
||||
public ByteBuffer[] packBuffers(ByteBuffer recordMarker) {
|
||||
ByteBuffer[] arr = new ByteBuffer[buffers.length];
|
||||
for (int i = 0; i < buffers.length; i++)
|
||||
arr[i] = buffers[i].getBuffer();
|
||||
return arr;
|
||||
}
|
||||
|
||||
public void freeBuffers() {
|
||||
for (int i = 0; i < buffers.length; i++) {
|
||||
BufferPool.free(buffers[i]);
|
||||
buffers[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.getClass().getCanonicalName()+": callid="+callId;
|
||||
}
|
||||
|
||||
public int getRpcMessageSize() {
|
||||
return RecordMarker.HDR_SIZE + hdrLen + dataLen + msgLen;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.xtreemfs.foundation.LifeCycleThread;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.RPCHeader;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.PBRPCDatagramPacket;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.RecordMarker;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferInputStream;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCUDPSocketServer extends LifeCycleThread implements RPCServerInterface {
|
||||
|
||||
public final int port;
|
||||
|
||||
private final DatagramChannel channel;
|
||||
|
||||
private final Selector selector;
|
||||
|
||||
private volatile boolean quit;
|
||||
|
||||
private final LinkedBlockingQueue<UDPMessage> q;
|
||||
|
||||
private final RPCServerRequestListener receiver;
|
||||
|
||||
public static final int MAX_UDP_SIZE = 2048;
|
||||
|
||||
private final AtomicInteger callIdCounter;
|
||||
|
||||
public RPCUDPSocketServer(int port, RPCServerRequestListener receiver) throws IOException {
|
||||
super("UDPComStage");
|
||||
this.port = port;
|
||||
q = new LinkedBlockingQueue<UDPMessage>();
|
||||
this.receiver = receiver;
|
||||
callIdCounter = new AtomicInteger(1);
|
||||
|
||||
selector = Selector.open();
|
||||
|
||||
channel = DatagramChannel.open();
|
||||
channel.socket().setReuseAddress(true);
|
||||
channel.socket().bind(new InetSocketAddress(port));
|
||||
channel.configureBlocking(false);
|
||||
channel.register(selector, SelectionKey.OP_READ);
|
||||
|
||||
if (Logging.isInfo())
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.net, this, "UDP socket on port %d ready", port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendResponse(RPCServerRequest request, RPCServerResponse response) {
|
||||
UDPMessage msg = (UDPMessage)request.getConnection();
|
||||
UDPMessage responseMsg = new UDPMessage(response.getBuffers()[0], msg.getAddress(), this);
|
||||
request.freeBuffers();
|
||||
send(responseMsg);
|
||||
}
|
||||
|
||||
public void sendRequest(RPCHeader header, Message message, InetSocketAddress receiver) throws IOException {
|
||||
PBRPCDatagramPacket dpack = new PBRPCDatagramPacket(header, message);
|
||||
header = header.toBuilder().setCallId(callIdCounter.getAndIncrement()).build();
|
||||
UDPMessage msg = new UDPMessage(dpack.assembleDatagramPacket(), receiver, this);
|
||||
//msg.getBuffer().flip();
|
||||
send(msg);
|
||||
}
|
||||
|
||||
private void send(UDPMessage rq) {
|
||||
q.add(rq);
|
||||
|
||||
if (q.size() == 1) {
|
||||
// System.out.println("wakeup!");
|
||||
selector.wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
quit = true;
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
notifyStarted();
|
||||
|
||||
boolean isRdOnly = true;
|
||||
|
||||
while (!quit) {
|
||||
|
||||
if (q.size() == 0) {
|
||||
if (!isRdOnly) {
|
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ);
|
||||
// System.out.println("read only");
|
||||
isRdOnly = true;
|
||||
}
|
||||
} else {
|
||||
if (isRdOnly) {
|
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
|
||||
// System.out.println("read write");
|
||||
isRdOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
int numKeys = selector.select();
|
||||
|
||||
if (q.size() == 0) {
|
||||
if (!isRdOnly) {
|
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ);
|
||||
// System.out.println("read only");
|
||||
isRdOnly = true;
|
||||
}
|
||||
} else {
|
||||
if (isRdOnly) {
|
||||
channel.keyFor(selector).interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
|
||||
// System.out.println("read write");
|
||||
isRdOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (numKeys == 0)
|
||||
continue;
|
||||
|
||||
if (q.size() > 10000) {
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this, "QS!!!!! %d", q.size());
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this, "is readOnly: " + isRdOnly);
|
||||
}
|
||||
|
||||
// fetch events
|
||||
Set<SelectionKey> keys = selector.selectedKeys();
|
||||
Iterator<SelectionKey> iter = keys.iterator();
|
||||
|
||||
// process all events
|
||||
while (iter.hasNext()) {
|
||||
|
||||
SelectionKey key = iter.next();
|
||||
|
||||
// remove key from the list
|
||||
iter.remove();
|
||||
|
||||
if (key.isReadable()) {
|
||||
InetSocketAddress sender = null;
|
||||
// do {
|
||||
ReusableBuffer data = BufferPool.allocate(MAX_UDP_SIZE);
|
||||
sender = (InetSocketAddress) channel.receive(data.getBuffer());
|
||||
if (sender == null || !data.hasRemaining()) {
|
||||
BufferPool.free(data);
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"read key for empty read/empty packet");
|
||||
} else {
|
||||
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"read data from %s", sender.toString());
|
||||
|
||||
try {
|
||||
data.flip();
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "data: %s",
|
||||
data.toString());
|
||||
RecordMarker rm = new RecordMarker(data.getBuffer());
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "rm: %d/%d data: %d",
|
||||
rm.getRpcHeaderLength(), rm.getMessageLength(), data.limit());
|
||||
ReusableBufferInputStream rbis = new ReusableBufferInputStream(data);
|
||||
|
||||
final int origLimit = data.limit();
|
||||
assert (origLimit == RecordMarker.HDR_SIZE + rm.getRpcHeaderLength()
|
||||
+ rm.getMessageLength());
|
||||
data.limit(RecordMarker.HDR_SIZE + rm.getRpcHeaderLength());
|
||||
|
||||
RPCHeader header = RPCHeader.newBuilder().mergeFrom(rbis).build();
|
||||
|
||||
data.range(RecordMarker.HDR_SIZE + rm.getRpcHeaderLength(), rm.getMessageLength());
|
||||
|
||||
UDPMessage msg = new UDPMessage(null, sender, this);
|
||||
RPCServerRequest rq = new RPCServerRequest(msg, header, data);
|
||||
receiver.receiveRecord(rq);
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
Logging.logMessage(Logging.LEVEL_WARN, Category.net, this,
|
||||
"received invalid UPD message: "+ex);
|
||||
BufferPool.free(data);
|
||||
}
|
||||
}
|
||||
// } while (sender != null);
|
||||
} else if (key.isWritable()) {
|
||||
UDPMessage r = q.poll();
|
||||
while (r != null) {
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this,
|
||||
"sent packet to %s", r.getAddress().toString());
|
||||
int sent = channel.send(r.getBuffer().getBuffer(), r.getAddress());
|
||||
BufferPool.free(r.getBuffer());
|
||||
if (sent == 0) {
|
||||
if (Logging.isDebug())
|
||||
Logging.logMessage(Logging.LEVEL_DEBUG, Category.net, this, "cannot send anymore");
|
||||
q.put(r);
|
||||
break;
|
||||
}
|
||||
r = q.poll();
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("strange key state: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
selector.close();
|
||||
channel.close();
|
||||
|
||||
} catch (CancelledKeyException ex) {
|
||||
// ignore
|
||||
} catch (ClosedByInterruptException ex) {
|
||||
// ignore
|
||||
} catch (IOException ex) {
|
||||
Logging.logError(Logging.LEVEL_ERROR, this, ex);
|
||||
} catch (Throwable th) {
|
||||
notifyCrashed(th);
|
||||
return;
|
||||
}
|
||||
|
||||
notifyStopped();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.server;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.pbrpc.channels.ChannelIO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class UDPMessage implements RPCServerConnectionInterface {
|
||||
private final ReusableBuffer buffer;
|
||||
private final InetSocketAddress address;
|
||||
final RPCUDPSocketServer server;
|
||||
|
||||
public UDPMessage(ReusableBuffer buffer, InetSocketAddress address, RPCUDPSocketServer server) {
|
||||
this.buffer = buffer;
|
||||
this.address = address;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RPCServerInterface getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the buffer
|
||||
*/
|
||||
public ReusableBuffer getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the address
|
||||
*/
|
||||
public InetSocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getSender() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelIO getChannel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.utils;
|
||||
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.ErrorType;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.POSIXErrno;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.RPCHeader.ErrorResponse;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class ErrorUtils {
|
||||
|
||||
public static ErrorResponse getErrorResponse(ErrorType type, POSIXErrno errno, String message, String debug) {
|
||||
return ErrorResponse.newBuilder().setErrorType(type).setPosixErrno(errno).setErrorMessage(message).setDebugInfo(debug).build();
|
||||
}
|
||||
|
||||
public static ErrorResponse getErrorResponse(ErrorType type, POSIXErrno errno, String message, Throwable cause) {
|
||||
return ErrorResponse.newBuilder().setErrorType(type).setPosixErrno(errno).setErrorMessage(message).setDebugInfo(OutputUtils.stackTraceToString(cause)).build();
|
||||
}
|
||||
|
||||
public static ErrorResponse getErrorResponse(ErrorType type, POSIXErrno errno, String message) {
|
||||
return ErrorResponse.newBuilder().setErrorType(type).setPosixErrno(errno).setErrorMessage(message).build();
|
||||
}
|
||||
|
||||
public static ErrorResponse getInternalServerError(Throwable cause) {
|
||||
return ErrorResponse.newBuilder().setErrorType(ErrorType.INTERNAL_SERVER_ERROR).setPosixErrno(POSIXErrno.POSIX_ERROR_EIO).
|
||||
setErrorMessage(cause.toString()).setDebugInfo(OutputUtils.stackTraceToString(cause)).build();
|
||||
}
|
||||
|
||||
public static ErrorResponse getInternalServerError(Throwable cause, String additionalErrorMessage) {
|
||||
return ErrorResponse.newBuilder().setErrorType(ErrorType.INTERNAL_SERVER_ERROR).setPosixErrno(POSIXErrno.POSIX_ERROR_EIO).
|
||||
setErrorMessage(additionalErrorMessage + "; " + cause.toString()).setDebugInfo(OutputUtils.stackTraceToString(cause)).build();
|
||||
}
|
||||
|
||||
public static String formatError(ErrorResponse error) {
|
||||
if (error == null)
|
||||
return "no error";
|
||||
return error.getErrorType()+"/"+error.getPosixErrno()+": "+error.getErrorMessage() +(error.hasDebugInfo() ? ";\n"+error.getDebugInfo() : "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.utils;
|
||||
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.Message;
|
||||
import java.io.IOException;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.RPCHeader;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class PBRPCDatagramPacket {
|
||||
|
||||
private final RPCHeader header;
|
||||
private final Message message;
|
||||
|
||||
public PBRPCDatagramPacket(ReusableBuffer datagramToParse, Message msgPrototype) throws IOException {
|
||||
RecordMarker rm = new RecordMarker(datagramToParse.getBuffer());
|
||||
ReusableBufferInputStream rbis = new ReusableBufferInputStream(datagramToParse);
|
||||
|
||||
final int origLimit = datagramToParse.limit();
|
||||
assert(origLimit == rm.HDR_SIZE+rm.getRpcHeaderLength()+rm.getMessageLength());
|
||||
datagramToParse.limit(rm.HDR_SIZE+rm.getRpcHeaderLength());
|
||||
|
||||
header = RPCHeader.newBuilder().mergeFrom(rbis).build();
|
||||
|
||||
datagramToParse.limit(origLimit);
|
||||
message = msgPrototype.newBuilderForType().mergeFrom(rbis).build();
|
||||
}
|
||||
|
||||
public PBRPCDatagramPacket(RPCHeader header, Message message) {
|
||||
this.header = header;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ReusableBuffer assembleDatagramPacket() throws IOException {
|
||||
|
||||
ReusableBufferOutputStream out = new ReusableBufferOutputStream(RecordMarker.HDR_SIZE+getHeader().getSerializedSize()+getMessage().getSerializedSize());
|
||||
RecordMarker rm = new RecordMarker(getHeader().getSerializedSize(), getMessage().getSerializedSize(), 0);
|
||||
rm.writeFragmentHeader(out);
|
||||
getHeader().writeTo(out);
|
||||
getMessage().writeTo(out);
|
||||
out.flip();
|
||||
ReusableBuffer[] bufs = out.getBuffers();
|
||||
assert(bufs.length == 1);
|
||||
return bufs[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the header
|
||||
*/
|
||||
public RPCHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the message
|
||||
*/
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RecordMarker {
|
||||
|
||||
public static final int HDR_SIZE = Integer.SIZE/8*3;
|
||||
|
||||
private final int rpcHeaderLength;
|
||||
private final int messageLength;
|
||||
private final int dataLength;
|
||||
|
||||
public RecordMarker(ByteBuffer buf) throws IOException {
|
||||
rpcHeaderLength = buf.getInt();
|
||||
messageLength = buf.getInt();
|
||||
dataLength = buf.getInt();
|
||||
}
|
||||
|
||||
public RecordMarker(int rpcHeaderLength, int messageLength, int dataLength) {
|
||||
this.rpcHeaderLength = rpcHeaderLength;
|
||||
this.messageLength = messageLength;
|
||||
this.dataLength = dataLength;
|
||||
}
|
||||
|
||||
public void writeFragmentHeader(ByteBuffer buf) {
|
||||
buf.putInt(getRpcHeaderLength());
|
||||
buf.putInt(getMessageLength());
|
||||
buf.putInt(getDataLength());
|
||||
}
|
||||
|
||||
public void writeFragmentHeader(ReusableBufferOutputStream out) throws IOException {
|
||||
ByteBuffer buf = ByteBuffer.allocate(HDR_SIZE);
|
||||
buf.putInt(getRpcHeaderLength());
|
||||
buf.putInt(getMessageLength());
|
||||
buf.putInt(getDataLength());
|
||||
out.write(buf.array());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rpcHeaderLength
|
||||
*/
|
||||
public int getRpcHeaderLength() {
|
||||
return rpcHeaderLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the messageLength
|
||||
*/
|
||||
public int getMessageLength() {
|
||||
return messageLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the dataLength
|
||||
*/
|
||||
public int getDataLength() {
|
||||
return dataLength;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class ReusableBufferInputStream extends InputStream {
|
||||
|
||||
private final ReusableBuffer data;
|
||||
|
||||
public ReusableBufferInputStream(ReusableBuffer data) {
|
||||
assert(data != null);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (data.hasRemaining())
|
||||
return data.get();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buf, int offset, int length) throws IOException {
|
||||
final int bytesRemaining = data.remaining();
|
||||
if (bytesRemaining == 0)
|
||||
return -1;
|
||||
final int bytesToRead = (bytesRemaining >= length) ? length : bytesRemaining;
|
||||
data.get(buf, offset, bytesToRead);
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.pbrpc.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class ReusableBufferOutputStream extends OutputStream {
|
||||
|
||||
public static final int BUFF_SIZE = 1024*8;
|
||||
|
||||
private final int bufSize;
|
||||
|
||||
private final ReusableBuffer firstBuffer;
|
||||
|
||||
private List<ReusableBuffer> buffers;
|
||||
|
||||
private ReusableBuffer currentBuffer;
|
||||
|
||||
private int length;
|
||||
|
||||
public ReusableBufferOutputStream(int bufSize) {
|
||||
this.bufSize = bufSize;
|
||||
|
||||
firstBuffer = BufferPool.allocate(bufSize);
|
||||
currentBuffer = firstBuffer;
|
||||
length = 0;
|
||||
}
|
||||
|
||||
private ReusableBuffer checkAndGetBuffer(int requiredSpace) {
|
||||
if (currentBuffer.remaining() < requiredSpace) {
|
||||
if (buffers == null)
|
||||
buffers = new ArrayList<ReusableBuffer>(15);
|
||||
final int newBufSize = (bufSize >= requiredSpace) ? bufSize : requiredSpace;
|
||||
final ReusableBuffer buf = BufferPool.allocate(newBufSize);
|
||||
buffers.add(buf);
|
||||
currentBuffer = buf;
|
||||
}
|
||||
return currentBuffer;
|
||||
}
|
||||
|
||||
public void appendBuffer(ReusableBuffer buffer) {
|
||||
currentBuffer = buffer;
|
||||
if (buffers == null)
|
||||
buffers = new ArrayList<ReusableBuffer>(15);
|
||||
buffer.position(buffer.limit());
|
||||
buffers.add(buffer);
|
||||
length += buffer.remaining();
|
||||
}
|
||||
|
||||
public void flip() {
|
||||
firstBuffer.flip();
|
||||
if (buffers != null) {
|
||||
for (ReusableBuffer buffer : buffers) {
|
||||
buffer.flip();
|
||||
}
|
||||
}
|
||||
currentBuffer = firstBuffer;
|
||||
}
|
||||
|
||||
public void freeBuffers() {
|
||||
BufferPool.free(firstBuffer);
|
||||
if (buffers != null) {
|
||||
for (ReusableBuffer buffer : buffers) {
|
||||
BufferPool.free(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
checkAndGetBuffer(1).put((byte)b);
|
||||
length++;
|
||||
}
|
||||
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
checkAndGetBuffer(len).put(b, off, len);
|
||||
length += len;
|
||||
}
|
||||
|
||||
public ReusableBuffer[] getBuffers() {
|
||||
if (buffers == null) {
|
||||
return new ReusableBuffer[]{firstBuffer};
|
||||
} else {
|
||||
ReusableBuffer[] arr = new ReusableBuffer[buffers.size()+1];
|
||||
arr[0] = firstBuffer;
|
||||
for (int i = 1; i <= buffers.size(); i++)
|
||||
arr[i] = buffers.get(i-1);
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
}
|
||||
110
java/foundation/src/org/xtreemfs/foundation/trace/Tracer.java
Normal file
110
java/foundation/src/org/xtreemfs/foundation/trace/Tracer.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.trace;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class Tracer {
|
||||
|
||||
/**
|
||||
* Set this to true to enable trace log file for all requests.
|
||||
*
|
||||
* @attention: MUST BE SET TO FALSE FOR NORMAL OPERATIONS.
|
||||
*/
|
||||
public static final boolean COLLECT_TRACES = false;
|
||||
|
||||
public enum TraceEvent {
|
||||
|
||||
RECEIVED('>'), RESPONSE_SENT('<'), ERROR_SENT('E');
|
||||
|
||||
private final char eventType;
|
||||
|
||||
TraceEvent(char eventType) {
|
||||
this.eventType = eventType;
|
||||
}
|
||||
|
||||
public char getEventType() {
|
||||
return this.eventType;
|
||||
}
|
||||
};
|
||||
|
||||
private static Tracer theInstance;
|
||||
|
||||
private final FileOutputStream fos;
|
||||
|
||||
private Tracer(String traceFileName) throws IOException {
|
||||
theInstance = this;
|
||||
|
||||
fos = new FileOutputStream(traceFileName, true);
|
||||
if (Logging.isInfo())
|
||||
Logging.logMessage(Logging.LEVEL_INFO, Category.misc, this,
|
||||
"TRACING IS ENABLED, THIS WILL CAUSE PERFORMANCE TO BE REDUCED!");
|
||||
fos.write("#requestId;internal rq sequence no;event;component;message\n".getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the tracer.
|
||||
*
|
||||
* @param traceFileName
|
||||
* file name to write trace data to (append mode).
|
||||
* @throws java.io.IOException
|
||||
* if the file cannot be opened
|
||||
*/
|
||||
public static void initialize(String traceFileName) throws IOException {
|
||||
new Tracer(traceFileName);
|
||||
}
|
||||
|
||||
private void writeTraceRecord(String requestId, long intRqSeqNo, TraceEvent event, String component,
|
||||
String message) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
if (requestId != null)
|
||||
sb.append(requestId);
|
||||
|
||||
sb.append(';');
|
||||
sb.append(intRqSeqNo);
|
||||
sb.append(';');
|
||||
sb.append(event.getEventType());
|
||||
sb.append(';');
|
||||
if (component != null)
|
||||
sb.append(component);
|
||||
sb.append(';');
|
||||
if (message != null)
|
||||
sb.append(message);
|
||||
sb.append("\n");
|
||||
try {
|
||||
fos.write(sb.toString().getBytes());
|
||||
} catch (IOException ex) {
|
||||
Logging.logError(Logging.LEVEL_ERROR, this, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void trace(String requestId, long intRqSeqNo, TraceEvent event, String component,
|
||||
String message) {
|
||||
assert (theInstance != null) : "Tracer not initialized";
|
||||
theInstance.writeTraceRecord(requestId, intRqSeqNo, event, component, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalize() {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
137
java/foundation/src/org/xtreemfs/foundation/util/CLIParser.java
Normal file
137
java/foundation/src/org/xtreemfs/foundation/util/CLIParser.java
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CLIParser {
|
||||
|
||||
public static final class CliOption {
|
||||
public enum OPTIONTYPE {
|
||||
NUMBER, STRING, SWITCH, URL, FILE
|
||||
};
|
||||
|
||||
public final OPTIONTYPE optType;
|
||||
|
||||
public Boolean switchValue;
|
||||
|
||||
public String stringValue;
|
||||
|
||||
public Long numValue;
|
||||
|
||||
public PBRPCServiceURL urlValue;
|
||||
|
||||
public File fileValue;
|
||||
|
||||
public String urlDefaultProtocol;
|
||||
|
||||
public int urlDefaultPort;
|
||||
|
||||
public String usageParams;
|
||||
|
||||
public String usageText;
|
||||
|
||||
public CliOption(OPTIONTYPE oType) {
|
||||
this.optType = oType;
|
||||
if (optType == OPTIONTYPE.SWITCH)
|
||||
switchValue = new Boolean(false);
|
||||
}
|
||||
|
||||
public CliOption(OPTIONTYPE oType, String usageText, String usageParams) {
|
||||
this(oType);
|
||||
this.usageText = usageText;
|
||||
this.usageParams = usageParams == null? "": usageParams;
|
||||
}
|
||||
}
|
||||
|
||||
public static void parseCLI(String[] args, Map<String, CliOption> options, List<String> arguments)
|
||||
throws IllegalArgumentException {
|
||||
List<String> argList = Arrays.asList(args);
|
||||
|
||||
Iterator<String> iter = argList.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final String arg = iter.next().trim();
|
||||
if (arg.startsWith("-")) {
|
||||
// option
|
||||
final String optName = arg.substring(1);
|
||||
final CliOption option = options.get(optName);
|
||||
if (option == null) {
|
||||
throw new IllegalArgumentException(arg + " is not a valid option");
|
||||
}
|
||||
switch (option.optType) {
|
||||
case SWITCH: {
|
||||
option.switchValue = true;
|
||||
break;
|
||||
}
|
||||
case STRING: {
|
||||
if (iter.hasNext()) {
|
||||
final String value = iter.next();
|
||||
option.stringValue = value.trim();
|
||||
} else {
|
||||
throw new IllegalArgumentException(arg + " requires a string argument");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NUMBER: {
|
||||
if (iter.hasNext()) {
|
||||
final String value = iter.next();
|
||||
try {
|
||||
option.numValue = Long.valueOf(value.trim());
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException(arg + " requires a integer argument and "
|
||||
+ value + " is not an integer");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(arg + " requires a string argument");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case URL: {
|
||||
if (iter.hasNext()) {
|
||||
final String value = iter.next();
|
||||
try {
|
||||
final PBRPCServiceURL tmp = new PBRPCServiceURL(value,
|
||||
option.urlDefaultProtocol, option.urlDefaultPort);
|
||||
option.urlValue = tmp;
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(arg + " requires a string argument");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FILE: {
|
||||
if (iter.hasNext()) {
|
||||
final String value = iter.next();
|
||||
try {
|
||||
final File tmp = new File(value);
|
||||
option.fileValue = tmp;
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException(arg + " requires <protocol>://<host>:<port>");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(arg + " requires a string argument");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
arguments.add(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
212
java/foundation/src/org/xtreemfs/foundation/util/CLOption.java
Normal file
212
java/foundation/src/org/xtreemfs/foundation/util/CLOption.java
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.util;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public abstract class CLOption {
|
||||
|
||||
protected String shortName;
|
||||
protected String longName;
|
||||
protected String helpText;
|
||||
protected boolean set;
|
||||
|
||||
public CLOption(String shortName, String longName, String helpText) {
|
||||
this.shortName = shortName;
|
||||
this.longName = longName;
|
||||
this.helpText = helpText;
|
||||
if ((shortName == null )&& (longName == null))
|
||||
throw new IllegalArgumentException("must specify either a shortName or a longName or both");
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (shortName != null) {
|
||||
sb.append("-");
|
||||
sb.append(shortName);
|
||||
if (longName != null)
|
||||
sb.append("/");
|
||||
else
|
||||
sb.append(" ");
|
||||
}
|
||||
if (longName != null) {
|
||||
sb.append("--");
|
||||
sb.append(longName);
|
||||
sb.append("=");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getName(boolean useShortName) {
|
||||
if (useShortName)
|
||||
return this.shortName;
|
||||
else
|
||||
return this.longName;
|
||||
}
|
||||
|
||||
public abstract String getHelp();
|
||||
|
||||
/**
|
||||
* called only when the option is present
|
||||
* @param value
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public void parse(String value) throws IllegalArgumentException {
|
||||
set = true;
|
||||
}
|
||||
|
||||
public boolean isSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
public abstract boolean requiresArgument();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getHelp();
|
||||
}
|
||||
|
||||
public static class Switch extends CLOption {
|
||||
|
||||
protected boolean value;
|
||||
|
||||
public Switch(String shortName, String longName, String helpText) {
|
||||
super(shortName,longName,helpText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return getName()+helpText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(String value) throws IllegalArgumentException {
|
||||
super.parse(value);
|
||||
this.value = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresArgument() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringValue extends CLOption {
|
||||
|
||||
protected String value;
|
||||
|
||||
public StringValue(String shortName, String longName, String helpText) {
|
||||
super(shortName,longName,helpText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return getName()+"<string> "+helpText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(String value) throws IllegalArgumentException {
|
||||
super.parse(value);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresArgument() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class IntegerValue extends CLOption {
|
||||
|
||||
protected int value;
|
||||
|
||||
public IntegerValue(String shortName, String longName, String helpText) {
|
||||
super(shortName,longName,helpText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return getName()+"<number> "+helpText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(String value) throws IllegalArgumentException {
|
||||
super.parse(value);
|
||||
try {
|
||||
this.value = Integer.valueOf(value);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException("'"+value+"' is not a valid number");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresArgument() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class URLValue extends CLOption {
|
||||
|
||||
protected PBRPCServiceURL value;
|
||||
|
||||
protected final String defaultSchema;
|
||||
|
||||
protected final int defaultPort;
|
||||
|
||||
public URLValue(String shortName, String longName, String helpText,
|
||||
String defaultSchema, int defaultPort) {
|
||||
super(shortName,longName,helpText);
|
||||
this.defaultSchema = defaultSchema;
|
||||
this.defaultPort = defaultPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return getName()+"[<schema>://]<hostname>[:<port>] "+helpText+
|
||||
" (default schema is "+defaultSchema+", default port is "+defaultPort+")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(String value) throws IllegalArgumentException {
|
||||
super.parse(value);
|
||||
try {
|
||||
this.value = new PBRPCServiceURL(value, defaultSchema, defaultPort);
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new IllegalArgumentException("'"+value+"' is not a valid URL ("+ex.getMessage()+")");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresArgument() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public PBRPCServiceURL getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class CLOptionParser {
|
||||
|
||||
private final String programName;
|
||||
|
||||
private final List<CLOption> options;
|
||||
|
||||
private final List<String> arguments;
|
||||
|
||||
|
||||
public CLOptionParser(String programName) {
|
||||
this.programName = programName;
|
||||
options = new LinkedList<CLOption>();
|
||||
arguments = new LinkedList<String>();
|
||||
}
|
||||
|
||||
public CLOption addOption(CLOption option) {
|
||||
CLOption rv = option;
|
||||
for (CLOption tmp : options) {
|
||||
if (( (tmp.getName(true) == null) || tmp.getName(true).equals(option.getName(true)) ) &&
|
||||
( (tmp.getName(false) == null) || tmp.getName(false).equals(option.getName(false))) ) {
|
||||
rv = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rv == option)
|
||||
options.add(option);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public void printUsage(String arguments) {
|
||||
System.out.println(programName+" [options] "+arguments);
|
||||
printOptionHelp();
|
||||
System.out.println("");
|
||||
}
|
||||
|
||||
public void printOptionHelp() {
|
||||
for (CLOption option : options) {
|
||||
System.out.println("\t"+option.getHelp());
|
||||
}
|
||||
}
|
||||
|
||||
public void parse(String[] args) throws IllegalArgumentException {
|
||||
int position = 0;
|
||||
boolean nextIsValue = false;
|
||||
CLOption currentOption = null;
|
||||
while (position < args.length) {
|
||||
if (nextIsValue) {
|
||||
assert(currentOption != null);
|
||||
currentOption.parse(args[position]);
|
||||
currentOption = null;
|
||||
nextIsValue = false;
|
||||
} else {
|
||||
if (args[position].charAt(0) == '-') {
|
||||
boolean useShortName;
|
||||
String name;
|
||||
String value = null;
|
||||
if (args[position].charAt(1) == '-') {
|
||||
useShortName = false;
|
||||
name = args[position].substring(2);
|
||||
if (name.length() == 0) {
|
||||
throw new IllegalArgumentException("-- is not a valid option");
|
||||
}
|
||||
int posEq = name.indexOf("=");
|
||||
if (posEq >= 0) {
|
||||
value = name.substring(posEq+1);
|
||||
name = name.substring(0, posEq);
|
||||
}
|
||||
} else {
|
||||
useShortName = true;
|
||||
name = args[position].substring(1);
|
||||
if (name.length() == 0) {
|
||||
throw new IllegalArgumentException("- is not a valid option");
|
||||
}
|
||||
}
|
||||
boolean optFound = false;
|
||||
for (CLOption option : options) {
|
||||
final String optName = option.getName(useShortName);
|
||||
if ((optName != null) && optName.equals(name)) {
|
||||
if (option.requiresArgument()) {
|
||||
if (value == null) {
|
||||
currentOption = option;
|
||||
nextIsValue = true;
|
||||
} else {
|
||||
option.parse(value);
|
||||
}
|
||||
} else {
|
||||
option.parse(null);
|
||||
}
|
||||
optFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!optFound)
|
||||
throw new IllegalArgumentException("'"+args[position]+"' is not a valid option");
|
||||
|
||||
} else {
|
||||
arguments.add(args[position]);
|
||||
}
|
||||
}
|
||||
position++;
|
||||
}
|
||||
if (nextIsValue) {
|
||||
throw new IllegalArgumentException("expected value for option '"+currentOption.getName()+"'");
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getArguments() {
|
||||
return this.arguments;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
132
java/foundation/src/org/xtreemfs/foundation/util/FSUtils.java
Normal file
132
java/foundation/src/org/xtreemfs/foundation/util/FSUtils.java
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Bjoern Kolbeck, Jan Stender,
|
||||
* Felix Langner, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class containing helper functions for working with the local file system.
|
||||
*
|
||||
* @author stender
|
||||
*/
|
||||
public class FSUtils {
|
||||
|
||||
/**
|
||||
* Recursively deletes all contents of the given directory.
|
||||
*
|
||||
* @param file
|
||||
* the directory to delete
|
||||
*/
|
||||
public static void delTree(File file) {
|
||||
|
||||
if (!file.exists())
|
||||
return;
|
||||
|
||||
File[] fileList;
|
||||
if ((fileList = file.listFiles())!=null){
|
||||
for (File f : fileList) {
|
||||
if (f.isDirectory())
|
||||
delTree(f);
|
||||
else
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
file.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a whole directory tree to another directory.
|
||||
*
|
||||
* @param srcFile
|
||||
* the source tree
|
||||
* @param trgFile
|
||||
* the target point where to copy the source tree
|
||||
* @throws IOException
|
||||
* if an I/O error occurs
|
||||
*/
|
||||
public static void copyTree(File srcFile, File trgFile) throws IOException {
|
||||
|
||||
if (srcFile.isDirectory()) {
|
||||
|
||||
trgFile.mkdir();
|
||||
for (File file : srcFile.listFiles()) {
|
||||
copyTree(file, new File(trgFile, file.getName()));
|
||||
}
|
||||
} else {
|
||||
|
||||
FileChannel in = null, out = null;
|
||||
try {
|
||||
in = new FileInputStream(srcFile).getChannel();
|
||||
out = new FileOutputStream(trgFile).getChannel();
|
||||
|
||||
in.transferTo(0, in.size(), out);
|
||||
} finally {
|
||||
if (in != null)
|
||||
in.close();
|
||||
if (out != null)
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the free disk space on the partition storing the given directory.
|
||||
*
|
||||
* @param dir
|
||||
* the directory stored in the partition
|
||||
* @return the free disk space
|
||||
*/
|
||||
public static long getFreeSpace(String dir) {
|
||||
return new File(dir).getFreeSpace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available disk space on the partition storing the given directory.
|
||||
*
|
||||
* @param dir
|
||||
* the directory stored in the partition
|
||||
* @return the available disk space (for non-privileged users)
|
||||
*/
|
||||
public static long getUsableSpace(String dir) {
|
||||
return new File(dir).getUsableSpace();
|
||||
}
|
||||
|
||||
public static File[] listRecursively(File rootDir, FileFilter filter) {
|
||||
List<File> list = new ArrayList<File>();
|
||||
listRecursively(rootDir, filter, list);
|
||||
return list.toArray(new File[list.size()]);
|
||||
}
|
||||
|
||||
private static void listRecursively(File rootDir, FileFilter filter, List<File> list) {
|
||||
|
||||
if (!rootDir.exists())
|
||||
return;
|
||||
|
||||
// first, all files in subdirectories
|
||||
File[] nestedDirs = rootDir.listFiles(new FileFilter() {
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.isDirectory();
|
||||
}
|
||||
});
|
||||
|
||||
for (File dir : nestedDirs)
|
||||
listRecursively(dir, filter, list);
|
||||
|
||||
for (File f : rootDir.listFiles(filter))
|
||||
list.add(f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Konrad-Zuse-Zentrum fuer Informationstechnik Berlin
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* Neither the name of the Konrad-Zuse-Zentrum fuer Informationstechnik Berlin
|
||||
* nor the names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* AUTHORS: Bjoern Kolbeck (ZIB)
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class InvalidUsageException extends Exception {
|
||||
private static final long serialVersionUID = -5461559345851901506L;
|
||||
|
||||
public InvalidUsageException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Jan Stender, Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public final class OutputUtils {
|
||||
|
||||
public static final char[] trHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
public static final byte[] fromHex;
|
||||
|
||||
static {
|
||||
fromHex = new byte[128];
|
||||
fromHex['0'] = 0;
|
||||
fromHex['1'] = 1;
|
||||
fromHex['2'] = 2;
|
||||
fromHex['3'] = 3;
|
||||
fromHex['4'] = 4;
|
||||
fromHex['5'] = 5;
|
||||
fromHex['6'] = 6;
|
||||
fromHex['7'] = 7;
|
||||
fromHex['8'] = 8;
|
||||
fromHex['9'] = 9;
|
||||
fromHex['A'] = 10;
|
||||
fromHex['a'] = 10;
|
||||
fromHex['B'] = 11;
|
||||
fromHex['b'] = 11;
|
||||
fromHex['C'] = 12;
|
||||
fromHex['c'] = 12;
|
||||
fromHex['D'] = 13;
|
||||
fromHex['d'] = 13;
|
||||
fromHex['E'] = 14;
|
||||
fromHex['e'] = 14;
|
||||
fromHex['F'] = 15;
|
||||
fromHex['f'] = 15;
|
||||
|
||||
}
|
||||
|
||||
public static final String byteToHexString(byte b) {
|
||||
StringBuilder sb = new StringBuilder(2);
|
||||
sb.append(trHex[((b >> 4) & 0x0F)]);
|
||||
sb.append(trHex[(b & 0x0F)]);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static final String byteArrayToHexString(byte[] array) {
|
||||
StringBuilder sb = new StringBuilder(2 * array.length);
|
||||
for (byte b : array) {
|
||||
sb.append(trHex[((b >> 4) & 0x0F)]);
|
||||
sb.append(trHex[(b & 0x0F)]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static final String byteArrayToFormattedHexString(byte[] array) {
|
||||
return byteArrayToFormattedHexString(array, 0, array.length);
|
||||
}
|
||||
|
||||
public static final String byteArrayToFormattedHexString(byte[] array, int offset, int len) {
|
||||
StringBuilder sb = new StringBuilder(2 * len);
|
||||
for (int i = offset; i < offset + len; i++) {
|
||||
sb.append(trHex[((array[i] >> 4) & 0x0F)]);
|
||||
sb.append(trHex[(array[i] & 0x0F)]);
|
||||
if ((i - offset) % 4 == 3) {
|
||||
if ((i - offset) % 16 == 15)
|
||||
sb.append("\n");
|
||||
else
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static final String stackTraceToString(Throwable th) {
|
||||
|
||||
PrintStream ps = null;
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ps = new PrintStream(out);
|
||||
if (th != null)
|
||||
th.printStackTrace(ps);
|
||||
|
||||
return new String(out.toByteArray());
|
||||
|
||||
} finally {
|
||||
if (ps != null)
|
||||
ps.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String formatBytes(long bytes) {
|
||||
|
||||
double kb = bytes / 1024.0;
|
||||
double mb = bytes / (1024.0 * 1024.0);
|
||||
double gb = bytes / (1024.0 * 1024.0 * 1024.0);
|
||||
double tb = bytes / (1024.0 * 1024.0 * 1024.0 * 1024.0);
|
||||
|
||||
if (tb >= 1.0) {
|
||||
return String.format("%.2f TB", tb);
|
||||
} else if (gb >= 1.0) {
|
||||
return String.format("%.2f GB", gb);
|
||||
} else if (mb >= 1.0) {
|
||||
return String.format("%.2f MB", mb);
|
||||
} else if (kb >= 1.0) {
|
||||
return String.format("%.2f kB", kb);
|
||||
} else {
|
||||
return bytes + " bytes";
|
||||
}
|
||||
}
|
||||
|
||||
public static String escapeToXML(String st) {
|
||||
st = st.replace("&", "&");
|
||||
st = st.replace("'", "'");
|
||||
st = st.replace("<", "<");
|
||||
st = st.replace(">", ">");
|
||||
st = st.replace("\"", """);
|
||||
return st;
|
||||
}
|
||||
|
||||
public static String unescapeFromXML(String st) {
|
||||
st = st.replace("&", "&");
|
||||
st = st.replace("'", "'");
|
||||
st = st.replace("<", "<");
|
||||
st = st.replace(">", ">");
|
||||
st = st.replace(""", "\"");
|
||||
return st;
|
||||
}
|
||||
|
||||
public static String encodeBase64(byte[] bytes) {
|
||||
return new String(Base64.encodeBase64(bytes));
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(String s) throws IOException {
|
||||
return Base64.decodeBase64(s.getBytes());
|
||||
}
|
||||
|
||||
public static byte[] hexStringToByteArray(String hexString) {
|
||||
|
||||
assert (hexString.length() % 2 == 0);
|
||||
byte[] bytes = new byte[hexString.length() / 2];
|
||||
|
||||
for (int i = 0; i < hexString.length(); i += 2) {
|
||||
int b = Integer.parseInt(hexString.substring(i, i + 2), 16);
|
||||
bytes[i / 2] = b >= 128 ? (byte) (b - 256) : (byte) b;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an integer as a hex string to sb starting with the LSB.
|
||||
*
|
||||
* @param sb
|
||||
* @param value
|
||||
*/
|
||||
public static void writeHexInt(final StringBuffer sb, final int value) {
|
||||
sb.append(OutputUtils.trHex[(value & 0x0F)]);
|
||||
sb.append(OutputUtils.trHex[((value >> 4) & 0x0F)]);
|
||||
sb.append(OutputUtils.trHex[((value >> 8) & 0x0F)]);
|
||||
sb.append(OutputUtils.trHex[((value >> 12) & 0x0F)]);
|
||||
sb.append(OutputUtils.trHex[((value >> 16) & 0x0F)]);
|
||||
sb.append(OutputUtils.trHex[((value >> 20) & 0x0F)]);
|
||||
sb.append(OutputUtils.trHex[((value >> 24) & 0x0F)]);
|
||||
sb.append(OutputUtils.trHex[((value >> 28) & 0x0F)]);
|
||||
}
|
||||
|
||||
public static void writeHexLong(final StringBuffer sb, final long value) {
|
||||
OutputUtils.writeHexInt(sb, (int) (value & 0xFFFFFFFF));
|
||||
OutputUtils.writeHexInt(sb, (int) (value >> 32));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an integer from a hex string (starting with the LSB).
|
||||
*
|
||||
* @param str
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
public static int readHexInt(final String str, int position) {
|
||||
int value = OutputUtils.fromHex[str.charAt(position)];
|
||||
value += ((int) OutputUtils.fromHex[str.charAt(position + 1)]) << 4;
|
||||
value += ((int) OutputUtils.fromHex[str.charAt(position + 2)]) << 8;
|
||||
value += ((int) OutputUtils.fromHex[str.charAt(position + 3)]) << 12;
|
||||
value += ((int) OutputUtils.fromHex[str.charAt(position + 4)]) << 16;
|
||||
value += ((int) OutputUtils.fromHex[str.charAt(position + 5)]) << 20;
|
||||
value += ((int) OutputUtils.fromHex[str.charAt(position + 6)]) << 24;
|
||||
value += ((int) OutputUtils.fromHex[str.charAt(position + 7)]) << 28;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static long readHexLong(final String str, int position) {
|
||||
int low = OutputUtils.readHexInt(str, position);
|
||||
int high = OutputUtils.readHexInt(str, position + 8);
|
||||
|
||||
// calculate the value: left-shift the upper 4 bytes by 32 bit and
|
||||
// append the lower 32 bit
|
||||
long value = ((long) high) << 32 | (((long) low) & 4294967295L);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String getThreadDump() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<HTML><BODY><H1>THREAD STATES</H1><PRE>");
|
||||
final Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
|
||||
for (Thread t : traces.keySet()) {
|
||||
sb.append("<B>thread: ");
|
||||
sb.append(t.getName());
|
||||
sb.append("</B>\n");
|
||||
final StackTraceElement[] elems = traces.get(t);
|
||||
for (int i = elems.length - 1; i >= 0; i--) {
|
||||
sb.append(elems[i].toString());
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append("</PRE></BODY></HTML>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats number of seconds to a string consists of number of seconds, minutes, hours or days and the
|
||||
* corresponding entity (e.g. "3 minutes" if seconds is between 180 and 239).
|
||||
*
|
||||
* @param seconds
|
||||
* @return
|
||||
*/
|
||||
public static String SecondsToString(long seconds) {
|
||||
String timeString = null;
|
||||
if (seconds < 60) {
|
||||
// seconds less than one minute
|
||||
timeString = seconds + " seconds";
|
||||
} else if (seconds < 3600) {
|
||||
// seconds less than one hour
|
||||
timeString = (seconds / 60) + " minutes";
|
||||
} else if (seconds < 86400) {
|
||||
// seconds less than one day
|
||||
timeString = (seconds / 3600) + " hours";
|
||||
} else {
|
||||
// seconds equals or longer than one day
|
||||
timeString = (seconds / 86400) + " days";
|
||||
}
|
||||
return timeString;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.util;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class PBRPCServiceURL {
|
||||
|
||||
static {
|
||||
|
||||
urlPattern = Pattern.compile("((pbrpc[gs]?):\\/\\/)?([^:]+)(:([0-9]+))?/?");
|
||||
}
|
||||
|
||||
private static final Pattern urlPattern;
|
||||
|
||||
private final String protocol;
|
||||
|
||||
private final String host;
|
||||
|
||||
private final int port;
|
||||
|
||||
public PBRPCServiceURL(String url, String defaultProtocol, int defaultPort) throws MalformedURLException {
|
||||
|
||||
//parse URL
|
||||
Matcher m = urlPattern.matcher(url);
|
||||
if (m.matches()) {
|
||||
|
||||
if (m.group(2) != null)
|
||||
protocol = m.group(2);
|
||||
else
|
||||
protocol = defaultProtocol;
|
||||
|
||||
host = m.group(3);
|
||||
|
||||
if (m.group(4) != null)
|
||||
port = Integer.valueOf(m.group(4).substring(1));
|
||||
else
|
||||
port = defaultPort;
|
||||
|
||||
} else
|
||||
throw new MalformedURLException("'"+url+"' is not a valid XtreemFS service URL");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the protocol
|
||||
*/
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the host
|
||||
*/
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the port
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return protocol+"://"+host+":"+port;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.util;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.xtreemfs.foundation.SSLOptions;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.logging.Logging.Category;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.Ping;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCNIOSocketServer;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequest;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequestListener;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferInputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class PingServer {
|
||||
|
||||
public static final String CERT_DIR = "../../tests/certs/";
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
try {
|
||||
Logging.start(Logging.LEVEL_DEBUG, Category.all);
|
||||
|
||||
SSLOptions ssl = null;
|
||||
if (true) {
|
||||
ssl = new SSLOptions(new FileInputStream(PingServer.CERT_DIR + "Client.p12"),
|
||||
"passphrase", SSLOptions.PKCS12_CONTAINER, new FileInputStream(PingServer.CERT_DIR + "trusted.jks"),
|
||||
"passphrase", SSLOptions.JKS_CONTAINER, false, true, null, null);
|
||||
}
|
||||
|
||||
RPCNIOSocketServer server = new RPCNIOSocketServer(12345, null, new RPCServerRequestListener() {
|
||||
int cnt = 0;
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
try {
|
||||
ReusableBufferInputStream is = new ReusableBufferInputStream(rq.getMessage());
|
||||
Ping.PingRequest pingRq = Ping.PingRequest.parseFrom(is);
|
||||
|
||||
Ping.PingResponse resp = null;
|
||||
if (pingRq.getSendError()) {
|
||||
resp = Ping.PingResponse.newBuilder().setError(Ping.PingResponse.PingError.newBuilder().setErrorMessage("error message")).build();
|
||||
} else {
|
||||
Ping.PingResponse.PingResult result = Ping.PingResponse.PingResult.newBuilder().setText(pingRq.getText()).build();
|
||||
resp = Ping.PingResponse.newBuilder().setResult(result).build();
|
||||
}
|
||||
ReusableBuffer data = null;
|
||||
if (rq.getData() != null) {
|
||||
data = rq.getData().createViewBuffer();
|
||||
data.limit(data.capacity());
|
||||
data.position(data.capacity());
|
||||
}
|
||||
rq.sendResponse(resp,data);
|
||||
cnt++;
|
||||
if (cnt%1000 == 0) {
|
||||
System.out.println(BufferPool.getStatus());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.GARBAGE_ARGS).setErrorMessage(ex.getMessage()).setDebugInfo(OutputUtils.stackTraceToString(ex)).build());
|
||||
} finally {
|
||||
rq.freeBuffers();
|
||||
}
|
||||
}
|
||||
}, ssl);
|
||||
server.start();
|
||||
server.waitForStartup();
|
||||
System.out.println("PING server running");
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(PingServer.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2014 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.buffer;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests different sequences of {@link BufferPool#allocate(int)} and {@link BufferPool#free(ReusableBuffer)}
|
||||
* operations, some also regarding view buffers.
|
||||
*
|
||||
* Please note that {@link BufferPool} is a singleton and therefore the pool size increases as the number of
|
||||
* run tests does. Therefore, each test has to evaluate changes in the pool size relative to the pool size at
|
||||
* the start of test.
|
||||
*
|
||||
*/
|
||||
public class BufferPoolTest {
|
||||
public static final int TEST_BUFFER_SIZE = 8192;
|
||||
|
||||
@Test
|
||||
public final void testSimpleAllocateAndFree() {
|
||||
ReusableBuffer buf = null;
|
||||
// There may be already a buffer pooled. If not, the pool size will stay 0 after an allocate().
|
||||
int currentPoolSize = Math.max(0, BufferPool.getPoolSize(TEST_BUFFER_SIZE) - 1);
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals("BufferPool must be empty because a buffer was only allocated but not returned so far.",
|
||||
currentPoolSize,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
BufferPool.free(buf);
|
||||
assertEquals("One buffer must have been returned and pooled.", currentPoolSize + 1,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals("Pooled buffer must have been re-allocated.", currentPoolSize,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
BufferPool.free(buf);
|
||||
assertEquals("One buffer must have been returned and pooled again.", currentPoolSize + 1,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testReusableViewBuffers() {
|
||||
ReusableBuffer buf = null;
|
||||
// There may be already a buffer pooled. If not, the pool size will stay 0 after an allocate().
|
||||
int currentPoolSize = Math.max(0, BufferPool.getPoolSize(TEST_BUFFER_SIZE) - 1);
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals(currentPoolSize, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
BufferPool.free(buf);
|
||||
assertEquals(currentPoolSize + 1, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals(currentPoolSize, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
ReusableBuffer viewBuffer = buf.createViewBuffer();
|
||||
|
||||
BufferPool.free(viewBuffer);
|
||||
assertEquals("Buffer not returned to pool yet since one reference is left.", currentPoolSize,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
BufferPool.free(buf);
|
||||
assertEquals("Buffer must have been returned to pool since no reference is left.", currentPoolSize + 1,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testReusableViewBuffersOfReusableViewBuffers() {
|
||||
ReusableBuffer buf = null;
|
||||
// There may be already a buffer pooled. If not, the pool size will stay 0 after an allocate().
|
||||
int currentPoolSize = Math.max(0, BufferPool.getPoolSize(TEST_BUFFER_SIZE) - 1);
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals(currentPoolSize, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
BufferPool.free(buf);
|
||||
assertEquals(currentPoolSize + 1, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals(currentPoolSize, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
ReusableBuffer viewBuffer = buf.createViewBuffer();
|
||||
// Create a view buffer of a view buffer.
|
||||
ReusableBuffer viewBuffer2 = viewBuffer.createViewBuffer();
|
||||
|
||||
BufferPool.free(viewBuffer2);
|
||||
assertEquals("Buffer not returned to pool yet since two references are left.", currentPoolSize,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
BufferPool.free(viewBuffer);
|
||||
assertEquals("Buffer not returned to pool yet since one reference is left.", currentPoolSize,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
BufferPool.free(buf);
|
||||
assertEquals("Buffer must have been returned to pool since no reference is left.", currentPoolSize + 1,
|
||||
BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
private void assertThatAssertionsAreEnabled() {
|
||||
boolean assertOn = false;
|
||||
// *assigns* true if assertions are on.
|
||||
assert assertOn = true;
|
||||
assertTrue("Enable assertions or this test won't work correctly.", assertOn);
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public final void testDoubleFreeThrows() {
|
||||
assertThatAssertionsAreEnabled();
|
||||
|
||||
ReusableBuffer buf = null;
|
||||
// There may be already a buffer pooled. If not, the pool size will stay 0 after an allocate().
|
||||
int currentPoolSize = Math.max(0, BufferPool.getPoolSize(TEST_BUFFER_SIZE) - 1);
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals(currentPoolSize, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
BufferPool.free(buf);
|
||||
assertEquals(currentPoolSize + 1, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
// Double free will trigger assertion.
|
||||
BufferPool.free(buf);
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public final void testDoubleFreeOfRecursiveViewBuffersThrows() {
|
||||
assertThatAssertionsAreEnabled();
|
||||
|
||||
ReusableBuffer buf = null;
|
||||
// There may be already a buffer pooled. If not, the pool size will stay 0 after an allocate().
|
||||
int currentPoolSize = Math.max(0, BufferPool.getPoolSize(TEST_BUFFER_SIZE) - 1);
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals(currentPoolSize, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
BufferPool.free(buf);
|
||||
assertEquals(currentPoolSize + 1, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
|
||||
buf = BufferPool.allocate(TEST_BUFFER_SIZE);
|
||||
assertEquals(currentPoolSize, BufferPool.getPoolSize(TEST_BUFFER_SIZE));
|
||||
ReusableBuffer viewBuffer = buf.createViewBuffer();
|
||||
// Create a view buffer of a view buffer.
|
||||
ReusableBuffer viewBuffer2 = viewBuffer.createViewBuffer();
|
||||
|
||||
BufferPool.free(viewBuffer2);
|
||||
BufferPool.free(viewBuffer);
|
||||
BufferPool.free(buf);
|
||||
|
||||
// Double free will trigger assertion.
|
||||
BufferPool.free(viewBuffer2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by Bjoern Kolbeck, Zuse Institute Berlin
|
||||
* 2014 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.foundation.buffer;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ReusableBufferTest {
|
||||
|
||||
@Test
|
||||
public final void testArray() {
|
||||
ReusableBuffer rb = ReusableBuffer.wrap("Yagga Yagga".getBytes());
|
||||
ReusableBuffer vb = rb.createViewBuffer();
|
||||
|
||||
vb.position(0);
|
||||
vb.limit(5);
|
||||
String result = new String(vb.array());
|
||||
|
||||
assertEquals("Yagga", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testRecursiveNonResuableViewBuffer() {
|
||||
ReusableBuffer rb = ReusableBuffer.wrap("Yagga Yagga".getBytes());
|
||||
assertFalse(rb.isReusable());
|
||||
|
||||
ReusableBuffer viewBuffer = rb.createViewBuffer();
|
||||
assertFalse(viewBuffer.isReusable());
|
||||
|
||||
ReusableBuffer viewBuffer2 = viewBuffer.createViewBuffer();
|
||||
assertFalse(viewBuffer2.isReusable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testNonResuableViewBufferHasIndependentPositions() {
|
||||
ReusableBuffer rb = ReusableBuffer.wrap("Yagga Yagga".getBytes());
|
||||
assertFalse(rb.isReusable());
|
||||
rb.position(0);
|
||||
rb.limit(5);
|
||||
|
||||
ReusableBuffer viewBuffer = rb.createViewBuffer();
|
||||
assertFalse(viewBuffer.isReusable());
|
||||
viewBuffer.position(1);
|
||||
viewBuffer.limit(4);
|
||||
|
||||
assertEquals(0, rb.position());
|
||||
assertEquals(5, rb.limit());
|
||||
assertEquals(1, viewBuffer.position());
|
||||
assertEquals(4, viewBuffer.limit());
|
||||
|
||||
ReusableBuffer viewBufferOfViewBuffer = viewBuffer.createViewBuffer();
|
||||
assertFalse(viewBufferOfViewBuffer.isReusable());
|
||||
viewBufferOfViewBuffer.position(2);
|
||||
viewBufferOfViewBuffer.limit(3);
|
||||
|
||||
assertEquals(0, rb.position());
|
||||
assertEquals(5, rb.limit());
|
||||
assertEquals(1, viewBuffer.position());
|
||||
assertEquals(4, viewBuffer.limit());
|
||||
assertEquals(2, viewBufferOfViewBuffer.position());
|
||||
assertEquals(3, viewBufferOfViewBuffer.limit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testRange() {
|
||||
// Test with a view buffer and with a reusable buffer.
|
||||
ReusableBuffer[] buffers = new ReusableBuffer[] { ReusableBuffer.wrap("Yagga Yagga".getBytes()),
|
||||
BufferPool.allocate(12) };
|
||||
for (ReusableBuffer buf : buffers) {
|
||||
buf.range(1, 4);
|
||||
assertEquals(0, buf.position());
|
||||
assertEquals(4, buf.limit());
|
||||
assertEquals(4, buf.capacity());
|
||||
assertEquals(4, buf.remaining());
|
||||
|
||||
// Now create a view buffer which will have a different range.
|
||||
ReusableBuffer viewBuf = buf.createViewBuffer();
|
||||
viewBuf.range(2, 2);
|
||||
assertEquals(0, viewBuf.position());
|
||||
assertEquals(2, viewBuf.limit());
|
||||
assertEquals(2, viewBuf.capacity());
|
||||
assertEquals(2, viewBuf.remaining());
|
||||
|
||||
// The range of the original buffer was not affected.
|
||||
assertEquals(0, buf.position());
|
||||
assertEquals(4, buf.limit());
|
||||
assertEquals(4, buf.capacity());
|
||||
assertEquals(4, buf.remaining());
|
||||
|
||||
if (buf.isReusable()) {
|
||||
BufferPool.free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testShrink() {
|
||||
// Test with a view buffer and with a reusable buffer.
|
||||
ReusableBuffer[] buffers = new ReusableBuffer[] { ReusableBuffer.wrap("Yagga Yagga".getBytes()),
|
||||
BufferPool.allocate(12) };
|
||||
for (ReusableBuffer buf : buffers) {
|
||||
buf.shrink(4);
|
||||
assertEquals(0, buf.position());
|
||||
assertEquals(4, buf.limit());
|
||||
assertEquals(4, buf.capacity());
|
||||
assertEquals(4, buf.remaining());
|
||||
|
||||
// Now create a view buffer which will have a different range.
|
||||
ReusableBuffer viewBuf = buf.createViewBuffer();
|
||||
viewBuf.shrink(2);
|
||||
assertEquals(0, viewBuf.position());
|
||||
assertEquals(2, viewBuf.limit());
|
||||
assertEquals(2, viewBuf.capacity());
|
||||
assertEquals(2, viewBuf.remaining());
|
||||
|
||||
// The range of the original buffer was not affected.
|
||||
assertEquals(0, buf.position());
|
||||
assertEquals(4, buf.limit());
|
||||
assertEquals(4, buf.capacity());
|
||||
assertEquals(4, buf.remaining());
|
||||
|
||||
if (buf.isReusable()) {
|
||||
BufferPool.free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testEnlarge() {
|
||||
// Test with a view buffer and with a reusable buffer.
|
||||
ReusableBuffer[] buffers = new ReusableBuffer[] { ReusableBuffer.wrap("Yagga Yagga".getBytes()),
|
||||
BufferPool.allocate(11) };
|
||||
for (ReusableBuffer buf : buffers) {
|
||||
int originalBufSize = buf.capacity();
|
||||
|
||||
// When there is no underlying, larger buffer, enlarge won't have an effect (the capacity of the
|
||||
// underlying buffer can be higher than the current buffer size.)
|
||||
assertFalse(buf.enlarge(buf.capacityUnderlying() + 1));
|
||||
// Enlarging to the current size should always work.
|
||||
assertTrue(buf.enlarge(originalBufSize));
|
||||
assertEquals(originalBufSize, buf.capacity());
|
||||
|
||||
// Create a view buffer first which is smaller than the original one.
|
||||
buf.limit(originalBufSize / 2);
|
||||
ReusableBuffer smallerBuf = buf.createViewBuffer();
|
||||
|
||||
// Enlarge it, but not larger than the underlying buffer.
|
||||
int newBufSize = smallerBuf.capacity() * 2;
|
||||
assertTrue(smallerBuf.enlarge(newBufSize));
|
||||
assertEquals(0, buf.position());
|
||||
assertEquals(newBufSize, smallerBuf.limit());
|
||||
assertEquals(newBufSize, smallerBuf.capacity());
|
||||
assertEquals(newBufSize, smallerBuf.remaining());
|
||||
|
||||
// You cannot enlarge it over the capacity of the original buffer.
|
||||
assertFalse(smallerBuf.enlarge(buf.capacityUnderlying() + 1));
|
||||
|
||||
// The range of the original buffer was not affected.
|
||||
assertEquals(0, buf.position());
|
||||
assertEquals(originalBufSize / 2, buf.limit());
|
||||
assertEquals(originalBufSize, buf.capacity());
|
||||
assertEquals(originalBufSize / 2, buf.remaining());
|
||||
|
||||
if (buf.isReusable()) {
|
||||
BufferPool.free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.test.foundation.checksums;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.Adler32;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.xtreemfs.foundation.checksums.ChecksumAlgorithm;
|
||||
import org.xtreemfs.foundation.checksums.ChecksumFactory;
|
||||
import org.xtreemfs.foundation.checksums.ChecksumProvider;
|
||||
import org.xtreemfs.foundation.checksums.provider.JavaChecksumProvider;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
|
||||
/**
|
||||
* tests the checksum factory and some checksums
|
||||
*
|
||||
* 19.08.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class ChecksumFactoryTest {
|
||||
private ChecksumFactory factory;
|
||||
private ByteBuffer data;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Logging.start(Logging.LEVEL_ERROR);
|
||||
|
||||
this.factory = ChecksumFactory.getInstance();
|
||||
|
||||
ChecksumProvider provider = new JavaChecksumProvider();
|
||||
this.factory.addProvider(provider);
|
||||
|
||||
this.data = ByteBuffer.wrap(generateRandomBytes(1024 * 128));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* generates randomly filled byte-array
|
||||
*
|
||||
* @param length
|
||||
* of the byte-array
|
||||
*/
|
||||
public static byte[] generateRandomBytes(int length) {
|
||||
Random r = new Random();
|
||||
byte[] bytes = new byte[length];
|
||||
|
||||
r.nextBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* tests the internal java checksum algorithms
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testJavaChecksumAlgorithm() throws Exception {
|
||||
// compute checksum with xtreemfs ChecksumFactory
|
||||
long xtreemfsValue = computeXtreemfsChecksum("Adler32", true);
|
||||
|
||||
// compute checksum with java API
|
||||
Checksum javaAlgorithm = new Adler32();
|
||||
javaAlgorithm.update(data.array(), 0, data.array().length);
|
||||
long javaValue = javaAlgorithm.getValue();
|
||||
|
||||
// System.out.println(javaValue);
|
||||
// System.out.println(xtreemfsValue);
|
||||
|
||||
assertEquals(javaValue, xtreemfsValue);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * tests the internal java message digest algorithms
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public void testJavaMessageDigestAlgorithm() throws Exception {
|
||||
// // compute checksum with xtreemfs ChecksumFactory
|
||||
// String xtreemfsValue = computeXtreemfsChecksum("MD5", true);
|
||||
//
|
||||
// // compute checksum with java API
|
||||
// String javaValue = computeJavaMessageDigest("MD5");
|
||||
//
|
||||
// // System.out.println("java: "+xtreemfsValue);
|
||||
// // System.out.println("xtreemfs: "+javaValue.toString());
|
||||
//
|
||||
// assertEquals(javaValue.toString(), xtreemfsValue);
|
||||
// }
|
||||
|
||||
/**
|
||||
* @param algorithm
|
||||
* @param returnAlgorithm
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
private long computeXtreemfsChecksum(String algorithm, boolean returnAlgorithm) throws NoSuchAlgorithmException {
|
||||
// compute checksum with xtreemfs ChecksumFactory
|
||||
ChecksumAlgorithm xtreemfsAlgorithm = factory.getAlgorithm(algorithm);
|
||||
xtreemfsAlgorithm.update(data);
|
||||
long xtreemfsValue = xtreemfsAlgorithm.getValue();
|
||||
if (returnAlgorithm)
|
||||
this.factory.returnAlgorithm(xtreemfsAlgorithm);
|
||||
return xtreemfsValue;
|
||||
}
|
||||
|
||||
private long computeJavaCheckSum(String algorithm) throws NoSuchAlgorithmException {
|
||||
// compute checksum with java API
|
||||
Adler32 adler = new Adler32();
|
||||
adler.update(data.array());
|
||||
return adler.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* tests, if the internal buffer of the checksums is working correctly, if the checksum is used more than once
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testIfChecksumIsAlwaysTheSame() throws Exception {
|
||||
ChecksumAlgorithm algorithm = factory.getAlgorithm("Adler32");
|
||||
algorithm.update(data);
|
||||
long oldValue = algorithm.getValue();
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
algorithm.update(data);
|
||||
long newValue = algorithm.getValue();
|
||||
|
||||
assertEquals(oldValue, newValue);
|
||||
oldValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tests, if the ChecksumFactory delivers only "thread-safe" instances (cache-pool)
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testThreadSafety() throws Exception {
|
||||
final int THREADS = 8;
|
||||
this.data = ByteBuffer.wrap(generateRandomBytes(1024 * 1024 * 32));
|
||||
|
||||
// compute correct checksum with java API
|
||||
Long javaValue = computeJavaCheckSum("Adler32");
|
||||
|
||||
Callable<Long> computation = new Callable<Long>() {
|
||||
@Override
|
||||
public Long call() {
|
||||
try {
|
||||
// compute checksum with xtreemfs ChecksumFactory
|
||||
long xtreemfsValue = computeXtreemfsChecksum("Adler32", true);
|
||||
return xtreemfsValue;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return 0l;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
};
|
||||
LinkedList<Future<Long>> results = useMultipleThreads(THREADS, computation);
|
||||
|
||||
// compare correct java checksum with xtreemfs checksums
|
||||
for (Future<Long> result : results) {
|
||||
assertEquals(javaValue, result.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tests, if the ChecksumFactory cache-pool works correctly
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testChecksumFactoryCache() throws Exception {
|
||||
// FIXME: use bigger values for more comprehensive testing, but this will slow down the test
|
||||
final int THREADS = 8;
|
||||
final int ROUNDS = 50;
|
||||
this.data = ByteBuffer.wrap(generateRandomBytes(1024 * 1024));
|
||||
|
||||
// compute correct checksum with java API
|
||||
Long javaValue = computeJavaCheckSum("Adler32");
|
||||
|
||||
Callable<LinkedList<Long>> computation = new Callable<LinkedList<Long>>() {
|
||||
@Override
|
||||
public LinkedList<Long> call() {
|
||||
try {
|
||||
LinkedList<Long> values = new LinkedList<Long>();
|
||||
boolean returning = false;
|
||||
for (int i = 0; i < ROUNDS; i++) {
|
||||
// compute checksum with xtreemfs ChecksumFactory
|
||||
long xtreemfsValue = computeXtreemfsChecksum("Adler32", returning);
|
||||
values.add(xtreemfsValue);
|
||||
returning = !returning;
|
||||
}
|
||||
return values;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
LinkedList<Future<LinkedList<Long>>> results = useMultipleThreads(THREADS, computation);
|
||||
|
||||
// compare correct java checksum with xtreemfs checksums
|
||||
for (Future<LinkedList<Long>> result : results) {
|
||||
for (Long value : result.get()) {
|
||||
assertEquals(javaValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* executes a given computation in a couple of threads and returns the results of the computations
|
||||
*
|
||||
* @param THREADS
|
||||
* @param computation
|
||||
* @return a list of futures, which contain the results of the computations
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private <E> LinkedList<Future<E>> useMultipleThreads(final int THREADS, Callable<E> computation)
|
||||
throws InterruptedException {
|
||||
LinkedList<Future<E>> results = new LinkedList<Future<E>>();
|
||||
// compute xtreemfs checksums with multiple threads
|
||||
ExecutorService executor = Executors.newFixedThreadPool(THREADS);
|
||||
for (int i = 0; i < THREADS; i++) {
|
||||
Future<E> tmp = executor.submit(computation);
|
||||
results.add(tmp);
|
||||
}
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(60, TimeUnit.SECONDS);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010 by Christian Lorenz,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.test.foundation.checksums;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.xtreemfs.foundation.checksums.StringChecksumAlgorithm;
|
||||
import org.xtreemfs.foundation.checksums.algorithms.SDBM;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
|
||||
/**
|
||||
* some tests for the checksum algorithms, which are based on strings
|
||||
*
|
||||
* 02.09.2008
|
||||
*
|
||||
* @author clorenz
|
||||
*/
|
||||
public class StringChecksumAlgorithmTest {
|
||||
private ByteBuffer bufferData;
|
||||
private String stringData;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Logging.start(Logging.LEVEL_ERROR);
|
||||
|
||||
this.stringData = "";
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
this.stringData += "Test, ";
|
||||
}
|
||||
this.bufferData = ByteBuffer.wrap(stringData.getBytes());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* tests, if the SDBM algorithm generates the same checksum with a String-input and ByteBuffer-input
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testSDBMStringBufferEquality() throws Exception {
|
||||
// compute checksum with xtreemfs ChecksumFactory
|
||||
StringChecksumAlgorithm algorithm = new SDBM();
|
||||
|
||||
// string
|
||||
algorithm.digest(stringData);
|
||||
long stringValue = algorithm.getValue();
|
||||
|
||||
// buffer
|
||||
algorithm.update(bufferData);
|
||||
long bufferValue = algorithm.getValue();
|
||||
|
||||
// System.out.println(stringValue);
|
||||
// System.out.println(bufferValue);
|
||||
|
||||
assertEquals(stringValue, bufferValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.test.foundation.pbrpc;
|
||||
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.junit.Test;
|
||||
import org.xtreemfs.foundation.pbrpc.Schemes;
|
||||
import java.net.InetSocketAddress;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCAuthentication;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCNIOSocketClient;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCResponse;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.Ping.PingResponse;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.PingServiceClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.xtreemfs.foundation.SSLOptions;
|
||||
import org.xtreemfs.foundation.TimeSync;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.pbrpc.client.PBRPCException;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.Ping;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.Ping.PingRequest;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCNIOSocketServer;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequest;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequestListener;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferInputStream;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class PBRPCClientServerTest {
|
||||
private final int TEST_PORT = 12999;
|
||||
|
||||
private static final String[] schemes = new String[]{Schemes.SCHEME_PBRPC, Schemes.SCHEME_PBRPCS, Schemes.SCHEME_PBRPCG};
|
||||
|
||||
private static TimeSync ts = null;
|
||||
|
||||
public PBRPCClientServerTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
Logging.start(Logging.LEVEL_WARN, Logging.Category.all);
|
||||
ts = TimeSync.initializeLocal(50);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
ts.close();
|
||||
}
|
||||
|
||||
// TODO add test methods here.
|
||||
// The methods must be annotated with annotation @Test. For example:
|
||||
//
|
||||
// @Test
|
||||
// public void hello() {}\
|
||||
@Test
|
||||
public void testRegularRPC() throws Exception {
|
||||
ResponseCreator creator = new ResponseCreator() {
|
||||
@Override
|
||||
public void answer(RPCServerRequest rq, PingRequest pRq) throws Exception {
|
||||
Ping.PingResponse.PingResult result = Ping.PingResponse.PingResult.newBuilder().setText(pRq.getText()).build();
|
||||
Ping.PingResponse resp = Ping.PingResponse.newBuilder().setResult(result).build();
|
||||
|
||||
rq.sendResponse(resp, null);
|
||||
}
|
||||
};
|
||||
|
||||
TestExecutor exec = new TestExecutor() {
|
||||
|
||||
@Override
|
||||
public void execTest(RPCNIOSocketClient client) throws Exception {
|
||||
PingServiceClient psClient = new PingServiceClient(client,null);
|
||||
|
||||
RPC.UserCredentials userCred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("tester").build();
|
||||
RPCResponse<PingResponse> response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Hello World!", false, null);
|
||||
assertEquals(response.get().getResult().getText(),"Hello World!");
|
||||
response.freeBuffers();
|
||||
|
||||
response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Murpel", false, null);
|
||||
assertEquals(response.get().getResult().getText(),"Murpel");
|
||||
response.freeBuffers();
|
||||
}
|
||||
};
|
||||
for (String scheme: schemes)
|
||||
runTest(scheme, creator, exec);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testErrorResponse() throws Exception {
|
||||
ResponseCreator creator = new ResponseCreator() {
|
||||
@Override
|
||||
public void answer(RPCServerRequest rq, PingRequest pRq) throws Exception {
|
||||
rq.sendError(RPC.ErrorType.ERRNO, RPC.POSIXErrno.POSIX_ERROR_EIO, "YaggYagga");
|
||||
}
|
||||
};
|
||||
|
||||
TestExecutor exec = new TestExecutor() {
|
||||
|
||||
@Override
|
||||
public void execTest(RPCNIOSocketClient client) throws Exception {
|
||||
PingServiceClient psClient = new PingServiceClient(client,null);
|
||||
|
||||
RPC.UserCredentials userCred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("tester").build();
|
||||
RPCResponse<PingResponse> response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Hello World!", false, null);
|
||||
|
||||
try {
|
||||
response.get();
|
||||
fail("expected error response");
|
||||
} catch (PBRPCException ex) {
|
||||
assertEquals(ex.getErrorType(),RPC.ErrorType.ERRNO);
|
||||
assertEquals(ex.getPOSIXErrno(),RPC.POSIXErrno.POSIX_ERROR_EIO);
|
||||
}
|
||||
response.freeBuffers();
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
for (String scheme: schemes)
|
||||
runTest(scheme, creator, exec);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalServerError() throws Exception {
|
||||
ResponseCreator creator = new ResponseCreator() {
|
||||
@Override
|
||||
public void answer(RPCServerRequest rq, PingRequest pRq) throws Exception {
|
||||
rq.sendRedirect("redirtome");
|
||||
}
|
||||
};
|
||||
|
||||
TestExecutor exec = new TestExecutor() {
|
||||
|
||||
@Override
|
||||
public void execTest(RPCNIOSocketClient client) throws Exception {
|
||||
PingServiceClient psClient = new PingServiceClient(client,null);
|
||||
|
||||
RPC.UserCredentials userCred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("tester").build();
|
||||
RPCResponse<PingResponse> response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Hello World!", false, null);
|
||||
|
||||
try {
|
||||
response.get();
|
||||
fail("expected error response");
|
||||
} catch (PBRPCException ex) {
|
||||
assertEquals(ex.getErrorType(),RPC.ErrorType.REDIRECT);
|
||||
assertEquals(ex.getRedirectToServerUUID(),"redirtome");
|
||||
}
|
||||
response.freeBuffers();
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
for (String scheme: schemes)
|
||||
runTest(scheme, creator, exec);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataPing() throws Exception {
|
||||
ResponseCreator creator = new ResponseCreator() {
|
||||
@Override
|
||||
public void answer(RPCServerRequest rq, PingRequest pRq) throws Exception {
|
||||
Ping.PingResponse.PingResult result = Ping.PingResponse.PingResult.newBuilder().setText(pRq.getText()).build();
|
||||
Ping.PingResponse resp = Ping.PingResponse.newBuilder().setResult(result).build();
|
||||
|
||||
ReusableBuffer data = null;
|
||||
if (rq.getData() != null) {
|
||||
data = rq.getData().createViewBuffer();
|
||||
data.limit(data.capacity());
|
||||
data.position(data.capacity());
|
||||
}
|
||||
|
||||
rq.sendResponse(resp, data);
|
||||
}
|
||||
};
|
||||
|
||||
TestExecutor exec = new TestExecutor() {
|
||||
|
||||
@Override
|
||||
public void execTest(RPCNIOSocketClient client) throws Exception {
|
||||
PingServiceClient psClient = new PingServiceClient(client,null);
|
||||
byte[] arr = new byte[2065];
|
||||
for (int i= 0; i < arr.length; i++)
|
||||
arr[i] = 'x';
|
||||
ReusableBuffer sendData = ReusableBuffer.wrap(arr);
|
||||
// System.out.println("data: "+sendData);
|
||||
|
||||
RPC.UserCredentials userCred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("tester").build();
|
||||
RPCResponse<PingResponse> response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Hello World!", false, sendData);
|
||||
assertEquals(response.get().getResult().getText(),"Hello World!");
|
||||
|
||||
ReusableBuffer recdata = response.getData();
|
||||
assertTrue(recdata.hasRemaining());
|
||||
while (recdata.hasRemaining()) {
|
||||
assertEquals(recdata.get(),(byte)'x');
|
||||
}
|
||||
response.freeBuffers();
|
||||
|
||||
}
|
||||
};
|
||||
for (String scheme: schemes)
|
||||
runTest(scheme, creator, exec);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeout() throws Exception {
|
||||
ResponseCreator creator = new ResponseCreator() {
|
||||
@Override
|
||||
public void answer(RPCServerRequest rq, PingRequest pRq) throws Exception {
|
||||
//don't do anything
|
||||
}
|
||||
};
|
||||
|
||||
TestExecutor exec = new TestExecutor() {
|
||||
|
||||
@Override
|
||||
public void execTest(RPCNIOSocketClient client) throws Exception {
|
||||
PingServiceClient psClient = new PingServiceClient(client,null);
|
||||
|
||||
RPC.UserCredentials userCred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("tester").build();
|
||||
RPCResponse<PingResponse> response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Hello World!", false, null);
|
||||
|
||||
try {
|
||||
response.get();
|
||||
fail("expected error response");
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
response.freeBuffers();
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
for (String scheme: schemes)
|
||||
runTest(scheme, creator, exec);
|
||||
|
||||
}
|
||||
|
||||
public void runTest(String pbrpcScheme, ResponseCreator creator, TestExecutor exec) throws Exception {
|
||||
RPCNIOSocketClient client = null;
|
||||
RPCNIOSocketServer server = null;
|
||||
|
||||
// System.out.println("loading ssl context");
|
||||
|
||||
SSLOptions srvSSL = null;
|
||||
SSLOptions clientSSL = null;
|
||||
if (pbrpcScheme.equals(Schemes.SCHEME_PBRPCS) || pbrpcScheme.equals(Schemes.SCHEME_PBRPCG)) {
|
||||
srvSSL = createSSLOptions("DIR.p12", "passphrase", SSLOptions.PKCS12_CONTAINER,
|
||||
"trusted.jks", "passphrase", SSLOptions.JKS_CONTAINER, pbrpcScheme.equals(Schemes.SCHEME_PBRPCG), null);
|
||||
|
||||
clientSSL= createSSLOptions("Client.p12", "passphrase",
|
||||
SSLOptions.PKCS12_CONTAINER, "trusted.jks", "passphrase", SSLOptions.JKS_CONTAINER, pbrpcScheme.equals(Schemes.SCHEME_PBRPCG), null);
|
||||
}
|
||||
|
||||
// System.out.println("setup done");
|
||||
|
||||
try {
|
||||
|
||||
server = getServer(creator,srvSSL);
|
||||
|
||||
server.start();
|
||||
server.waitForStartup();
|
||||
|
||||
client = new RPCNIOSocketClient(clientSSL, 5000, 5*60*1000, "runTest");
|
||||
client.start();
|
||||
client.waitForStartup();
|
||||
|
||||
exec.execTest(client);
|
||||
|
||||
} finally {
|
||||
//clean up
|
||||
if (client != null) {
|
||||
client.shutdown();
|
||||
client.waitForShutdown();
|
||||
}
|
||||
if (server != null) {
|
||||
server.shutdown();
|
||||
server.waitForShutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RPCNIOSocketServer getServer(final ResponseCreator creator, SSLOptions sslOpt) throws IOException {
|
||||
return new RPCNIOSocketServer(TEST_PORT, null, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("received request");
|
||||
try {
|
||||
ReusableBufferInputStream is = new ReusableBufferInputStream(rq.getMessage());
|
||||
Ping.PingRequest pingRq = Ping.PingRequest.parseFrom(is);
|
||||
|
||||
creator.answer(rq, pingRq);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.GARBAGE_ARGS).setErrorMessage(ex.getMessage()).setDebugInfo(OutputUtils.stackTraceToString(ex)).build());
|
||||
fail(ex.toString());
|
||||
|
||||
}
|
||||
}
|
||||
}, sslOpt);
|
||||
}
|
||||
|
||||
private SSLOptions createSSLOptions(String keyStoreName, String ksPassphrase,
|
||||
String ksContainerType, String trustStoreName, String tsPassphrase, String tsContainerType, boolean gridSSL, String sslProtocolString)
|
||||
throws IOException {
|
||||
|
||||
ClassLoader cl = this.getClass().getClassLoader();
|
||||
|
||||
InputStream ks = cl.getResourceAsStream(keyStoreName);
|
||||
if (ks == null) {
|
||||
// Assume the working directory is "java/servers".
|
||||
String testCert = "../../tests/certs/" + keyStoreName;
|
||||
if (new File(testCert).isFile()) {
|
||||
ks = new FileInputStream(testCert);
|
||||
} else {
|
||||
// Assume the working directory is the root of the project.
|
||||
ks = new FileInputStream("tests/certs/" + keyStoreName);
|
||||
}
|
||||
}
|
||||
|
||||
InputStream ts = cl.getResourceAsStream(trustStoreName);
|
||||
if (ts == null) {
|
||||
// Assume the working directory is "java/servers".
|
||||
String testCert = "../../tests/certs/" + trustStoreName;
|
||||
if (new File(testCert).isFile()) {
|
||||
ts = new FileInputStream(testCert);
|
||||
} else {
|
||||
// Assume the working directory is the root of the project.
|
||||
ts = new FileInputStream("tests/certs/" + trustStoreName);
|
||||
}
|
||||
}
|
||||
|
||||
return new SSLOptions(ks, ksPassphrase, ksContainerType, ts, tsPassphrase, tsContainerType, false, gridSSL, sslProtocolString, null);
|
||||
}
|
||||
|
||||
private static interface ResponseCreator {
|
||||
public void answer(RPCServerRequest rq, PingRequest pRq) throws Exception;
|
||||
}
|
||||
|
||||
private static interface TestExecutor {
|
||||
public void execTest(RPCNIOSocketClient client) throws Exception;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.test.foundation.pbrpc;
|
||||
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.PBRPCDatagramPacket;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCAuthentication;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.Ping.PingRequest;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.MessageType;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.RPCHeader;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.UserCredentials;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class PBRPCDatagramPacketTest {
|
||||
|
||||
public PBRPCDatagramPacketTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
// TODO add test methods here.
|
||||
// The methods must be annotated with annotation @Test. For example:
|
||||
//
|
||||
// @Test
|
||||
// public void hello() {}
|
||||
|
||||
@Test
|
||||
public void testPacket() throws Exception {
|
||||
|
||||
RPCHeader.RequestHeader rqHdr = RPCHeader.RequestHeader.newBuilder().setAuthData(RPCAuthentication.authNone).setUserCreds(RPCAuthentication.userService).setInterfaceId(1).setProcId(2).build();
|
||||
RPCHeader hdr = RPCHeader.newBuilder().setCallId(12345).setMessageType(MessageType.RPC_REQUEST).setRequestHeader(rqHdr).build();
|
||||
|
||||
PingRequest pRq = PingRequest.newBuilder().setText("YAGGA!").setSendError(false).build();
|
||||
|
||||
PBRPCDatagramPacket dp = new PBRPCDatagramPacket(hdr, pRq);
|
||||
ReusableBuffer data = dp.assembleDatagramPacket();
|
||||
|
||||
dp = new PBRPCDatagramPacket(data, PingRequest.getDefaultInstance());
|
||||
|
||||
PingRequest response = (PingRequest)dp.getMessage();
|
||||
|
||||
assertEquals(dp.getHeader().getCallId(),12345);
|
||||
assertEquals(dp.getHeader().getMessageType(),MessageType.RPC_REQUEST);
|
||||
assertEquals(response.getText(),pRq.getText());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.xtreemfs.test.foundation.pbrpc;
|
||||
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.TimeSync;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequest;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.Ping.PingResponse;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequestListener;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCNIOSocketServer;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCAuthentication;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCNIOSocketClient;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCResponse;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferInputStream;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.Ping;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.PingServiceClient;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class PBRPCTest {
|
||||
private final int TEST_PORT = 12999;
|
||||
|
||||
private static TimeSync ts = null;
|
||||
|
||||
public PBRPCTest() {
|
||||
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
Logging.start(Logging.LEVEL_WARN, Logging.Category.all);
|
||||
ts = TimeSync.initializeLocal(50);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
ts.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRPCClient() throws Exception {
|
||||
RPCNIOSocketClient client = null;
|
||||
RPCNIOSocketServer server = null;
|
||||
|
||||
try {
|
||||
|
||||
server = new RPCNIOSocketServer(TEST_PORT, null, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("received request");
|
||||
try {
|
||||
ReusableBufferInputStream is = new ReusableBufferInputStream(rq.getMessage());
|
||||
Ping.PingRequest pingRq = Ping.PingRequest.parseFrom(is);
|
||||
|
||||
Ping.PingResponse.PingResult result = Ping.PingResponse.PingResult.newBuilder().setText(pingRq.getText()).build();
|
||||
Ping.PingResponse resp = Ping.PingResponse.newBuilder().setResult(result).build();
|
||||
|
||||
rq.sendResponse(resp, null);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.GARBAGE_ARGS).setErrorMessage(ex.getMessage()).setDebugInfo(OutputUtils.stackTraceToString(ex)).build());
|
||||
fail(ex.toString());
|
||||
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
server.start();
|
||||
server.waitForStartup();
|
||||
|
||||
client = new RPCNIOSocketClient(null, 15000, 5*60*1000, "testRPCClient");
|
||||
client.start();
|
||||
client.waitForStartup();
|
||||
|
||||
PingServiceClient psClient = new PingServiceClient(client,null);
|
||||
|
||||
RPC.UserCredentials userCred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("tester").build();
|
||||
RPCResponse<PingResponse> response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Hello World!", false, null);
|
||||
assertEquals(response.get().getResult().getText(),"Hello World!");
|
||||
|
||||
response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Murpel", false, null);
|
||||
assertEquals(response.get().getResult().getText(),"Murpel");
|
||||
|
||||
} finally {
|
||||
//clean up
|
||||
if (client != null) {
|
||||
client.shutdown();
|
||||
client.waitForShutdown();
|
||||
}
|
||||
if (server != null) {
|
||||
server.shutdown();
|
||||
server.waitForShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRPCWithData() throws Exception {
|
||||
RPCNIOSocketClient client = null;
|
||||
RPCNIOSocketServer server = null;
|
||||
|
||||
try {
|
||||
|
||||
server = new RPCNIOSocketServer(TEST_PORT, null, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("received request");
|
||||
try {
|
||||
ReusableBufferInputStream is = new ReusableBufferInputStream(rq.getMessage());
|
||||
Ping.PingRequest pingRq = Ping.PingRequest.parseFrom(is);
|
||||
|
||||
Ping.PingResponse.PingResult result = Ping.PingResponse.PingResult.newBuilder().setText(pingRq.getText()).build();
|
||||
Ping.PingResponse resp = Ping.PingResponse.newBuilder().setResult(result).build();
|
||||
|
||||
ReusableBuffer data = null;
|
||||
if (rq.getData() != null) {
|
||||
data = rq.getData().createViewBuffer();
|
||||
data.limit(data.capacity());
|
||||
data.position(data.capacity());
|
||||
}
|
||||
|
||||
rq.sendResponse(resp, data);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.GARBAGE_ARGS).setErrorMessage(ex.getMessage()).setDebugInfo(OutputUtils.stackTraceToString(ex)).build());
|
||||
fail(ex.toString());
|
||||
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
server.start();
|
||||
server.waitForStartup();
|
||||
|
||||
client = new RPCNIOSocketClient(null, 15000, 5*60*1000, "testRPCWithData");
|
||||
client.start();
|
||||
client.waitForStartup();
|
||||
|
||||
PingServiceClient psClient = new PingServiceClient(client,null);
|
||||
|
||||
byte[] arr = new byte[2065];
|
||||
for (int i= 0; i < arr.length; i++)
|
||||
arr[i] = 'x';
|
||||
ReusableBuffer sendData = ReusableBuffer.wrap(arr);
|
||||
// System.out.println("data: "+sendData);
|
||||
|
||||
RPC.UserCredentials userCred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("tester").build();
|
||||
RPCResponse<PingResponse> response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Hello World!", false, sendData);
|
||||
assertEquals(response.get().getResult().getText(),"Hello World!");
|
||||
|
||||
ReusableBuffer recdata = response.getData();
|
||||
assertTrue(recdata.hasRemaining());
|
||||
while (recdata.hasRemaining()) {
|
||||
assertEquals(recdata.get(),(byte)'x');
|
||||
}
|
||||
|
||||
response = psClient.doPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred, "Murpel", false, null);
|
||||
assertEquals(response.get().getResult().getText(),"Murpel");
|
||||
|
||||
} finally {
|
||||
//clean up
|
||||
if (client != null) {
|
||||
client.shutdown();
|
||||
client.waitForShutdown();
|
||||
}
|
||||
if (server != null) {
|
||||
server.shutdown();
|
||||
server.waitForShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEmptyMessages() throws Exception {
|
||||
RPCNIOSocketClient client = null;
|
||||
RPCNIOSocketServer server = null;
|
||||
|
||||
try {
|
||||
|
||||
server = new RPCNIOSocketServer(TEST_PORT, null, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("received request");
|
||||
try {
|
||||
assertNull(rq.getMessage());
|
||||
rq.sendResponse(null, null);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.GARBAGE_ARGS).setErrorMessage(ex.getMessage()).setDebugInfo(OutputUtils.stackTraceToString(ex)).build());
|
||||
fail(ex.toString());
|
||||
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
server.start();
|
||||
server.waitForStartup();
|
||||
|
||||
client = new RPCNIOSocketClient(null, 15000, 5*60*1000, "PBRPCTest::testEmptyMessages()");
|
||||
client.start();
|
||||
client.waitForStartup();
|
||||
|
||||
PingServiceClient psClient = new PingServiceClient(client,null);
|
||||
|
||||
RPC.UserCredentials userCred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("tester").build();
|
||||
RPCResponse response = psClient.emptyPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred);
|
||||
assertNull(response.get());
|
||||
|
||||
response = psClient.emptyPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred);
|
||||
assertNull(response.get());
|
||||
|
||||
response = psClient.emptyPing(new InetSocketAddress("localhost", TEST_PORT), RPCAuthentication.authNone, userCred);
|
||||
assertNull(response.get());
|
||||
|
||||
} finally {
|
||||
//clean up
|
||||
if (client != null) {
|
||||
client.shutdown();
|
||||
client.waitForShutdown();
|
||||
}
|
||||
if (server != null) {
|
||||
server.shutdown();
|
||||
server.waitForShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.test.foundation.pbrpc;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
import org.xtreemfs.foundation.buffer.ReusableBuffer;
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCNIOSocketServer;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.xtreemfs.foundation.buffer.BufferPool;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequest;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequestListener;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.RecordMarker;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferInputStream;
|
||||
import org.xtreemfs.foundation.pbrpc.utils.ReusableBufferOutputStream;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCNIOSocketServerTest {
|
||||
|
||||
private RPCNIOSocketServer server;
|
||||
|
||||
public RPCNIOSocketServerTest() {
|
||||
Logging.start(Logging.LEVEL_WARN, Logging.Category.all);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
// TODO add test methods here.
|
||||
// The methods must be annotated with annotation @Test. For example:
|
||||
//
|
||||
// @Test
|
||||
// public void hello() {}
|
||||
|
||||
@Test
|
||||
public void testSerialization() throws Exception {
|
||||
final int CALLID = 5464566;
|
||||
|
||||
RPC.Auth auth = RPC.Auth.newBuilder().setAuthType(RPC.AuthType.AUTH_NONE).build();
|
||||
RPC.UserCredentials ucred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("user").build();
|
||||
RPC.RPCHeader.RequestHeader rqHdr = RPC.RPCHeader.RequestHeader.newBuilder().setAuthData(auth).setUserCreds(ucred).setProcId(2).setInterfaceId(2).build();
|
||||
RPC.RPCHeader header = RPC.RPCHeader.newBuilder().setCallId(CALLID).setMessageType(RPC.MessageType.RPC_REQUEST).setRequestHeader(rqHdr).build();
|
||||
|
||||
ReusableBufferOutputStream ois = new ReusableBufferOutputStream(ReusableBufferOutputStream.BUFF_SIZE);
|
||||
header.writeTo(ois);
|
||||
ois.flip();
|
||||
|
||||
byte[] data = new byte[ois.getBuffers()[0].remaining()];
|
||||
ois.getBuffers()[0].get(data);
|
||||
|
||||
ReusableBufferInputStream is = new ReusableBufferInputStream(ReusableBuffer.wrap(data));
|
||||
|
||||
RPC.RPCHeader deser = RPC.RPCHeader.parseFrom(is);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleRPC() throws Exception {
|
||||
|
||||
final int CALLID = 5464566;
|
||||
final int TEST_PORT = 9991;
|
||||
final String USERID = "yaggaYagga";
|
||||
|
||||
server = new RPCNIOSocketServer(TEST_PORT, null, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("received request");
|
||||
try {
|
||||
assertEquals(CALLID,rq.getHeader().getCallId());
|
||||
assertEquals(RPC.MessageType.RPC_REQUEST, rq.getHeader().getMessageType());
|
||||
|
||||
//send a dummy message
|
||||
RPC.UserCredentials msg = RPC.UserCredentials.newBuilder().setUsername(USERID).build();
|
||||
|
||||
ReusableBuffer data = BufferPool.allocate(2);
|
||||
data.put((byte)15);
|
||||
data.put((byte)20);
|
||||
|
||||
rq.sendResponse(msg, data);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.GARBAGE_ARGS).setErrorMessage(ex.getMessage()).setDebugInfo(OutputUtils.stackTraceToString(ex)).build());
|
||||
fail(ex.toString());
|
||||
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
server.start();
|
||||
server.waitForStartup();
|
||||
|
||||
Socket sock = new Socket("localhost", TEST_PORT);
|
||||
OutputStream out = sock.getOutputStream();
|
||||
InputStream in = sock.getInputStream();
|
||||
|
||||
RPC.Auth auth = RPC.Auth.newBuilder().setAuthType(RPC.AuthType.AUTH_NONE).build();
|
||||
RPC.UserCredentials ucred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("user").build();
|
||||
RPC.RPCHeader.RequestHeader rqHdr = RPC.RPCHeader.RequestHeader.newBuilder().setAuthData(auth).setUserCreds(ucred).setProcId(2).setInterfaceId(2).build();
|
||||
RPC.RPCHeader header = RPC.RPCHeader.newBuilder().setCallId(CALLID).setMessageType(RPC.MessageType.RPC_REQUEST).setRequestHeader(rqHdr).build();
|
||||
|
||||
ReusableBufferOutputStream ois = new ReusableBufferOutputStream(ReusableBufferOutputStream.BUFF_SIZE);
|
||||
header.writeTo(ois);
|
||||
int hdrLen = ois.length();
|
||||
|
||||
ByteBuffer recordMarker = ByteBuffer.allocate(RecordMarker.HDR_SIZE);
|
||||
recordMarker.putInt(hdrLen);
|
||||
recordMarker.putInt(0);
|
||||
recordMarker.putInt(0);
|
||||
recordMarker.flip();
|
||||
|
||||
ois.flip();
|
||||
|
||||
out.write(recordMarker.array());
|
||||
byte[] data = new byte[ois.getBuffers()[0].remaining()];
|
||||
ois.getBuffers()[0].get(data);
|
||||
out.write(data);
|
||||
|
||||
byte[] markerIn = new byte[RecordMarker.HDR_SIZE];
|
||||
in.read(markerIn);
|
||||
ReusableBuffer marker = ReusableBuffer.wrap(markerIn);
|
||||
|
||||
hdrLen = marker.getInt();
|
||||
int msgLen = marker.getInt();
|
||||
int dataLen = marker.getInt();
|
||||
|
||||
// System.out.println("header: "+hdrLen+"/"+msgLen+"/"+dataLen);
|
||||
|
||||
byte[] hdrIn = new byte[hdrLen];
|
||||
byte[] msgIn = new byte[msgLen];
|
||||
byte[] dataIn = new byte[dataLen];
|
||||
|
||||
in.read(hdrIn);
|
||||
in.read(msgIn);
|
||||
in.read(dataIn);
|
||||
|
||||
// System.out.println("read data");
|
||||
|
||||
RPC.RPCHeader respHdr = RPC.RPCHeader.parseFrom(hdrIn);
|
||||
|
||||
RPC.UserCredentials uc = RPC.UserCredentials.parseFrom(msgIn);
|
||||
|
||||
assertEquals(RPC.MessageType.RPC_RESPONSE_SUCCESS,respHdr.getMessageType());
|
||||
assertEquals(header.getCallId(),respHdr.getCallId());
|
||||
|
||||
assertEquals(USERID,uc.getUsername());
|
||||
|
||||
assertEquals(15,dataIn[0]);
|
||||
assertEquals(20,dataIn[1]);
|
||||
|
||||
sock.close();
|
||||
server.shutdown();
|
||||
server.waitForShutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRPCWithoutMessage() throws Exception {
|
||||
|
||||
final int CALLID = 5464566;
|
||||
final int TEST_PORT = 9991;
|
||||
final String USERID = "yaggaYagga";
|
||||
|
||||
server = new RPCNIOSocketServer(TEST_PORT, null, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("received request");
|
||||
try {
|
||||
assertNotNull(rq.getData());
|
||||
assertNull(rq.getMessage());
|
||||
assertEquals(CALLID,rq.getHeader().getCallId());
|
||||
assertEquals(RPC.MessageType.RPC_REQUEST, rq.getHeader().getMessageType());
|
||||
|
||||
//send a dummy message
|
||||
RPC.UserCredentials msg = RPC.UserCredentials.newBuilder().setUsername(USERID).build();
|
||||
|
||||
ReusableBuffer data = BufferPool.allocate(2);
|
||||
data.put((byte)15);
|
||||
data.put((byte)20);
|
||||
|
||||
rq.sendResponse(msg, data);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.GARBAGE_ARGS).setErrorMessage(ex.getMessage()).setDebugInfo(OutputUtils.stackTraceToString(ex)).build());
|
||||
fail(ex.toString());
|
||||
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
server.start();
|
||||
server.waitForStartup();
|
||||
|
||||
Socket sock = new Socket("localhost", TEST_PORT);
|
||||
OutputStream out = sock.getOutputStream();
|
||||
InputStream in = sock.getInputStream();
|
||||
|
||||
RPC.Auth auth = RPC.Auth.newBuilder().setAuthType(RPC.AuthType.AUTH_NONE).build();
|
||||
RPC.UserCredentials ucred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("user").build();
|
||||
RPC.RPCHeader.RequestHeader rqHdr = RPC.RPCHeader.RequestHeader.newBuilder().setAuthData(auth).setUserCreds(ucred).setProcId(2).setInterfaceId(2).build();
|
||||
RPC.RPCHeader header = RPC.RPCHeader.newBuilder().setCallId(CALLID).setMessageType(RPC.MessageType.RPC_REQUEST).setRequestHeader(rqHdr).build();
|
||||
|
||||
ReusableBufferOutputStream ois = new ReusableBufferOutputStream(ReusableBufferOutputStream.BUFF_SIZE);
|
||||
header.writeTo(ois);
|
||||
int hdrLen = ois.length();
|
||||
|
||||
ByteBuffer recordMarker = ByteBuffer.allocate(RecordMarker.HDR_SIZE);
|
||||
recordMarker.putInt(hdrLen);
|
||||
recordMarker.putInt(0);
|
||||
recordMarker.putInt(16);
|
||||
recordMarker.flip();
|
||||
|
||||
ois.flip();
|
||||
|
||||
out.write(recordMarker.array());
|
||||
byte[] data = new byte[ois.getBuffers()[0].remaining()];
|
||||
ois.getBuffers()[0].get(data);
|
||||
out.write(data);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
out.write('a');
|
||||
}
|
||||
|
||||
byte[] markerIn = new byte[RecordMarker.HDR_SIZE];
|
||||
in.read(markerIn);
|
||||
ReusableBuffer marker = ReusableBuffer.wrap(markerIn);
|
||||
|
||||
hdrLen = marker.getInt();
|
||||
int msgLen = marker.getInt();
|
||||
int dataLen = marker.getInt();
|
||||
|
||||
// System.out.println("header: "+hdrLen+"/"+msgLen+"/"+dataLen);
|
||||
|
||||
byte[] hdrIn = new byte[hdrLen];
|
||||
byte[] msgIn = new byte[msgLen];
|
||||
byte[] dataIn = new byte[dataLen];
|
||||
|
||||
in.read(hdrIn);
|
||||
in.read(msgIn);
|
||||
in.read(dataIn);
|
||||
|
||||
// System.out.println("read data");
|
||||
|
||||
RPC.RPCHeader respHdr = RPC.RPCHeader.parseFrom(hdrIn);
|
||||
|
||||
RPC.UserCredentials uc = RPC.UserCredentials.parseFrom(msgIn);
|
||||
|
||||
assertEquals(RPC.MessageType.RPC_RESPONSE_SUCCESS,respHdr.getMessageType());
|
||||
assertEquals(header.getCallId(),respHdr.getCallId());
|
||||
|
||||
assertEquals(USERID,uc.getUsername());
|
||||
|
||||
assertEquals(15,dataIn[0]);
|
||||
assertEquals(20,dataIn[1]);
|
||||
|
||||
sock.close();
|
||||
server.shutdown();
|
||||
server.waitForShutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorResponse() throws Exception {
|
||||
|
||||
final int CALLID = 5464566;
|
||||
final int TEST_PORT = 9991;
|
||||
final String USERID = "yaggaYagga";
|
||||
|
||||
server = new RPCNIOSocketServer(TEST_PORT, null, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("received request");
|
||||
try {
|
||||
assertEquals(CALLID,rq.getHeader().getCallId());
|
||||
assertEquals(RPC.MessageType.RPC_REQUEST, rq.getHeader().getMessageType());
|
||||
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.AUTH_FAILED).setErrorMessage("dummy error").setDebugInfo("no info here").build());
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
rq.sendError(RPC.RPCHeader.ErrorResponse.newBuilder().setErrorType(RPC.ErrorType.GARBAGE_ARGS).setErrorMessage(ex.getMessage()).setDebugInfo(OutputUtils.stackTraceToString(ex)).build());
|
||||
rq.freeBuffers();
|
||||
fail(ex.toString());
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
server.start();
|
||||
server.waitForStartup();
|
||||
|
||||
Socket sock = new Socket("localhost", TEST_PORT);
|
||||
OutputStream out = sock.getOutputStream();
|
||||
InputStream in = sock.getInputStream();
|
||||
|
||||
RPC.Auth auth = RPC.Auth.newBuilder().setAuthType(RPC.AuthType.AUTH_NONE).build();
|
||||
RPC.UserCredentials ucred = RPC.UserCredentials.newBuilder().setUsername("test").addGroups("user").build();
|
||||
RPC.RPCHeader.RequestHeader rqHdr = RPC.RPCHeader.RequestHeader.newBuilder().setAuthData(auth).setUserCreds(ucred).setProcId(2).setInterfaceId(2).build();
|
||||
RPC.RPCHeader header = RPC.RPCHeader.newBuilder().setCallId(CALLID).setMessageType(RPC.MessageType.RPC_REQUEST).setRequestHeader(rqHdr).build();
|
||||
|
||||
ReusableBufferOutputStream ois = new ReusableBufferOutputStream(ReusableBufferOutputStream.BUFF_SIZE);
|
||||
header.writeTo(ois);
|
||||
int hdrLen = ois.length();
|
||||
|
||||
ByteBuffer recordMarker = ByteBuffer.allocate(RecordMarker.HDR_SIZE);
|
||||
recordMarker.putInt(hdrLen);
|
||||
recordMarker.putInt(0);
|
||||
recordMarker.putInt(0);
|
||||
recordMarker.flip();
|
||||
|
||||
ois.flip();
|
||||
|
||||
out.write(recordMarker.array());
|
||||
byte[] data = new byte[ois.getBuffers()[0].remaining()];
|
||||
ois.getBuffers()[0].get(data);
|
||||
out.write(data);
|
||||
|
||||
byte[] markerIn = new byte[RecordMarker.HDR_SIZE];
|
||||
in.read(markerIn);
|
||||
ReusableBuffer marker = ReusableBuffer.wrap(markerIn);
|
||||
|
||||
hdrLen = marker.getInt();
|
||||
int msgLen = marker.getInt();
|
||||
int dataLen = marker.getInt();
|
||||
|
||||
assertEquals(0,msgLen);
|
||||
assertEquals(0,dataLen);
|
||||
|
||||
// System.out.println("header: "+hdrLen+"/"+msgLen+"/"+dataLen);
|
||||
|
||||
byte[] hdrIn = new byte[hdrLen];
|
||||
|
||||
in.read(hdrIn);
|
||||
|
||||
RPC.RPCHeader respHdr = RPC.RPCHeader.parseFrom(hdrIn);
|
||||
|
||||
assertEquals(RPC.MessageType.RPC_RESPONSE_ERROR,respHdr.getMessageType());
|
||||
assertEquals(header.getCallId(),respHdr.getCallId());
|
||||
assertEquals(RPC.ErrorType.AUTH_FAILED,respHdr.getErrorResponse().getErrorType());
|
||||
|
||||
sock.close();
|
||||
server.shutdown();
|
||||
server.waitForShutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.xtreemfs.test.foundation.pbrpc;
|
||||
|
||||
import org.xtreemfs.foundation.logging.Logging;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.RPCHeader;
|
||||
import java.net.InetSocketAddress;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequest;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCUDPSocketServer;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.xtreemfs.foundation.pbrpc.client.RPCAuthentication;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.Ping.PingRequest;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.ErrorType;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.MessageType;
|
||||
import org.xtreemfs.foundation.pbrpc.generatedinterfaces.RPC.POSIXErrno;
|
||||
import org.xtreemfs.foundation.pbrpc.server.RPCServerRequestListener;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class RPCUDPSocketServerTest {
|
||||
|
||||
RPCUDPSocketServer server1, server2;
|
||||
|
||||
final static int PORT_1 = 33333;
|
||||
final static int PORT_2 = 33334;
|
||||
|
||||
public RPCUDPSocketServerTest() {
|
||||
Logging.start(Logging.LEVEL_WARN, Logging.Category.all);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
// TODO add test methods here.
|
||||
// The methods must be annotated with annotation @Test. For example:
|
||||
//
|
||||
// @Test
|
||||
// public void hello() {}
|
||||
|
||||
@Test
|
||||
public void testUDP() throws Exception {
|
||||
server1 = new RPCUDPSocketServer(PORT_1, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("srv1: "+rq);
|
||||
}
|
||||
});
|
||||
|
||||
server2 = new RPCUDPSocketServer(PORT_2, new RPCServerRequestListener() {
|
||||
|
||||
@Override
|
||||
public void receiveRecord(RPCServerRequest rq) {
|
||||
// System.out.println("srv2: "+rq);
|
||||
rq.sendError(ErrorType.ERRNO, POSIXErrno.POSIX_ERROR_EIO, "yagga");
|
||||
}
|
||||
});
|
||||
|
||||
server1.start();
|
||||
server2.start();
|
||||
|
||||
server1.waitForStartup();
|
||||
server2.waitForStartup();
|
||||
|
||||
RPCHeader.RequestHeader rqHdr = RPCHeader.RequestHeader.newBuilder().setAuthData(RPCAuthentication.authNone).setUserCreds(RPCAuthentication.userService).setInterfaceId(1).setProcId(5).build();
|
||||
RPCHeader hdr = RPCHeader.newBuilder().setCallId(555).setMessageType(MessageType.RPC_REQUEST).setRequestHeader(rqHdr).build();
|
||||
PingRequest pRq = PingRequest.newBuilder().setSendError(false).setText("yagga").build();
|
||||
server1.sendRequest(hdr, pRq, new InetSocketAddress("localhost",PORT_2));
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
server1.shutdown();
|
||||
server2.shutdown();
|
||||
|
||||
server1.waitForShutdown();
|
||||
server2.waitForShutdown();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2011 by Jan Stender, Bjoern Kolbeck,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.xtreemfs.test.foundation.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.xtreemfs.foundation.util.OutputUtils;
|
||||
|
||||
public class OutputUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testLongHexEncoding() throws Exception {
|
||||
|
||||
final long[] values = { 805306368000L, Integer.MIN_VALUE, Integer.MAX_VALUE, Long.MIN_VALUE,
|
||||
Long.MAX_VALUE, 1, 0, -1 };
|
||||
|
||||
for (long value : values) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
OutputUtils.writeHexLong(sb, value);
|
||||
|
||||
assertEquals(value, OutputUtils.readHexLong(sb.toString(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWriteHex() {
|
||||
final int objno = 129;
|
||||
final int objver = 459435;
|
||||
final int trepo = 1;
|
||||
final long checksum = 843349439598l;
|
||||
|
||||
final StringBuffer sb = new StringBuffer(Integer.SIZE/8*3+2*Long.SIZE/8);
|
||||
OutputUtils.writeHexInt(sb,objno);
|
||||
OutputUtils.writeHexInt(sb,objver);
|
||||
OutputUtils.writeHexInt(sb,trepo);
|
||||
OutputUtils.writeHexInt(sb,(int) (checksum >> 32));
|
||||
OutputUtils.writeHexInt(sb,(int) (checksum & 0xFFFFFFFF));
|
||||
OutputUtils.writeHexLong(sb, checksum);
|
||||
final String result = sb.toString();
|
||||
// System.out.println("result: "+result);
|
||||
|
||||
int tmp = OutputUtils.readHexInt(result, 0);
|
||||
assertEquals(objno,tmp);
|
||||
tmp = OutputUtils.readHexInt(result, 8);
|
||||
assertEquals(objver,tmp);
|
||||
tmp = OutputUtils.readHexInt(result, 16);
|
||||
assertEquals(trepo,tmp);
|
||||
tmp = OutputUtils.readHexInt(result, 24);
|
||||
long tmp2 = ((long)tmp)<< 32;
|
||||
tmp = OutputUtils.readHexInt(result, 32);
|
||||
tmp2 += tmp;
|
||||
assertEquals(checksum,tmp2);
|
||||
tmp2 = OutputUtils.readHexLong(result, 40);
|
||||
assertEquals(checksum,tmp2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 by Bjoern Kolbeck, Felix Langner,
|
||||
* Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.xtreemfs.test.foundation.util;
|
||||
|
||||
import org.junit.*;
|
||||
import org.xtreemfs.foundation.pbrpc.Schemes;
|
||||
import org.xtreemfs.foundation.util.PBRPCServiceURL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author bjko
|
||||
*/
|
||||
public class PBRPCServiceURLTest {
|
||||
|
||||
public PBRPCServiceURLTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getProtocol method, of class ONCRPCServiceURL.
|
||||
*/
|
||||
@Test
|
||||
public void testURLParse() throws Exception {
|
||||
String host = "yagga";
|
||||
int port = 1254;
|
||||
PBRPCServiceURL u = new PBRPCServiceURL("pbrpcg://"+host+":"+port+"/",
|
||||
Schemes.SCHEME_PBRPC,12345);
|
||||
assertEquals(host, u.getHost());
|
||||
assertEquals(port, u.getPort());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user