├── .gitignore ├── LICENSE ├── RELEASE-NOTES.txt ├── build.gradle ├── clean-all.sh ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── setversion.sh └── src ├── main └── java │ └── doip │ └── library │ ├── comm │ ├── DoipTcpConnection.java │ ├── DoipTcpConnectionListener.java │ ├── DoipTcpStreamBuffer.java │ ├── DoipTcpStreamBufferListener.java │ ├── DoipUdpMessageHandler.java │ ├── DoipUdpMessageHandlerListener.java │ └── package-info.java │ ├── exception │ ├── DoipException.java │ ├── HeaderTooShort.java │ ├── IllegalNullArgument.java │ ├── IncorrectPatternFormat.java │ ├── InvalidPayloadLength.java │ ├── InvalidPayloadType.java │ ├── MessageNotComplete.java │ ├── MessageTooLarge.java │ ├── OutOfMemory.java │ ├── UnknownPayloadType.java │ ├── UnsupportedEncoding.java │ └── package-info.java │ ├── message │ ├── DoipHeaderNegAck.java │ ├── DoipMessage.java │ ├── DoipTcpAliveCheckRequest.java │ ├── DoipTcpAliveCheckResponse.java │ ├── DoipTcpDiagnosticMessage.java │ ├── DoipTcpDiagnosticMessageAck.java │ ├── DoipTcpDiagnosticMessageNegAck.java │ ├── DoipTcpDiagnosticMessagePosAck.java │ ├── DoipTcpHeaderNegAck.java │ ├── DoipTcpMessage.java │ ├── DoipTcpRoutingActivationRequest.java │ ├── DoipTcpRoutingActivationResponse.java │ ├── DoipUdpDiagnosticPowerModeRequest.java │ ├── DoipUdpDiagnosticPowerModeResponse.java │ ├── DoipUdpEntityStatusRequest.java │ ├── DoipUdpEntityStatusResponse.java │ ├── DoipUdpHeaderNegAck.java │ ├── DoipUdpMessage.java │ ├── DoipUdpVehicleAnnouncementMessage.java │ ├── DoipUdpVehicleIdentRequest.java │ ├── DoipUdpVehicleIdentRequestWithEid.java │ ├── DoipUdpVehicleIdentRequestWithVin.java │ ├── UdsMessage.java │ └── package-info.java │ ├── net │ ├── TcpReceiver.java │ ├── TcpReceiverListener.java │ ├── TcpReceiverThread.java │ ├── TcpServer.java │ ├── TcpServerListener.java │ ├── TcpServerThread.java │ ├── UdpReceiver.java │ ├── UdpReceiverListener.java │ ├── UdpReceiverThread.java │ └── package-info.java │ ├── properties │ ├── EmptyPropertyValue.java │ ├── MissingProperty.java │ ├── MissingSystemProperty.java │ ├── PropertyFile.java │ └── package-info.java │ ├── timer │ ├── NanoTimer.java │ ├── Timer.java │ ├── TimerListener.java │ └── TimerThread.java │ └── util │ ├── Conversion.java │ ├── Helper.java │ ├── LookupEntry.java │ ├── LookupTable.java │ ├── PlantUmlx.java │ ├── StreamBuffer.java │ ├── StringConstants.java │ ├── TextBuilder.java │ └── package-info.java └── test ├── java └── doip │ └── library │ ├── message │ ├── TestDoipTcpAliveCheckRequest.java │ ├── TestDoipTcpAliveCheckResponse.java │ ├── TestDoipTcpDiagnosticMessage.java │ ├── TestDoipTcpDiagnosticMessageNegAck.java │ └── TestDoipTcpDiagnosticMessagePosAck.java │ ├── properties │ └── TestPropertyFile.java │ ├── test │ └── TestTemplate.java │ ├── timer │ └── TestTimer.java │ └── util │ ├── TestConversion.java │ ├── TestLookupEntry.java │ └── TestLookupTable.java └── resources ├── LookupTableA.txt ├── log4j2.xml └── test.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /build/ 3 | /.classpath 4 | /.project 5 | /.settings/ 6 | /bin/ 7 | doip-library.log 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /RELEASE-NOTES.txt: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | 3 | DoIP - Diagnostics over IP 4 | Component: DoIP Library 5 | 6 | ============================================================================== 7 | 8 | INTRODUCTION 9 | ============ 10 | The "DoIP Library" contains common implementation for DoIP which can be 11 | used in a DoIP simulation as well as in a DoIP tester. 12 | 13 | The source code is hosted at GitHub 14 | https://github.com/doip/doip-library.git 15 | 16 | A documentation of the whole project is available at 17 | http://automotive-doip.com/downloads/DoIP-Software-Documentation.pdf 18 | 19 | ============================================================================== 20 | 21 | Release Notes for Version: 1.3.0 22 | 23 | CHANGES 24 | ======= 25 | 26 | - Issue #24: Changed DoIP version to 0x03 27 | 28 | ============================================================================== 29 | 30 | Release Notes for Version: 1.2.2 31 | 32 | CHANGES 33 | ======= 34 | 35 | - Issue #22: Updated README 36 | 37 | ============================================================================== 38 | 39 | Release Notes for Version: 1.2.1 40 | 41 | CHANGES 42 | ======= 43 | 44 | - Issue #19: Minor changes (see git logs for details) 45 | 46 | ============================================================================== 47 | 48 | Release Notes for Version: 1.2.0 49 | 50 | CHANGES 51 | ======= 52 | 53 | - Issue #16: Update to JUnit 5, Gradle 7.3, doip-logging 1.2.0, 54 | doip-junit 1.1.1. 55 | 56 | ============================================================================== 57 | 58 | Release Notes for Version: 1.1.5 59 | 60 | NEW FEATURES 61 | ============ 62 | 63 | - Issue #11: Implemented class StringConstants which just contains some 64 | static strings for logging some lines and borders. This 65 | ensures that all lines in the project have the same layout. 66 | - Issue #12: PropertyFile can now also read boolean values. 67 | 68 | 69 | FIXED BUGS 70 | ========== 71 | 72 | n/a 73 | 74 | CHANGES 75 | ======= 76 | 77 | n/a 78 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // doip-library 2 | 3 | plugins { 4 | id 'java-library' 5 | id 'eclipse' 6 | id 'jacoco' 7 | } 8 | 9 | 10 | group = "com.github.doip" 11 | version = '2.0.0' 12 | 13 | repositories { 14 | mavenCentral() 15 | maven { url 'https://jitpack.io' } 16 | } 17 | 18 | dependencies { 19 | implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.19.0' 20 | implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.19.0' 21 | 22 | implementation 'com.github.starcode88:starcode88-jutils:1.0.2' 23 | 24 | testImplementation 'com.github.starcode88:starcode88-jtest:1.2.1' 25 | 26 | testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' 27 | } 28 | 29 | test { 30 | useJUnitPlatform() 31 | systemProperty 'log4j.configurationFile', 'src/test/resources/log4j2.xml' 32 | reports { 33 | junitXml { 34 | outputPerTestCase = true // defaults to false 35 | //mergeReruns = true // defaults to false 36 | } 37 | } 38 | } 39 | 40 | javadoc { 41 | failOnError = false 42 | } 43 | -------------------------------------------------------------------------------- /clean-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -f *.log 4 | rm -rf build 5 | rm -rf .settings 6 | rm -f .classpath 7 | rm -f .project 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doip/doip-library/2558ee40b04da450a165c45e7bc812e3501ec6dc/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 134 | 135 | Please set the JAVA_HOME variable in your environment to match the 136 | location of your Java installation." 137 | fi 138 | 139 | # Increase the maximum file descriptors if we can. 140 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 141 | case $MAX_FD in #( 142 | max*) 143 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 144 | # shellcheck disable=SC3045 145 | MAX_FD=$( ulimit -H -n ) || 146 | warn "Could not query maximum file descriptor limit" 147 | esac 148 | case $MAX_FD in #( 149 | '' | soft) :;; #( 150 | *) 151 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 152 | # shellcheck disable=SC3045 153 | ulimit -n "$MAX_FD" || 154 | warn "Could not set maximum file descriptor limit to $MAX_FD" 155 | esac 156 | fi 157 | 158 | # Collect all arguments for the java command, stacking in reverse order: 159 | # * args from the command line 160 | # * the main class name 161 | # * -classpath 162 | # * -D...appname settings 163 | # * --module-path (only if needed) 164 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 165 | 166 | # For Cygwin or MSYS, switch paths to Windows format before running java 167 | if "$cygwin" || "$msys" ; then 168 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 169 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 170 | 171 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 172 | 173 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 174 | for arg do 175 | if 176 | case $arg in #( 177 | -*) false ;; # don't mess with options #( 178 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 179 | [ -e "$t" ] ;; #( 180 | *) false ;; 181 | esac 182 | then 183 | arg=$( cygpath --path --ignore --mixed "$arg" ) 184 | fi 185 | # Roll the args list around exactly as many times as the number of 186 | # args, so each arg winds up back in the position where it started, but 187 | # possibly modified. 188 | # 189 | # NB: a `for` loop captures its iteration list before it begins, so 190 | # changing the positional parameters here affects neither the number of 191 | # iterations, nor the values presented in `arg`. 192 | shift # remove old arg 193 | set -- "$@" "$arg" # push replacement arg 194 | done 195 | fi 196 | 197 | 198 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 199 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 200 | 201 | # Collect all arguments for the java command; 202 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 203 | # shell script including quotes and variable substitutions, so put them in 204 | # double quotes to make sure that they get re-expanded; and 205 | # * put everything else in single quotes, so that it's not re-expanded. 206 | 207 | set -- \ 208 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 209 | -classpath "$CLASSPATH" \ 210 | org.gradle.wrapper.GradleWrapperMain \ 211 | "$@" 212 | 213 | # Stop when "xargs" is not available. 214 | if ! command -v xargs >/dev/null 2>&1 215 | then 216 | die "xargs is not available" 217 | fi 218 | 219 | # Use "xargs" to parse quoted args. 220 | # 221 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 222 | # 223 | # In Bash we could simply go: 224 | # 225 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 226 | # set -- "${ARGS[@]}" "$@" 227 | # 228 | # but POSIX shell has neither arrays nor command substitution, so instead we 229 | # post-process each arg (as a line of input to sed) to backslash-escape any 230 | # character that might be a shell metacharacter, then use eval to reverse 231 | # that process (while maintaining the separation between arguments), and wrap 232 | # the whole thing up as a single "set" statement. 233 | # 234 | # This will of course break if any of these variables contains a newline or 235 | # an unmatched quote. 236 | # 237 | 238 | eval "set -- $( 239 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 240 | xargs -n1 | 241 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 242 | tr '\n' ' ' 243 | )" '"$@"' 244 | 245 | exec "$JAVACMD" "$@" 246 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'doip-library' 2 | 3 | -------------------------------------------------------------------------------- /setversion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version=$1 4 | 5 | if [ -z "$version" ] 6 | then 7 | echo "ERROR: Missing argument " 8 | exit 1 9 | fi 10 | 11 | # Modify build.gradle 12 | sed -i s/'version.*=.*'/'version = '\'"$version"\'/g build.gradle 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/doip/library/comm/DoipTcpConnection.java: -------------------------------------------------------------------------------- 1 | package doip.library.comm; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.net.Socket; 6 | import java.util.Iterator; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.apache.logging.log4j.LogManager; 12 | import org.apache.logging.log4j.Logger; 13 | 14 | import doip.library.message.DoipMessage; 15 | import doip.library.message.DoipTcpAliveCheckRequest; 16 | import doip.library.message.DoipTcpAliveCheckResponse; 17 | import doip.library.message.DoipTcpDiagnosticMessage; 18 | import doip.library.message.DoipTcpDiagnosticMessageNegAck; 19 | import doip.library.message.DoipTcpDiagnosticMessagePosAck; 20 | import doip.library.message.DoipTcpHeaderNegAck; 21 | import doip.library.message.DoipTcpRoutingActivationRequest; 22 | import doip.library.message.DoipTcpRoutingActivationResponse; 23 | import doip.library.net.TcpReceiverListener; 24 | import doip.library.net.TcpReceiverThread; 25 | import doip.library.util.Conversion; 26 | import doip.library.util.Helper; 27 | 28 | /** 29 | * Implements features for a TCP connection. It can be used in a DoIP 30 | * simulation as well as in a DoIP tester. 31 | * 32 | * But be aware that the listeners functions which will be called in case 33 | * of a invalid message will send a DoIP negative acknowledgement message. 34 | * But a diagnostic tester should not send a negative acknowledgement 35 | * message to avoid "ping-pong" behavior. If this class is used in a 36 | * diagnostic tester you should override these methods and just do nothing 37 | * in the function. 38 | * 39 | * This class contains two important 40 | * members: A instance of a DoipStreamBuffer and an instance of a 41 | * TcpReceiverThread. When the function "start(Socket socket)" will be called 42 | * this class creates a new TcpReceiverThread which is listening for incoming 43 | * data on the socket. Therefore this class will add itself to the listeners of 44 | * the TcpReceiverThread. When new data had been received it will append the 45 | * data to the instance of the DoipStreamBuffer. The DoipStreamBuffer will try 46 | * to interpret the data and calls its listeners when data had been processed. 47 | * Therefore this class adds itself also to the listeners of the 48 | * DoipStreamBuffer. 49 | * 50 | * @author Marco Wehnert 51 | * 52 | */ 53 | public class DoipTcpConnection implements DoipTcpStreamBufferListener, TcpReceiverListener { 54 | 55 | /** 56 | * log4j logger 57 | */ 58 | private static Logger logger = LogManager.getLogger(DoipTcpConnection.class); 59 | 60 | /** 61 | * Stream buffer where received TCP data will be appended and then get 62 | * processed. 63 | */ 64 | private DoipTcpStreamBuffer streamBuffer = null; 65 | 66 | /** 67 | * This is the TCP receiver thread which will waiting for new incoming data of 68 | * the TCP socket. 69 | */ 70 | private TcpReceiverThread tcpReceiverThread = null; 71 | 72 | /** 73 | * The socket on which data will be received 74 | */ 75 | private Socket socket = null; 76 | 77 | /** 78 | * Defines the maximum number (N) of bytes which will be printed from a byte field. 79 | * If the byte field is longer then only the first N bytes will be printed 80 | * followed by three dots. 81 | */ 82 | private int maxByteArraySizeLogging = 64; 83 | 84 | /** 85 | * List of listeners which will be notified when new DoIP messages had been 86 | * received. A typical listener is a gateway. The gateway will handle the 87 | * messages. 88 | */ 89 | private LinkedList listeners = new LinkedList(); 90 | 91 | private Map context = null; 92 | 93 | public DoipTcpConnection(String tcpReceiverThreadName, int maxByteArraySizeLogging) { 94 | this.maxByteArraySizeLogging = maxByteArraySizeLogging; 95 | this.streamBuffer = new DoipTcpStreamBuffer(); 96 | this.tcpReceiverThread = new TcpReceiverThread(tcpReceiverThreadName, maxByteArraySizeLogging); 97 | streamBuffer.addListener(this); 98 | } 99 | 100 | public void setContext(Map context) { 101 | this.context = context; 102 | } 103 | 104 | /** 105 | * Starts to listen on the TCP socket for incoming data. 106 | * 107 | * @param socket 108 | * @throws IOException 109 | */ 110 | public void start(Socket socket) { 111 | if (logger.isTraceEnabled()) { 112 | logger.trace(">>> public void start(Socket socket)"); 113 | } 114 | this.socket = socket; 115 | this.tcpReceiverThread.setContext(this.context); 116 | this.tcpReceiverThread.addListener(this); 117 | try { 118 | //PlantUml.logCall(this, this.tcpReceiverThread, "start(socket)"); 119 | this.tcpReceiverThread.start(socket); 120 | } finally { 121 | //PlantUml.logReturn(this, this.tcpReceiverThread); 122 | } 123 | if (logger.isTraceEnabled()) { 124 | logger.trace("<<< public void start(Socket socket)"); 125 | } 126 | } 127 | 128 | /** 129 | * Stops to listen on the socket for incoming data. 130 | */ 131 | public void stop() { 132 | if (logger.isTraceEnabled()) { 133 | logger.trace(">>> public void stop()"); 134 | } 135 | //PlantUml.logCall(this, this.tcpReceiverThread, "stop()"); 136 | this.tcpReceiverThread.stop(); 137 | //PlantUml.logReturn(this, this.tcpReceiverThread); 138 | /* Give the thread some time to terminate and call the listeners. 139 | * The thread will call the function "onSocketClosed(...)". 140 | * If it is too fast then function removeListener will remove listener 141 | * before it had a chance to call the method onSocketClosed(). 142 | */ 143 | try { 144 | Thread.sleep(10); 145 | } catch (InterruptedException e) { 146 | }; 147 | this.tcpReceiverThread.removeListener(this); 148 | if (logger.isTraceEnabled()) { 149 | logger.trace("<<< public void stop()"); 150 | } 151 | } 152 | 153 | public LinkedList getCopyOfListeners() { 154 | LinkedList copy = 155 | new LinkedList(); 156 | copy.addAll(listeners); 157 | return copy; 158 | } 159 | 160 | public void send(DoipMessage doipMessage) { 161 | if (logger.isTraceEnabled()) { 162 | logger.trace(">>> public void send(DoipMessage message)"); 163 | } 164 | byte[] messageData = doipMessage.getMessage(); 165 | this.send(messageData); 166 | 167 | if (logger.isTraceEnabled()) { 168 | logger.trace("<<< public void send(DoipMessage message)"); 169 | } 170 | } 171 | 172 | public void send(byte[] data) { 173 | if (logger.isTraceEnabled()) { 174 | logger.trace(">>> public void send(byte[] data)"); 175 | } 176 | try { 177 | if (logger.isInfoEnabled()) { 178 | logger.info("TCP-SEND: Target = " + socket.getInetAddress().getHostAddress() + ":" + socket.getPort() 179 | + ", Length = " + data.length 180 | + ", Data = " + Conversion.byteArrayToHexStringShortDotted(data, this.maxByteArraySizeLogging)); 181 | } 182 | OutputStream stream = this.socket.getOutputStream(); 183 | stream.write(data); 184 | stream.flush(); 185 | } catch (IOException e) { 186 | if (logger.isErrorEnabled()) { 187 | logger.error(Helper.getExceptionAsString(e)); 188 | } 189 | } 190 | 191 | if (logger.isTraceEnabled()) { 192 | logger.trace("<<< public void send(byte[] data)"); 193 | } 194 | } 195 | 196 | @Override 197 | public void onDataReceived(byte[] data) { 198 | if (logger.isTraceEnabled()) { 199 | logger.trace(">>> void onDataReceived(byte[] data)"); 200 | } 201 | this.streamBuffer.append(data); 202 | if (logger.isTraceEnabled()) { 203 | logger.trace("<<< void onDataReceived(byte[] data)"); 204 | } 205 | } 206 | 207 | @Override 208 | public void onHeaderIncorrectPatternFormat() { 209 | if (logger.isTraceEnabled()) { 210 | logger.trace(">>> void onHeaderIncorrectPatternFormat()"); 211 | } 212 | 213 | DoipTcpHeaderNegAck doipMessage = new DoipTcpHeaderNegAck(DoipTcpHeaderNegAck.NACK_INCORRECT_PATTERN_FORMAT); 214 | this.send(doipMessage); 215 | try { 216 | this.socket.close(); 217 | } catch (IOException e) { 218 | if (logger.isErrorEnabled()) { 219 | logger.error(Helper.getExceptionAsString(e)); 220 | } 221 | } 222 | 223 | if (logger.isTraceEnabled()) { 224 | logger.trace("<<< void onHeaderIncorrectPatternFormat()"); 225 | } 226 | } 227 | 228 | @Override 229 | public void onHeaderMessageTooLarge() { 230 | if (logger.isTraceEnabled()) { 231 | logger.trace(">>> void void onHeaderMessageTooLarge()"); 232 | } 233 | DoipTcpHeaderNegAck doipMessage = new DoipTcpHeaderNegAck(DoipTcpHeaderNegAck.NACK_MESSAGE_TOO_LARGE); 234 | this.send(doipMessage); 235 | if (logger.isTraceEnabled()) { 236 | logger.trace("<<< void void onHeaderMessageTooLarge()"); 237 | } 238 | } 239 | 240 | @Override 241 | public void onHeaderUnknownPayloadType() { 242 | if (logger.isTraceEnabled()) { 243 | logger.trace(">>> void onHeaderUnknownPayloadType()"); 244 | } 245 | DoipTcpHeaderNegAck doipMessage = new DoipTcpHeaderNegAck(DoipTcpHeaderNegAck.NACK_UNKNOWN_PAYLOAD_TYPE); 246 | this.send(doipMessage); 247 | if (logger.isTraceEnabled()) { 248 | logger.trace("<<< void onHeaderUnknownPayloadType()"); 249 | } 250 | } 251 | 252 | @Override 253 | public void onHeaderInvalidPayloadLength() { 254 | if (logger.isTraceEnabled()) { 255 | logger.trace(">>> void onHeaderInvalidPayloadLength()"); 256 | } 257 | DoipTcpHeaderNegAck doipMessage = new DoipTcpHeaderNegAck(DoipTcpHeaderNegAck.NACK_INVALID_PAYLOAD_LENGTH); 258 | this.send(doipMessage); 259 | try { 260 | this.socket.close(); 261 | } catch (IOException e) { 262 | logger.error(Helper.getExceptionAsString(e)); 263 | } 264 | if (logger.isTraceEnabled()) { 265 | logger.trace("<<< void onHeaderInvalidPayloadLength()"); 266 | } 267 | } 268 | 269 | @Override 270 | public void onPayloadCompleted(byte[] header, int payloadType, byte[] payload) { 271 | try { 272 | if (logger.isTraceEnabled()) { 273 | logger.trace(">>> void onPayloadCompleted(int payloadType, byte[] data)"); 274 | } 275 | 276 | boolean ret = false; 277 | ret = processDataByCustomHandler(header, payloadType, payload); 278 | if (ret) { 279 | return; 280 | } 281 | processDataByStandardHandler(header, payloadType, payload); 282 | 283 | } finally { 284 | if (logger.isTraceEnabled()) { 285 | logger.trace("<<< void onPayloadCompleted(int payloadType, byte[] data)"); 286 | } 287 | } 288 | } 289 | 290 | /** 291 | * This function can be overridden to implement some special handling 292 | * of the DoIP message. The parser will provide the DoIP header, the 293 | * payload type and the data in the payload. At first this method here will 294 | * be called, where you can implement some special handling. When you 295 | * return true, then no further processing of the data will be done. If 296 | * you return false, then the normal processing of the DoIP message will be 297 | * done. The normal handling will be done in the function 298 | * processDataByStandardHandler.y 299 | * @see processDataByStandardHandler 300 | * @param header 301 | * @param payloadType 302 | * @param payload 303 | * @return 304 | */ 305 | public boolean processDataByCustomHandler(byte[] header, int payloadType, byte[] payload) { 306 | return false; 307 | } 308 | 309 | public void processDataByStandardHandler(byte[] header, int payloadType, byte[] payload) { 310 | switch (payloadType) { 311 | case DoipMessage.TYPE_TCP_ALIVE_REQ: 312 | DoipTcpAliveCheckRequest doipTcpAliveCheckRequest = new DoipTcpAliveCheckRequest(); 313 | this.processDoipTcpAliveCheckRequest(doipTcpAliveCheckRequest); 314 | break; 315 | case DoipMessage.TYPE_TCP_ALIVE_RES: 316 | DoipTcpAliveCheckResponse doipTcpAliveCheckResponse = DoipTcpAliveCheckResponse.createInstance(payload); 317 | this.processDoipTcpAliveCheckResponse(doipTcpAliveCheckResponse); 318 | break; 319 | case DoipMessage.TYPE_TCP_ROUTING_REQ: 320 | DoipTcpRoutingActivationRequest doipTcpRoutingActivationRequest = DoipTcpRoutingActivationRequest 321 | .createInstance(payload); 322 | this.processDoipTcpRoutingActivationRequest(doipTcpRoutingActivationRequest); 323 | break; 324 | case DoipMessage.TYPE_TCP_ROUTING_RES: 325 | DoipTcpRoutingActivationResponse doipTcpRoutingActivationResponse = DoipTcpRoutingActivationResponse 326 | .createInstance(payload); 327 | this.processDoipTcpRoutingActivationResponse(doipTcpRoutingActivationResponse); 328 | break; 329 | case DoipMessage.TYPE_TCP_DIAG_MESSAGE: 330 | DoipTcpDiagnosticMessage doipTcpDiagnosticMessage = DoipTcpDiagnosticMessage.createInstance(payload); 331 | this.processDoipTcpDiagnosticMessage(doipTcpDiagnosticMessage); 332 | break; 333 | case DoipMessage.TYPE_TCP_DIAG_MESSAGE_POS_ACK: 334 | DoipTcpDiagnosticMessagePosAck doipTcpDiagnosticMessagePosAck = DoipTcpDiagnosticMessagePosAck 335 | .createInstance(payload); 336 | this.processDoipTcpDiagnosticMessagePosAck(doipTcpDiagnosticMessagePosAck); 337 | break; 338 | case DoipMessage.TYPE_TCP_DIAG_MESSAGE_NEG_ACK: 339 | DoipTcpDiagnosticMessageNegAck doipTcpDiagnosticMessageNegAck = DoipTcpDiagnosticMessageNegAck 340 | .createInstance(payload); 341 | this.processDoipTcpDiagnosticMessageNegAck(doipTcpDiagnosticMessageNegAck); 342 | break; 343 | case DoipMessage.TYPE_HEADER_NACK: 344 | DoipTcpHeaderNegAck doipTcpHeaderNegAck = DoipTcpHeaderNegAck.createInstance(payload); 345 | this.processDoipTcpHeaderNegAck(doipTcpHeaderNegAck); 346 | break; 347 | default: 348 | logger.fatal("##############################################"); 349 | logger.fatal("Unhandled case " + payloadType); 350 | logger.fatal("##############################################"); 351 | break; 352 | } 353 | } 354 | 355 | 356 | /** 357 | * The function will call for all listeners the function 358 | * @see onDoipTcpDiagnosticMessage 359 | * @param doipTcpDiagnosticMessage 360 | */ 361 | public void processDoipTcpDiagnosticMessage(DoipTcpDiagnosticMessage doipTcpDiagnosticMessage) { 362 | String function = "void processDoipTcpDiagnosticMessage(DoipTcpDiagnosticMessage doipTcpDiagnosticMessage)"; 363 | logger.trace(">>> " + function); 364 | 365 | Iterator iter = this.listeners.iterator(); 366 | while (iter.hasNext()) { 367 | DoipTcpConnectionListener listener = iter.next(); 368 | listener.onDoipTcpDiagnosticMessage(this, doipTcpDiagnosticMessage); 369 | } 370 | 371 | logger.trace("<<< " + function); 372 | } 373 | 374 | public void processDoipTcpDiagnosticMessageNegAck(DoipTcpDiagnosticMessageNegAck doipTcpDiagnosticMessageNegAck) { 375 | String function = "void processDoipTcpDiagnosticMessageNegAck(DoipTcpDiagnosticMessageNegAck doipTcpDiagnosticMessageNegAck)"; 376 | logger.trace(">>> " + function); 377 | 378 | Iterator iter = this.listeners.iterator(); 379 | while (iter.hasNext()) { 380 | DoipTcpConnectionListener listener = iter.next(); 381 | listener.onDoipTcpDiagnosticMessageNegAck(this, doipTcpDiagnosticMessageNegAck); 382 | } 383 | 384 | logger.trace("<<< " + function); 385 | } 386 | 387 | public void processDoipTcpDiagnosticMessagePosAck(DoipTcpDiagnosticMessagePosAck doipTcpDiagnosticMessagePosAck) { 388 | String function = "void processDoipTcpDiagnosticMessagePosAck(DoipTcpDiagnosticMessagePosAck doipTcpDiagnosticMessagePosAck)"; 389 | logger.trace(">>> " + function); 390 | 391 | Iterator iter = this.listeners.iterator(); 392 | while (iter.hasNext()) { 393 | DoipTcpConnectionListener listener = iter.next(); 394 | listener.onDoipTcpDiagnosticMessagePosAck(this, doipTcpDiagnosticMessagePosAck); 395 | } 396 | 397 | logger.trace("<<< " + function); 398 | } 399 | 400 | public void processDoipTcpRoutingActivationRequest( 401 | DoipTcpRoutingActivationRequest doipTcpRoutingActivationRequest) { 402 | String function = "void processDoipTcpRoutingActivationRequest(DoipTcpRoutingActivationRequest doipTcpRoutingActivationRequest)"; 403 | logger.trace(">>> " + function); 404 | 405 | Iterator iter = this.listeners.iterator(); 406 | while (iter.hasNext()) { 407 | DoipTcpConnectionListener listener = iter.next(); 408 | listener.onDoipTcpRoutingActivationRequest(this, doipTcpRoutingActivationRequest); 409 | } 410 | 411 | logger.trace("<<< " + function); 412 | } 413 | 414 | public void processDoipTcpRoutingActivationResponse( 415 | DoipTcpRoutingActivationResponse doipTcpRoutingActivationResponse) { 416 | String function = "void processDoipTcpRoutingActivationResponse(DoipTcpRoutingActivationResponse doipTcpRoutingActivationResponse)"; 417 | logger.trace(">>> " + function); 418 | 419 | Iterator iter = this.listeners.iterator(); 420 | while (iter.hasNext()) { 421 | DoipTcpConnectionListener listener = iter.next(); 422 | listener.onDoipTcpRoutingActivationResponse(this, doipTcpRoutingActivationResponse); 423 | } 424 | 425 | logger.trace("<<< " + function); 426 | } 427 | 428 | public void processDoipTcpAliveCheckRequest(DoipTcpAliveCheckRequest doipMessage) { 429 | String function = "void processDoipTcpAliveCheckRequest(DoipTcpAliveCheckRequest doipMessage)"; 430 | logger.trace(">>> " + function); 431 | 432 | Iterator iter = this.listeners.iterator(); 433 | while (iter.hasNext()) { 434 | DoipTcpConnectionListener listener = iter.next(); 435 | listener.onDoipTcpAliveCheckRequest(this, doipMessage); 436 | } 437 | 438 | logger.trace("<<< " + function); 439 | } 440 | 441 | /** 442 | * Will be called when a alive check response had been received. 443 | * The function just call the function onDoipTcpAliveCheckResponse(...) 444 | * in all listeners. 445 | * 446 | * @param doipTcpAliveCheckResponse 447 | */ 448 | public void processDoipTcpAliveCheckResponse(DoipTcpAliveCheckResponse doipTcpAliveCheckResponse) { 449 | String function = "void processDoipTcpAliveCheckResponse(DoipTcpAliveCheckResponse doipTcpAliveCheckResponse)"; 450 | try { 451 | logger.trace(">>> " + function); 452 | List copyOfCurrentListeners = getCopyOfListeners(); 453 | for (DoipTcpConnectionListener listener : copyOfCurrentListeners) { 454 | listener.onDoipTcpAliveCheckResponse(this, doipTcpAliveCheckResponse); 455 | } 456 | } finally { 457 | logger.trace("<<< " + function); 458 | } 459 | } 460 | 461 | public void processDoipTcpHeaderNegAck(DoipTcpHeaderNegAck doipTcpHeaderNegAck) { 462 | try { 463 | logger.trace(">>> void processDoipHeaderNegAck(DoipHeaderNegAck doipHeaderNegAck)"); 464 | 465 | List copyOfCurrentListeners = getCopyOfListeners(); 466 | for (DoipTcpConnectionListener listener : copyOfCurrentListeners) { 467 | listener.onDoipTcpHeaderNegAck(this, doipTcpHeaderNegAck); 468 | } 469 | } finally { 470 | logger.trace("<<< void processDoipHeaderNegAck(DoipHeaderNegAck doipHeaderNegAck)"); 471 | } 472 | } 473 | 474 | @Override 475 | public void onSocketClosed() { 476 | logger.trace(">>> public void onSocketClosed()"); 477 | List copyOfCurrentListeners = this.getCopyOfListeners(); 478 | logger.debug("Number of listeners: " + this.listeners.size()); 479 | for (DoipTcpConnectionListener listener : copyOfCurrentListeners) { 480 | listener.onConnectionClosed(this); 481 | } 482 | logger.trace("<<< public void onSocketClosed()"); 483 | } 484 | 485 | public final void addListener(DoipTcpConnectionListener listener) { 486 | logger.trace(">>> void addListener(DoipTcpConnectionListener listener)"); 487 | this.listeners.add(listener); 488 | logger.trace("<<< void addListener(DoipTcpConnectionListener listener)"); 489 | } 490 | 491 | public final void removeListener(DoipTcpConnectionListener listener) { 492 | logger.trace(">>> void removeListener(DoipTcpConnectionListener listener)"); 493 | this.listeners.remove(listener); 494 | logger.trace("<<< void removeListener(DoipTcpConnectionListener listener)"); 495 | } 496 | @Override 497 | public void onShredderCompleted(long payloadLength) { 498 | logger.trace(">>> public void onShredderCompleted(long payloadLength)"); 499 | logger.trace("<<< public void onShredderCompleted(long payloadLength)"); 500 | } 501 | 502 | public Socket getSocket() { 503 | return socket; 504 | } 505 | } 506 | -------------------------------------------------------------------------------- /src/main/java/doip/library/comm/DoipTcpConnectionListener.java: -------------------------------------------------------------------------------- 1 | package doip.library.comm; 2 | 3 | import doip.library.message.DoipTcpAliveCheckRequest; 4 | import doip.library.message.DoipTcpAliveCheckResponse; 5 | import doip.library.message.DoipTcpDiagnosticMessage; 6 | import doip.library.message.DoipTcpDiagnosticMessageNegAck; 7 | import doip.library.message.DoipTcpDiagnosticMessagePosAck; 8 | import doip.library.message.DoipTcpHeaderNegAck; 9 | import doip.library.message.DoipTcpRoutingActivationRequest; 10 | import doip.library.message.DoipTcpRoutingActivationResponse; 11 | 12 | public interface DoipTcpConnectionListener { 13 | 14 | public void onConnectionClosed(DoipTcpConnection doipTcpConnection); 15 | 16 | public void onDoipTcpDiagnosticMessage(DoipTcpConnection doipTcpConnection, DoipTcpDiagnosticMessage doipMessage); 17 | 18 | public void onDoipTcpDiagnosticMessagePosAck(DoipTcpConnection doipTcpConnection, DoipTcpDiagnosticMessagePosAck doipMessage); 19 | 20 | public void onDoipTcpDiagnosticMessageNegAck(DoipTcpConnection doipTcpConnection, DoipTcpDiagnosticMessageNegAck doipTcpDiagnosticMessageNegAck); 21 | 22 | public void onDoipTcpRoutingActivationRequest(DoipTcpConnection doipTcpConnection, DoipTcpRoutingActivationRequest doipMessage); 23 | 24 | public void onDoipTcpRoutingActivationResponse(DoipTcpConnection doipTcpConnection, DoipTcpRoutingActivationResponse doipMessage); 25 | 26 | public void onDoipTcpAliveCheckRequest(DoipTcpConnection doipTcpConnection, DoipTcpAliveCheckRequest doipMessage); 27 | 28 | public void onDoipTcpAliveCheckResponse(DoipTcpConnection doipTcpConnection, DoipTcpAliveCheckResponse doipMessage); 29 | 30 | public void onDoipTcpHeaderNegAck(DoipTcpConnection doipTcpConnection, DoipTcpHeaderNegAck doipMessage); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/doip/library/comm/DoipTcpStreamBuffer.java: -------------------------------------------------------------------------------- 1 | package doip.library.comm; 2 | 3 | import java.util.Iterator; 4 | import java.util.LinkedList; 5 | 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | import org.apache.logging.log4j.Marker; 9 | import org.apache.logging.log4j.MarkerManager; 10 | 11 | import doip.library.message.DoipMessage; 12 | import doip.library.util.StreamBuffer; 13 | 14 | /** 15 | * This class implements a stream buffer for DoIP which is required for TCP 16 | * communication. Bytes can be added to the buffer with the method "void 17 | * append(byte[] newData)". Then the buffer will try to interpret the existing 18 | * data in the buffer. If valid and and data had been received to complete the 19 | * DoIP message the buffer will inform its listeners. 20 | * 21 | * @author Marco Wehnert 22 | * 23 | */ 24 | public class DoipTcpStreamBuffer extends StreamBuffer { 25 | 26 | /** Log4j2 logger */ 27 | private static Logger logger = LogManager.getLogger(DoipTcpStreamBuffer.class); 28 | private static Marker enter = MarkerManager.getMarker("ENTER"); 29 | private static Marker exit = MarkerManager.getMarker("EXIT"); 30 | 31 | public static final int STATE_HEADER_NOT_COMPLETED = 1; 32 | 33 | public static final int STATE_PAYLOAD_NOT_COMPLETED = 2; 34 | 35 | public static final int STATE_SHREDDER_NOT_COMPLETED = 3; 36 | 37 | /** 38 | * Listeners which will be called when message had been completed. 39 | */ 40 | LinkedList listeners = new LinkedList(); 41 | 42 | /** 43 | * Defines the maximum length of the payload which can be accepted. If a TCP 44 | * message will be received where the payload length given by the header is 45 | * bigger than the maximum acceptable payload then the further data will 46 | * discarded. 47 | */ 48 | private long maxPayloadLength = 4095; 49 | 50 | /** 51 | * This is the payload type given by byte 2-3 52 | */ 53 | private int payloadType = -1; 54 | 55 | /** 56 | * This is the payload length which is given from byte 4-7 57 | */ 58 | private long payloadLength = -1; 59 | 60 | private long numberOfRemainingBytesToShredder = 0; 61 | private byte[] lastHeader = null; 62 | private int state = STATE_HEADER_NOT_COMPLETED; 63 | 64 | public void addListener(DoipTcpStreamBufferListener listener) { 65 | logger.trace(enter, ">>> public void addListener(DoipStreamBufferListener listener)"); 66 | this.listeners.add(listener); 67 | logger.trace(exit, "<<< public void addListener(DoipStreamBufferListener listener)"); 68 | } 69 | 70 | /** 71 | * Appends the new data to the internal buffer and starts to process the data in 72 | * the buffer. 73 | */ 74 | public void append(byte[] newData) { 75 | logger.trace(enter, ">>> void append(byte[] newData)"); 76 | 77 | logger.debug("Append " + newData.length + " bytes to the buffer"); 78 | super.append(newData); 79 | boolean ret = this.processBuffer(); 80 | while (ret) { 81 | logger.debug("After processing the buffer there are still " + this.getLength() 82 | + " bytes in the buffer which needs to get processed. Calling processBuffer() again."); 83 | ret = this.processBuffer(); 84 | } 85 | logger.trace(exit, "<<< void append(byte[] newData)"); 86 | } 87 | 88 | /** 89 | * Checks if the payload type is valid for a TCP message. 90 | * 91 | * @param payloadType The payload type 92 | * @return Returns true if the payload type is valid for a TCP message. Valid 93 | * payload types for UDP messages will also be declared as invalid 94 | * payload type. 95 | */ 96 | public static boolean isValidTcpPayloadType(int payloadType) { 97 | logger.trace(enter, ">>> public boolean checkPayloadType(int payloadType)"); 98 | boolean ret = false; 99 | if (payloadType == 0x0000 || payloadType == 0x0005 || payloadType == 0x0006 || payloadType == 0x0007 100 | || payloadType == 0x0008 || payloadType == 0x8001 || payloadType == 0x8002 || payloadType == 0x8003) { 101 | ret = true; 102 | } 103 | logger.trace(enter, ">>> public boolean checkPayloadType(int payloadType)"); 104 | return ret; 105 | } 106 | 107 | /** 108 | * This function will check if the payload length is correct. 109 | * 110 | * @return Returns true if the payload length is correct for the specific payload type. 111 | */ 112 | public boolean checkPayloadTypeSpecificLength() { 113 | logger.trace(">>> boolean checkPayloadTypeSpecificLength()"); 114 | boolean ret = false; 115 | switch (this.payloadType) { 116 | case DoipMessage.TYPE_HEADER_NACK: 117 | if (this.payloadLength == 1) 118 | ret = true; 119 | break; 120 | case DoipMessage.TYPE_TCP_ALIVE_REQ: 121 | if (this.payloadLength == 0) 122 | ret = true; 123 | break; 124 | case DoipMessage.TYPE_TCP_ALIVE_RES: 125 | if (this.payloadLength == 2) 126 | ret = true; 127 | break; 128 | case DoipMessage.TYPE_TCP_DIAG_MESSAGE: 129 | if (this.payloadLength >= 4) 130 | ret = true; 131 | break; 132 | case DoipMessage.TYPE_TCP_DIAG_MESSAGE_POS_ACK: 133 | if (this.payloadLength >= 5) 134 | ret = true; 135 | break; 136 | case DoipMessage.TYPE_TCP_DIAG_MESSAGE_NEG_ACK: 137 | if (this.payloadLength >= 5) 138 | ret = true; 139 | break; 140 | case DoipMessage.TYPE_TCP_ROUTING_REQ: 141 | if (this.payloadLength == 7 || this.payloadLength == 11) 142 | ret = true; 143 | break; 144 | case DoipMessage.TYPE_TCP_ROUTING_RES: 145 | if (this.payloadLength == 9 || this.payloadLength == 13) 146 | ret = true; 147 | break; 148 | default: 149 | break; 150 | } 151 | 152 | logger.trace("<<< boolean checkPayloadTypeSpecificLength()"); 153 | return ret; 154 | } 155 | 156 | /** 157 | * Checks if the first two bytes given by the parameter "data" contain a valid 158 | * sync pattern. 159 | * 160 | * @param data The data which will be checked. 161 | * @return Returns true if the first two bytes contain a valid sync pattern 162 | */ 163 | public static boolean checkSyncPattern(byte[] data) { 164 | logger.trace(">>> boolean checkSyncPattern(byte[] data)"); 165 | int protocolVersion = data[0] & 0xFF; 166 | int inverseProtocolVersion = data[1] & 0xFF; 167 | int xorProtocolVersion = protocolVersion ^ 0xFF; 168 | 169 | if (xorProtocolVersion != inverseProtocolVersion) { 170 | logger.info("Invalid sync pattern"); 171 | logger.trace("<<< boolean checkSyncPattern(byte[] data)"); 172 | return false; 173 | } 174 | 175 | logger.trace("<<< boolean checkSyncPattern(byte[] data)"); 176 | return true; 177 | } 178 | 179 | public long getMaxPayloadLength() { 180 | return maxPayloadLength; 181 | } 182 | 183 | public int getState() { 184 | return this.state; 185 | } 186 | 187 | /** 188 | * This function handles a TCP header according to figure 7 in section 7.1.2. 189 | * This function shall be called only in state STATE_HEADER_NOT_COMPLETE. 190 | */ 191 | public void handleTcpHeader() { 192 | logger.trace(">>> void handleTcpHeader()"); 193 | 194 | if (this.state != STATE_HEADER_NOT_COMPLETED) { 195 | throw new IllegalStateException( 196 | "void handleTcpHeader() had been called in illegal state STATE_HEADER_NOT_COMPLETE"); 197 | } 198 | 199 | byte[] data = this.getData(); 200 | 201 | logger.debug("Parse TCP message ..."); 202 | logger.debug("\tMessage Length = " + data.length); 203 | if (data.length < 8) { 204 | throw new IllegalStateException( 205 | "void handleTcpHeader() had been called, bute there are less than 8 bytes in the buffer"); 206 | } 207 | 208 | if (this.checkSyncPattern(data) == false) { 209 | logger.warn("Invalid sync pattern"); 210 | this.onHeaderIncorrectPatternFormat(); 211 | this.clear(); 212 | logger.trace("<<< void handleTcpHeader()"); 213 | return; 214 | } 215 | 216 | int high = data[2] & 0xFF; 217 | int low = data[3] & 0xFF; 218 | this.payloadType = (high << 8) | low; 219 | String text = DoipMessage.getPayloadTypeAsString(payloadType); 220 | if (text == null) 221 | text = "???"; 222 | logger.debug("\tPayload Type = " + String.format("0x%04X", payloadType) + ": " + text); 223 | 224 | long highhigh = data[4] & 0xFF; 225 | long highlow = data[5] & 0xFF; 226 | long lowhigh = data[6] & 0xFF; 227 | long lowlow = data[7] & 0xFF; 228 | this.payloadLength = (highhigh << 24) | (highlow << 16) | (lowhigh << 8) | lowlow; 229 | logger.debug("\tPayload Length in Header = " + payloadLength); 230 | 231 | if (isValidTcpPayloadType(payloadType) == false) { 232 | logger.warn("Invalid payload type"); 233 | this.onHeaderUnknownPayloadType(); 234 | this.state = STATE_SHREDDER_NOT_COMPLETED; 235 | this.numberOfRemainingBytesToShredder = this.payloadLength; 236 | this.lastHeader = this.remove(8); 237 | logger.trace("<<< void handleTcpHeader()"); 238 | return; 239 | } 240 | 241 | if (this.payloadLength > this.maxPayloadLength) { 242 | logger.warn("Payload length (= " + this.payloadLength + ") exceeds max payload length (= " 243 | + this.maxPayloadLength + ")"); 244 | this.onHeaderMessageTooLarge(); 245 | this.state = STATE_SHREDDER_NOT_COMPLETED; 246 | this.numberOfRemainingBytesToShredder = this.payloadLength; 247 | this.lastHeader = this.remove(8); 248 | logger.trace("<<< void handleTcpHeader()"); 249 | return; 250 | } 251 | 252 | /* 253 | * logger.debug("Check if payload length exceeds max memory size ..."); if 254 | * (this.payloadLength > this.maxMemorySize) { this.onHeaderOutOfMemory(); 255 | * this.state = STATE_SHREDDER_NOT_COMPLETED; this.numberOfBytesShreddered = 0; 256 | * this.lastHeader = this.remove(8); logger.trace("<<< void handleTcpHeader()"); 257 | * return; } 258 | */ 259 | 260 | if (this.checkPayloadTypeSpecificLength() == false) { 261 | logger.warn("Invalid payload length"); 262 | this.onHeaderInvalidPayloadLength(); 263 | this.clear(); 264 | logger.trace("<<< void handleTcpHeader()"); 265 | return; 266 | } 267 | 268 | // All checks had been performed, so now there is a valid header 269 | // in the buffer 270 | logger.debug("Remove header (8 bytes)"); 271 | this.lastHeader = this.remove(8); 272 | 273 | if (this.payloadLength == 0) { 274 | logger.info("Payload completed"); 275 | this.onPayloadCompleted(this.payloadType, new byte[0]); 276 | } else { 277 | this.state = STATE_PAYLOAD_NOT_COMPLETED; 278 | } 279 | 280 | logger.debug("After removing header there are " + this.getLength() + " bytes in the buffer"); 281 | logger.trace("<<< void handleTcpHeader()"); 282 | } 283 | 284 | private void onHeaderIncorrectPatternFormat() { 285 | logger.trace(">>> private void onHeaderIncorrectPatternFormat()"); 286 | Iterator iter = listeners.iterator(); 287 | while (iter.hasNext()) { 288 | DoipTcpStreamBufferListener listener = iter.next(); 289 | listener.onHeaderIncorrectPatternFormat(); 290 | } 291 | logger.trace("<<< private void onHeaderIncorrectPatternFormat()"); 292 | } 293 | 294 | private void onHeaderInvalidPayloadLength() { 295 | logger.trace("private void onHeaderInvalidPayloadLength()"); 296 | Iterator iter = listeners.iterator(); 297 | while (iter.hasNext()) { 298 | DoipTcpStreamBufferListener listener = iter.next(); 299 | listener.onHeaderInvalidPayloadLength(); 300 | } 301 | logger.trace("<<< private void onHeaderInvalidPayloadLength()"); 302 | 303 | } 304 | 305 | private void onHeaderMessageTooLarge() { 306 | logger.trace(">>> private void onHeaderMessageTooLarge()"); 307 | Iterator iter = listeners.iterator(); 308 | while (iter.hasNext()) { 309 | DoipTcpStreamBufferListener listener = iter.next(); 310 | listener.onHeaderMessageTooLarge(); 311 | } 312 | logger.trace("<<< private void onHeaderMessageTooLarge()"); 313 | } 314 | 315 | private void onHeaderUnknownPayloadType() { 316 | logger.trace(">>> private void onHeaderUnknownPayloadType()"); 317 | Iterator iter = listeners.iterator(); 318 | while (iter.hasNext()) { 319 | DoipTcpStreamBufferListener listener = iter.next(); 320 | listener.onHeaderUnknownPayloadType(); 321 | } 322 | logger.trace("private void onHeaderUnknownPayloadType()"); 323 | } 324 | 325 | private void onPayloadCompleted(int payloadType, byte[] data) { 326 | Iterator iter = listeners.iterator(); 327 | while (iter.hasNext()) { 328 | DoipTcpStreamBufferListener listener = iter.next(); 329 | listener.onPayloadCompleted(this.lastHeader, payloadType, data); 330 | } 331 | } 332 | 333 | 334 | /** 335 | * This function will process the existing data in the buffer. 336 | * 337 | * @return Returns true if buffer contains data which had not yet been 338 | * processed. In such a case the function processBuffer() needs to be 339 | * called again. 340 | */ 341 | public boolean processBuffer() { 342 | logger.trace(">>> boolean processBuffer()"); 343 | boolean ret = false; 344 | switch (state) { 345 | case STATE_HEADER_NOT_COMPLETED: 346 | logger.debug("Process buffer in state STATE_HEADER_NOT_COMPLETED"); 347 | ret = this.processBufferInStateHeaderNotCompleted(); 348 | break; 349 | case STATE_PAYLOAD_NOT_COMPLETED: 350 | logger.debug("Process buffer in state STATE_PAYLOAD_NOT_COMPLETED"); 351 | ret = this.processBufferInStatePayloadNotCompleted(); 352 | break; 353 | case STATE_SHREDDER_NOT_COMPLETED: 354 | logger.debug("Process buffer in state STATE_SHREDDER_NOT_COMPLETED"); 355 | ret = this.processBufferInStateShredderNotCompleted(); 356 | break; 357 | default: 358 | throw new IllegalStateException(); 359 | } 360 | logger.trace("<<< boolean processBuffer()"); 361 | return ret; 362 | } 363 | 364 | /** 365 | * Process the internal buffer in the state STATE_HEADER_NOT_COMPLETED 366 | * 367 | * @return Returns true if after calling this function there is still data in 368 | * the buffer which needs to be processed. 369 | */ 370 | public boolean processBufferInStateHeaderNotCompleted() { 371 | logger.trace(">>> boolean processBufferInStateHeaderNotCompleted()"); 372 | boolean ret = false; 373 | if (this.getLength() >= 8) { 374 | this.handleTcpHeader(); 375 | if (this.getLength() > 0) { 376 | ret = true; 377 | } 378 | } 379 | logger.trace("<<< boolean processBufferInStateHeaderNotCompleted()"); 380 | return ret; 381 | } 382 | 383 | public boolean processBufferInStatePayloadNotCompleted() { 384 | logger.trace(">>> boolean processBufferInStatePayloadNotCompleted()"); 385 | boolean ret = false; 386 | byte[] data = this.getData(); 387 | if (this.payloadLength == data.length) { 388 | this.clear(); 389 | this.onPayloadCompleted(this.payloadType, data); 390 | this.state = STATE_HEADER_NOT_COMPLETED; 391 | } else if (this.payloadLength < data.length) { 392 | byte[] payload = this.remove((int) this.payloadLength); 393 | this.onPayloadCompleted(payloadType, payload); 394 | this.state = STATE_HEADER_NOT_COMPLETED; 395 | ret = true; 396 | } 397 | logger.trace("<<< boolean processBufferInStatePayloadNotCompleted()"); 398 | return ret; 399 | } 400 | 401 | private void onShredderCompleted(long payloadLength) { 402 | Iterator iter = listeners.iterator(); 403 | while (iter.hasNext()) { 404 | DoipTcpStreamBufferListener listener = iter.next(); 405 | listener.onShredderCompleted(this.payloadLength); 406 | } 407 | } 408 | 409 | public boolean processBufferInStateShredderNotCompleted() { 410 | logger.trace(">>> boolean processBufferInStateShredderNotCompleted()"); 411 | 412 | // Get the current buffer 413 | byte[] data = this.getData(); 414 | 415 | if (data.length > this.numberOfRemainingBytesToShredder) { 416 | this.remove((int) this.numberOfRemainingBytesToShredder); 417 | this.numberOfRemainingBytesToShredder = 0; 418 | this.state = STATE_HEADER_NOT_COMPLETED; 419 | this.onShredderCompleted(this.payloadLength); 420 | logger.trace("<<< boolean processBufferInStateShredderNotCompleted()"); 421 | return true; 422 | } else if (data.length == this.numberOfRemainingBytesToShredder) { 423 | this.clear(); 424 | this.numberOfRemainingBytesToShredder = 0; 425 | this.state = STATE_HEADER_NOT_COMPLETED; 426 | this.onShredderCompleted(this.payloadLength); 427 | logger.trace("<<< boolean processBufferInStateShredderNotCompleted()"); 428 | return false; 429 | } else { 430 | // data.length < this.numberOfRemainingBytesToShredder 431 | this.clear(); 432 | this.numberOfRemainingBytesToShredder -= data.length; 433 | logger.trace("<<< boolean processBufferInStateShredderNotCompleted()"); 434 | return false; 435 | } 436 | } 437 | 438 | public void removeListener(DoipTcpStreamBufferListener listener) { 439 | logger.trace(">>> public void removeListener(DoipStreamBufferListener listener)"); 440 | this.listeners.remove(listener); 441 | logger.trace("<<< public void removeListener(DoipStreamBufferListener listener)"); 442 | } 443 | 444 | public void setMaxPayloadLength(long maxPayloadLength) { 445 | this.maxPayloadLength = maxPayloadLength; 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /src/main/java/doip/library/comm/DoipTcpStreamBufferListener.java: -------------------------------------------------------------------------------- 1 | package doip.library.comm; 2 | 3 | /** 4 | * Interface of a listener which will be called from the class DoipStreamBuffer. 5 | * A listener which implements this interface can register at the DoipStreamBuffer. 6 | * @author Marco Wehnert 7 | * 8 | */ 9 | public interface DoipTcpStreamBufferListener { 10 | /** 11 | * Will be called if the sync pattern in the DoIP header is invalid. 12 | */ 13 | public void onHeaderIncorrectPatternFormat(); 14 | /** 15 | * Will be called if the payload length in the DoIP header exceeds the 16 | * maximum message length. 17 | */ 18 | public void onHeaderMessageTooLarge(); 19 | 20 | /** 21 | * Will be called if the payload type in the header is unknown. 22 | * All payload types which only can occur in a UDP message are also declared as 23 | * invalid. 24 | */ 25 | public void onHeaderUnknownPayloadType(); 26 | 27 | /** 28 | * Will be called if the payload length is invalid, e.g. the payload length 29 | * does not match the expected payload length for the given payload type. 30 | */ 31 | public void onHeaderInvalidPayloadLength(); 32 | 33 | /** 34 | * Will be called if the payload had been completely received. 35 | * @param header The DoIP header of the received message. 36 | * @param payloadType The payload type of the received message. 37 | * @param payload The payload which had been received. 38 | */ 39 | public void onPayloadCompleted(byte[] header, int payloadType, byte[] payload); 40 | 41 | /** 42 | * Will be called when shredder has completed to shredder bytes. 43 | * @param payloadLength Number of bytes which had been shreddered. 44 | */ 45 | public void onShredderCompleted(long payloadLength); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/doip/library/comm/DoipUdpMessageHandlerListener.java: -------------------------------------------------------------------------------- 1 | package doip.library.comm; 2 | 3 | import java.net.DatagramPacket; 4 | 5 | import doip.library.message.DoipUdpDiagnosticPowerModeRequest; 6 | import doip.library.message.DoipUdpDiagnosticPowerModeResponse; 7 | import doip.library.message.DoipUdpEntityStatusRequest; 8 | import doip.library.message.DoipUdpEntityStatusResponse; 9 | import doip.library.message.DoipUdpHeaderNegAck; 10 | import doip.library.message.DoipUdpVehicleAnnouncementMessage; 11 | import doip.library.message.DoipUdpVehicleIdentRequest; 12 | import doip.library.message.DoipUdpVehicleIdentRequestWithEid; 13 | import doip.library.message.DoipUdpVehicleIdentRequestWithVin; 14 | 15 | public interface DoipUdpMessageHandlerListener { 16 | 17 | public void onDoipUdpVehicleIdentRequest(DoipUdpVehicleIdentRequest doipMessage, DatagramPacket packet); 18 | 19 | public void onDoipUdpVehicleIdentRequestWithEid(DoipUdpVehicleIdentRequestWithEid doipMessage, 20 | DatagramPacket packet); 21 | 22 | public void onDoipUdpVehicleIdentRequestWithVin(DoipUdpVehicleIdentRequestWithVin doipMessage, 23 | DatagramPacket packet); 24 | 25 | public void onDoipUdpVehicleAnnouncementMessage(DoipUdpVehicleAnnouncementMessage doipMessage, 26 | DatagramPacket packet); 27 | 28 | public void onDoipUdpDiagnosticPowerModeRequest(DoipUdpDiagnosticPowerModeRequest doipMessage, 29 | DatagramPacket packet); 30 | 31 | public void onDoipUdpDiagnosticPowerModeResponse(DoipUdpDiagnosticPowerModeResponse doipMessage, 32 | DatagramPacket packet); 33 | 34 | public void onDoipUdpEntityStatusRequest(DoipUdpEntityStatusRequest doipMessage, DatagramPacket packet); 35 | 36 | public void onDoipUdpEntityStatusResponse(DoipUdpEntityStatusResponse doipMessage, DatagramPacket packet); 37 | 38 | public void onDoipUdpHeaderNegAck(DoipUdpHeaderNegAck doipMessage, DatagramPacket packet); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/doip/library/comm/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The package contains modules for a DoIP communication. 3 | * These are modules which are specific for DoIP. 4 | */ 5 | package doip.library.comm; -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/DoipException.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | public class DoipException extends Exception { 4 | 5 | private static final long serialVersionUID = -1913952490830619880L; 6 | 7 | public DoipException(String string) { 8 | super(string); 9 | } 10 | 11 | public DoipException(Throwable cause) { 12 | super(cause); 13 | } 14 | 15 | public DoipException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/HeaderTooShort.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | @SuppressWarnings("serial") 4 | public class HeaderTooShort extends DoipException { 5 | 6 | public HeaderTooShort(String string) { 7 | super(string); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/IllegalNullArgument.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | /** 4 | * Convenience exception class which shall be thrown if the argument 5 | * of a method is null. 6 | */ 7 | public class IllegalNullArgument extends java.lang.IllegalArgumentException { 8 | 9 | private static final long serialVersionUID = 2712038578737726245L; 10 | 11 | public IllegalNullArgument(String argument, String method) { 12 | super("The argument <" + argument + "> in method <" + method + "> is null."); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/IncorrectPatternFormat.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | public class IncorrectPatternFormat extends DoipException { 4 | 5 | private static final long serialVersionUID = 2164899921754503791L; 6 | 7 | public IncorrectPatternFormat(String string) { 8 | super(string); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/InvalidPayloadLength.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | public class InvalidPayloadLength extends DoipException { 4 | 5 | private static final long serialVersionUID = -5986676154030113093L; 6 | 7 | public InvalidPayloadLength(String string) { 8 | super(string); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/InvalidPayloadType.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | public class InvalidPayloadType extends DoipException { 4 | 5 | private static final long serialVersionUID = 2075617465988131446L; 6 | 7 | public InvalidPayloadType(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/MessageNotComplete.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | @SuppressWarnings("serial") 4 | public class MessageNotComplete extends DoipException { 5 | 6 | public MessageNotComplete(String string) { 7 | super(string); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/MessageTooLarge.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | @SuppressWarnings("serial") 4 | public class MessageTooLarge extends DoipException { 5 | 6 | public MessageTooLarge(String string) { 7 | super(string); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/OutOfMemory.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | @SuppressWarnings("serial") 4 | public class OutOfMemory extends DoipException { 5 | 6 | public OutOfMemory(String string) { 7 | super(string); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/UnknownPayloadType.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | @SuppressWarnings("serial") 4 | public class UnknownPayloadType extends DoipException { 5 | 6 | public UnknownPayloadType(String string) { 7 | super(string); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/UnsupportedEncoding.java: -------------------------------------------------------------------------------- 1 | package doip.library.exception; 2 | 3 | public class UnsupportedEncoding extends DoipException { 4 | 5 | private static final long serialVersionUID = 5530439474054114347L; 6 | 7 | public UnsupportedEncoding(Throwable cause) { 8 | super(cause); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/doip/library/exception/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package contains all exception related to DoIP. 3 | * @author Marco Wehnert 4 | * 5 | */ 6 | package doip.library.exception; -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipHeaderNegAck.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | public interface DoipHeaderNegAck { 4 | 5 | public static final int NACK_INCORRECT_PATTERN_FORMAT = 0; 6 | public static final int NACK_UNKNOWN_PAYLOAD_TYPE = 1; 7 | public static final int NACK_MESSAGE_TOO_LARGE = 2; 8 | public static final int NACK_OUT_OF_MEMORY = 3; 9 | public static final int NACK_INVALID_PAYLOAD_LENGTH = 4; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipMessage.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | /** 8 | * Base class for all DoIP Messages 9 | * 10 | * @author Marco Wehnert 11 | * 12 | */ 13 | public abstract class DoipMessage { 14 | 15 | public final static int TYPE_HEADER_NACK = 0x0000; 16 | public final static int TYPE_UDP_VIR = 0x0001; 17 | public final static int TYPE_UDP_VIR_EID = 0x0002; 18 | public final static int TYPE_UDP_VIR_VIN = 0x0003; 19 | public final static int TYPE_UDP_VAM = 0x0004; 20 | public final static int TYPE_TCP_ROUTING_REQ = 0x0005; 21 | public final static int TYPE_TCP_ROUTING_RES = 0x0006; 22 | public final static int TYPE_TCP_ALIVE_REQ = 0x0007; 23 | public final static int TYPE_TCP_ALIVE_RES = 0x0008; 24 | 25 | public final static int TYPE_UDP_ENTITY_STATUS_REQ = 0x4001; 26 | public final static int TYPE_UDP_ENTITY_STATUS_RES = 0x4002; 27 | public final static int TYPE_UDP_DIAG_POWER_MODE_REQ = 0x4003; 28 | public final static int TYPE_UDP_DIAG_POWER_MODE_RES = 0x4004; 29 | 30 | public final static int TYPE_TCP_DIAG_MESSAGE = 0x8001; 31 | public final static int TYPE_TCP_DIAG_MESSAGE_POS_ACK = 0x8002; 32 | public final static int TYPE_TCP_DIAG_MESSAGE_NEG_ACK = 0x8003; 33 | 34 | private static Logger logger = LogManager.getLogger(DoipMessage.class); 35 | 36 | protected DoipMessage() { 37 | } 38 | 39 | public abstract byte[] getMessage(); 40 | 41 | public abstract void log(Level level); 42 | 43 | public abstract String getMessageName(); 44 | 45 | public static String getPayloadTypeAsString(int type) { 46 | switch (type) { 47 | case 0x0000: 48 | return "generic DoIP header negative acknowledge"; 49 | case 0x0001: 50 | return "vehicle identification request message"; 51 | case 0x0002: 52 | return "vehicle identification request message with EID"; 53 | case 0x0003: 54 | return "vehicle identification request message with VIN"; 55 | case 0x0004: 56 | return "vehicle announcement message"; 57 | case 0x0005: 58 | return "routing activation request"; 59 | case 0x0006: 60 | return "routing activation response"; 61 | case 0x0007: 62 | return "alive check request"; 63 | case 0x0008: 64 | return "alive check response"; 65 | case 0x4001: 66 | return "DoIP entity status request"; 67 | case 0x4002: 68 | return "DoIP entity status response"; 69 | case 0x4003: 70 | return "diagnostic power mode information request"; 71 | case 0x4004: 72 | return "diagnostic power mode information response"; 73 | case 0x8001: 74 | return "diagnostic message"; 75 | case 0x8002: 76 | return "diagnostic message positive acknowledgement"; 77 | case 0x8003: 78 | return "diagnostic message negative acknowledgement"; 79 | default: 80 | throw logger.throwing(Level.FATAL, new IllegalArgumentException("An invaid value has been passed to function DoipMessage.getPayoadTypeAsString(int type)")); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpAliveCheckRequest.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipTcpAliveCheckRequest extends DoipTcpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipTcpAliveCheckRequest.class); 10 | 11 | public DoipTcpAliveCheckRequest() { 12 | if (logger.isInfoEnabled()) { 13 | log(Level.INFO); 14 | } 15 | } 16 | 17 | public void log(Level level) { 18 | logger.log(level, "----------------------------------------"); 19 | logger.log(level, "DoIP alive check request"); 20 | logger.log(level, "----------------------------------------"); 21 | } 22 | 23 | public String getMessageName() { 24 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_ALIVE_REQ); 25 | } 26 | 27 | public static String getMessageNameOfClass() { 28 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_ALIVE_REQ); 29 | } 30 | 31 | @Override 32 | public void parsePayload(byte[] payload) { 33 | // Nothing to parse because there is no payload 34 | } 35 | 36 | @Override 37 | public byte[] getMessage() { 38 | byte[] message = new byte[] { 0x03, (byte) 0xFC, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00 }; 39 | return message; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpAliveCheckResponse.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipTcpAliveCheckResponse extends DoipTcpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipTcpAliveCheckResponse.class); 10 | 11 | private int sourceAddress = 0; 12 | 13 | private DoipTcpAliveCheckResponse() { 14 | } 15 | 16 | public DoipTcpAliveCheckResponse(int sourceAddress) { 17 | this.sourceAddress = sourceAddress; 18 | if (logger.isInfoEnabled()) { 19 | log(Level.INFO); 20 | } 21 | } 22 | 23 | public int getSourceAddress() { 24 | return sourceAddress; 25 | } 26 | 27 | public void setSourceAddress(int sourceAddress) { 28 | this.sourceAddress = sourceAddress; 29 | } 30 | 31 | 32 | public String getMessageName() { 33 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_ALIVE_RES); 34 | } 35 | 36 | public static String getMessageNameOfClass() { 37 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_ALIVE_RES); 38 | } 39 | 40 | public void log(Level level) { 41 | logger.log(level, "----------------------------------------"); 42 | logger.log(level, "DoIP alive check response:"); 43 | logger.log(level, " Source address = " + this.sourceAddress); 44 | logger.log(level, "----------------------------------------"); 45 | } 46 | 47 | public static DoipTcpAliveCheckResponse createInstance(byte[] payload) { 48 | DoipTcpAliveCheckResponse doipMessage = new DoipTcpAliveCheckResponse(); 49 | doipMessage.parsePayload(payload); 50 | doipMessage.log(Level.INFO); 51 | return doipMessage; 52 | } 53 | 54 | public void parsePayload(byte[] payload) { 55 | int high = payload[0] & 0xFF; 56 | int low = payload[1] & 0xFF; 57 | this.sourceAddress = (high << 8) | low; 58 | } 59 | 60 | @Override 61 | public byte[] getMessage() { 62 | byte[] message = new byte[] { 0x03, (byte) 0xFC, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00 }; 63 | message[8] = (byte) (sourceAddress >> 8); 64 | message[9] = (byte) sourceAddress; 65 | return message; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpDiagnosticMessage.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.apache.logging.log4j.Level; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | 9 | import doip.library.util.Conversion; 10 | 11 | public class DoipTcpDiagnosticMessage extends DoipTcpMessage { 12 | 13 | private Logger logger = LogManager.getLogger(DoipTcpDiagnosticMessage.class); 14 | 15 | private int sourceAddress = 0; 16 | private int targetAddress = 0; 17 | private byte[] diagnosticMessage = new byte[0]; 18 | 19 | private DoipTcpDiagnosticMessage() { 20 | } 21 | 22 | public DoipTcpDiagnosticMessage(int sourceAddress, int targetAddress, byte[] message) { 23 | this.sourceAddress = sourceAddress; 24 | this.targetAddress = targetAddress; 25 | this.diagnosticMessage = message; 26 | if (logger.isInfoEnabled()) { 27 | this.log(Level.INFO); 28 | } 29 | } 30 | 31 | public String getMessageName() { 32 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_DIAG_MESSAGE); 33 | } 34 | 35 | public static String getMessageNameOfClass() { 36 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_DIAG_MESSAGE); 37 | } 38 | 39 | public void log(Level level) { 40 | logger.log(level, "----------------------------------------"); 41 | logger.log(level, "DoIP diagnostic message:"); 42 | logger.log(level, " Source address = " + String.format("0x%04X", this.sourceAddress)); 43 | logger.log(level, " Target address = " + String.format("0x%04X", this.targetAddress)); 44 | logger.log(level, 45 | " Message = " + Conversion.byteArrayToHexStringShortDotted(this.diagnosticMessage, 64)); 46 | logger.log(level, "----------------------------------------"); 47 | } 48 | 49 | @Override 50 | public void parsePayload(byte[] payload) { 51 | int high = payload[0] & 0xFF; 52 | int low = payload[1] & 0xFF; 53 | this.sourceAddress = (high << 8) | low; 54 | high = payload[2] & 0xFF; 55 | low = payload[3] & 0xFF; 56 | this.targetAddress = (high << 8) | low; 57 | this.diagnosticMessage = Arrays.copyOfRange(payload, 4, payload.length); 58 | } 59 | 60 | public static DoipTcpDiagnosticMessage createInstance(byte[] payload) { 61 | DoipTcpDiagnosticMessage doipTcpDiagnosticMessage = new DoipTcpDiagnosticMessage(); 62 | doipTcpDiagnosticMessage.parsePayload(payload); 63 | doipTcpDiagnosticMessage.log(Level.INFO); 64 | return doipTcpDiagnosticMessage; 65 | } 66 | 67 | @Override 68 | public byte[] getMessage() { 69 | 70 | byte[] data = new byte[8 + 4 + diagnosticMessage.length]; 71 | data[0] = 0x03; 72 | data[1] = (byte) 0xFC; 73 | data[2] = (byte) 0x80; 74 | data[3] = 0x01; 75 | data[4] = (byte) ((diagnosticMessage.length + 4) >> 24); 76 | data[5] = (byte) ((diagnosticMessage.length + 4) >> 16); 77 | data[6] = (byte) ((diagnosticMessage.length + 4) >> 8); 78 | data[7] = (byte) ((diagnosticMessage.length + 4)); 79 | 80 | data[8] = (byte) (this.sourceAddress >> 8); 81 | data[9] = (byte) (this.sourceAddress); 82 | data[10] = (byte) (this.targetAddress >> 8); 83 | data[11] = (byte) (this.targetAddress); 84 | 85 | System.arraycopy(diagnosticMessage, 0, data, 12, diagnosticMessage.length); 86 | return data; 87 | } 88 | 89 | public int getSourceAddress() { 90 | return sourceAddress; 91 | } 92 | 93 | public void setSourceAddress(int sourceAddress) { 94 | this.sourceAddress = sourceAddress; 95 | } 96 | 97 | public int getTargetAddress() { 98 | return targetAddress; 99 | } 100 | 101 | public void setTargetAddress(int targetAddress) { 102 | this.targetAddress = targetAddress; 103 | } 104 | 105 | public byte[] getDiagnosticMessage() { 106 | return diagnosticMessage; 107 | } 108 | 109 | public void setDiagnosticMessage(byte[] diagnosticMessage) { 110 | this.diagnosticMessage = diagnosticMessage; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpDiagnosticMessageAck.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import java.util.Arrays; 4 | 5 | public abstract class DoipTcpDiagnosticMessageAck extends DoipTcpMessage { 6 | 7 | private int sourceAddress = 0; 8 | private int targetAddress = 0; 9 | private int ackCode = 0; 10 | 11 | private byte[] diagnosticMessage = new byte[0]; 12 | 13 | protected DoipTcpDiagnosticMessageAck() { 14 | } 15 | 16 | public DoipTcpDiagnosticMessageAck(int sourceAddress, int targetAddress, int ackCode, byte[] diagnosticMessage) { 17 | this.sourceAddress = sourceAddress; 18 | this.targetAddress = targetAddress; 19 | this.ackCode = ackCode; 20 | if (diagnosticMessage != null) { 21 | this.diagnosticMessage = diagnosticMessage; 22 | } 23 | } 24 | 25 | @Override 26 | public void parsePayload(byte[] payload) { 27 | int high = payload[0] & 0xFF; 28 | int low = payload[1] & 0xFF; 29 | this.sourceAddress = (high << 8) | low; 30 | high = payload[2] & 0xFF; 31 | low = payload[3] & 0xFF; 32 | this.targetAddress = (high << 8) | low; 33 | this.ackCode = payload[4] & 0xFF; 34 | if (payload.length > 5) { 35 | this.diagnosticMessage = Arrays.copyOfRange(payload, 5, payload.length); 36 | } else { 37 | this.diagnosticMessage = new byte[] {}; 38 | } 39 | } 40 | 41 | @Override 42 | public byte[] getMessage() { 43 | byte[] data = new byte[8 + 4 + 1 + diagnosticMessage.length]; 44 | data[0] = 0x03; 45 | data[1] = (byte) 0xFC; 46 | data[2] = (byte) 0x80; 47 | if (this.ackCode == 0) { 48 | data[3] = 0x02; 49 | } else { 50 | data[3] = 0x03; 51 | } 52 | data[4] = (byte) ((diagnosticMessage.length + 5) >> 24); 53 | data[5] = (byte) ((diagnosticMessage.length + 5) >> 16); 54 | data[6] = (byte) ((diagnosticMessage.length + 5) >> 8); 55 | data[7] = (byte) ((diagnosticMessage.length + 5)); 56 | 57 | data[8] = (byte) (this.sourceAddress >> 8); 58 | data[9] = (byte) (this.sourceAddress); 59 | data[10] = (byte) (this.targetAddress >> 8); 60 | data[11] = (byte) (this.targetAddress); 61 | data[12] = (byte) (this.ackCode); 62 | if (diagnosticMessage.length > 0) { 63 | System.arraycopy(diagnosticMessage, 0, data, 13, diagnosticMessage.length); 64 | } 65 | return data; 66 | } 67 | 68 | public int getSourceAddress() { 69 | return sourceAddress; 70 | } 71 | 72 | public void setSourceAddress(int sourceAddress) { 73 | this.sourceAddress = sourceAddress; 74 | } 75 | 76 | public int getTargetAddress() { 77 | return targetAddress; 78 | } 79 | 80 | public void setTargetAddress(int targetAddress) { 81 | this.targetAddress = targetAddress; 82 | } 83 | 84 | public int getAckCode() { 85 | return ackCode; 86 | } 87 | 88 | public void setAckCode(int ackCode) { 89 | this.ackCode = ackCode; 90 | } 91 | 92 | public byte[] getDiagnosticMessage() { 93 | return diagnosticMessage; 94 | } 95 | 96 | public void setDiagnosticMessage(byte[] diagnosticMessage) { 97 | if (diagnosticMessage == null) { 98 | this.diagnosticMessage = new byte[0]; 99 | } else { 100 | this.diagnosticMessage = diagnosticMessage; 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpDiagnosticMessageNegAck.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import doip.library.util.Conversion; 8 | 9 | public class DoipTcpDiagnosticMessageNegAck extends DoipTcpDiagnosticMessageAck { 10 | 11 | public static final int NACK_CODE_INVALID_SOURCE_ADDRESS = 0x02; 12 | public static final int NACK_CODE_UNKNOWN_TARGET_ADDRESS = 0x03; 13 | public static final int NACK_CODE_DIAGNOSTIC_MESSAGE_TOO_LARGE = 0x04; 14 | public static final int NACK_CODE_OUT_OF_MEMORY = 0x05; 15 | public static final int NACK_CODE_TARGET_UNREACHABLE = 0x06; 16 | public static final int NACK_CODE_UNKNOWN_NETWORK = 0x07; 17 | public static final int NACK_CODE_TRANSPORT_PROTOCOL_ERROR = 0x08; 18 | 19 | private Logger logger = LogManager.getLogger(DoipTcpDiagnosticMessageNegAck.class); 20 | 21 | private DoipTcpDiagnosticMessageNegAck() {} 22 | 23 | public DoipTcpDiagnosticMessageNegAck(int sourceAddress, int targetAddress, int ackCode, byte[] message) { 24 | super(sourceAddress, targetAddress, ackCode, message); 25 | log(Level.INFO); 26 | } 27 | 28 | public String getMessageName () { 29 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_DIAG_MESSAGE_NEG_ACK); 30 | } 31 | 32 | public static String getMessageNameOfClass() { 33 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_DIAG_MESSAGE_NEG_ACK); 34 | } 35 | 36 | public String getNackCodeAsString(int code) { 37 | switch (code) { 38 | case 0x02: 39 | return "0x02 (invalid source address)"; 40 | case 0x03: 41 | return "0x03 (unknown target address)"; 42 | case 0x04: 43 | return "0x04 (diagnostic message too large)"; 44 | case 0x05: 45 | return "0x05 (out of memory)"; 46 | case 0x06: 47 | return "0x06 (target unreachable)"; 48 | case 0x07: 49 | return "0x07 (unknown network)"; 50 | case 0x08: 51 | return "0x08 (transport protocol error)"; 52 | default: 53 | return String.format("%02X (???)", code); 54 | } 55 | } 56 | 57 | public void log(Level level) { 58 | logger.log(level, "----------------------------------------"); 59 | logger.log(level, "DoIP diagnostic message negative acknowledgement:"); 60 | logger.log(level, " Source address = " + this.getSourceAddress()); 61 | logger.log(level, " Target address = " + this.getTargetAddress()); 62 | logger.log(level, " NACK code = " + this.getNackCodeAsString(this.getAckCode())); 63 | logger.log(level, " Message = " + Conversion.byteArrayToHexStringShortDotted(this.getDiagnosticMessage(), 64)); 64 | logger.log(level, "----------------------------------------"); 65 | } 66 | 67 | public static DoipTcpDiagnosticMessageNegAck createInstance(byte[] payload) { 68 | DoipTcpDiagnosticMessageNegAck doipTcpDiagnosticMessageNegAck = new DoipTcpDiagnosticMessageNegAck(); 69 | doipTcpDiagnosticMessageNegAck.parsePayload(payload); 70 | doipTcpDiagnosticMessageNegAck.log(Level.INFO); 71 | return doipTcpDiagnosticMessageNegAck; 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpDiagnosticMessagePosAck.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import doip.library.util.Conversion; 8 | 9 | public class DoipTcpDiagnosticMessagePosAck extends DoipTcpDiagnosticMessageAck { 10 | 11 | private static Logger logger = LogManager.getLogger(DoipTcpDiagnosticMessagePosAck.class); 12 | 13 | private DoipTcpDiagnosticMessagePosAck() {} 14 | 15 | public DoipTcpDiagnosticMessagePosAck(int sourceAddress, int targetAddress, int ackCode, byte[] message) { 16 | super(sourceAddress, targetAddress, ackCode, message); 17 | log(Level.INFO); 18 | } 19 | 20 | public String getMessageName() { 21 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_DIAG_MESSAGE_POS_ACK); 22 | } 23 | 24 | public static String getMessageNameOfClass() { 25 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_DIAG_MESSAGE_POS_ACK); 26 | } 27 | 28 | public void log(Level level) { 29 | logger.log(level, "----------------------------------------"); 30 | logger.log(level, "DoIP diagnostic message positive acknowledgement:"); 31 | logger.log(level, " Source address = " + this.getSourceAddress()); 32 | logger.log(level, " Target address = " + this.getTargetAddress()); 33 | logger.log(level, " ACK code = " + this.getAckCode()); 34 | logger.log(level, " Message = " + Conversion.byteArrayToHexStringShortDotted(this.getDiagnosticMessage(), 64)); 35 | logger.log(level, "----------------------------------------"); 36 | } 37 | 38 | 39 | public static DoipTcpDiagnosticMessagePosAck createInstance(byte[] payload) { 40 | DoipTcpDiagnosticMessagePosAck doipTcpDiagnosticMessagePosAck = new DoipTcpDiagnosticMessagePosAck(); 41 | doipTcpDiagnosticMessagePosAck.parsePayload(payload); 42 | doipTcpDiagnosticMessagePosAck.log(Level.INFO); 43 | return doipTcpDiagnosticMessagePosAck; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpHeaderNegAck.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipTcpHeaderNegAck extends DoipTcpMessage implements DoipHeaderNegAck { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipTcpHeaderNegAck.class); 10 | 11 | private int code = -1; 12 | 13 | private DoipTcpHeaderNegAck() { 14 | } 15 | 16 | public DoipTcpHeaderNegAck(int code) { 17 | this.code = code; 18 | this.log(Level.INFO); 19 | } 20 | 21 | public static DoipTcpHeaderNegAck createInstance(byte[] payload) { 22 | DoipTcpHeaderNegAck doipHeaderNegAck = new DoipTcpHeaderNegAck(); 23 | doipHeaderNegAck.parsePayload(payload); 24 | doipHeaderNegAck.log(Level.INFO); 25 | return doipHeaderNegAck; 26 | } 27 | 28 | public String getMessageName() { 29 | return getPayloadTypeAsString(DoipMessage.TYPE_HEADER_NACK); 30 | } 31 | 32 | public static String getMessageNameOfClass() { 33 | return getPayloadTypeAsString(DoipMessage.TYPE_HEADER_NACK); 34 | } 35 | 36 | public void log(Level level) { 37 | logger.log(level, "----------------------------------------"); 38 | logger.log(level, "DoIP header negative acknowledgement (TCP):"); 39 | logger.log(level, " Code = " + String.format("0x%02X (", code) + getCodeAsString() + ")"); 40 | logger.log(level, "----------------------------------------"); 41 | } 42 | 43 | public String getCodeAsString() { 44 | switch (code) { 45 | case 0x00: 46 | return "incorrect pattern format"; 47 | case 0x01: 48 | return "unknown payload type"; 49 | case 0x02: 50 | return "message too large"; 51 | case 0x03: 52 | return "out of memory"; 53 | case 0x04: 54 | return "invalid payload length"; 55 | default: 56 | return "reserved by this document"; 57 | } 58 | } 59 | 60 | public int getCode() { 61 | return code; 62 | } 63 | 64 | @Override 65 | public byte[] getMessage() { 66 | byte[] message = new byte[] { 0x03, (byte) 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; 67 | message[8] = (byte) (code & 0xFF); 68 | return message; 69 | } 70 | 71 | public void parsePayload(byte[] payload) { 72 | this.code = payload[0] & 0xFF; 73 | } 74 | 75 | public void setCode(int code) { 76 | this.code = code; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpMessage.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | public abstract class DoipTcpMessage extends DoipMessage { 4 | 5 | public abstract void parsePayload(byte[] payload); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpRoutingActivationRequest.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipTcpRoutingActivationRequest extends DoipTcpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipTcpRoutingActivationRequest.class); 10 | 11 | int sourceAddress = 0; 12 | int activationType = 0; 13 | long oemData = -1; // -1 = No data is there 14 | 15 | public DoipTcpRoutingActivationRequest(int sourceAddress, int activationType, long oemData) { 16 | this.sourceAddress = sourceAddress; 17 | this.activationType = activationType; 18 | this.oemData = oemData; 19 | this.log(Level.INFO); 20 | } 21 | 22 | private DoipTcpRoutingActivationRequest() { 23 | } 24 | 25 | public String getMessageName() { 26 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_ROUTING_REQ); 27 | } 28 | 29 | public static String getMessageNameOfClass() { 30 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_ROUTING_REQ); 31 | } 32 | 33 | public void log(Level level) { 34 | logger.log(level, "----------------------------------------"); 35 | logger.log(level, "DoIP routing activation request:"); 36 | logger.log(level, " Source address = " + this.sourceAddress); 37 | logger.log(level, " Activation Type = " + this.activationType); 38 | logger.log(level, " OEM data = " + this.getOemData()); 39 | logger.log(level, "----------------------------------------"); 40 | } 41 | 42 | @Override 43 | public void parsePayload(byte[] payload) { 44 | int high = payload[0] & 0xFF; 45 | int low = payload[1] & 0xFF; 46 | this.sourceAddress = (high << 8) | low; 47 | this.activationType = payload[2] & 0xFF; 48 | if (payload.length == 11) { 49 | long highhigh = payload[3] & 0xFF; 50 | long highlow = payload[4] & 0xFF; 51 | long lowhigh = payload[5] & 0xFF; 52 | long lowlow = payload[6] & 0xFF; 53 | this.oemData = (highhigh << 24) | (highlow << 16) | (lowhigh << 8) | lowlow; 54 | } 55 | } 56 | 57 | public static DoipTcpRoutingActivationRequest createInstance(byte[] payload) { 58 | DoipTcpRoutingActivationRequest doipMessage = new DoipTcpRoutingActivationRequest(); 59 | doipMessage.parsePayload(payload); 60 | doipMessage.log(Level.INFO); 61 | return doipMessage; 62 | } 63 | 64 | @Override 65 | public byte[] getMessage() { 66 | byte[] message; 67 | if (oemData == -1) { 68 | message = new byte[8 + 7]; 69 | message[7] = 7; 70 | } else { 71 | message = new byte[8 + 11]; 72 | message[7] = 11; 73 | message[15] = (byte) (this.oemData >> 24); 74 | message[16] = (byte) (this.oemData >> 16); 75 | message[17] = (byte) (this.oemData >> 8); 76 | message[18] = (byte) (this.oemData); 77 | } 78 | 79 | message[0] = 0x03; 80 | message[1] = (byte) 0xFC; 81 | message[2] = 0x00; 82 | message[3] = 0x05; 83 | message[4] = 0x00; 84 | message[5] = 0x00; 85 | message[6] = 0x00; 86 | 87 | message[8] = (byte) (this.sourceAddress >> 8); 88 | message[9] = (byte) (this.sourceAddress); 89 | message[10] = (byte) this.activationType; 90 | 91 | message[11] = 0x00; 92 | message[12] = 0x00; 93 | message[13] = 0x00; 94 | message[14] = 0x00; 95 | return message; 96 | } 97 | 98 | public int getSourceAddress() { 99 | return sourceAddress; 100 | } 101 | 102 | public void setSourceAddress(int sourceAddress) { 103 | this.sourceAddress = sourceAddress; 104 | } 105 | 106 | public int getActivationType() { 107 | return activationType; 108 | } 109 | 110 | public void setActivationType(int activationType) { 111 | this.activationType = activationType; 112 | } 113 | 114 | public long getOemData() { 115 | return oemData; 116 | } 117 | 118 | public void setOemData(long oemData) { 119 | this.oemData = oemData; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipTcpRoutingActivationResponse.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipTcpRoutingActivationResponse extends DoipTcpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipTcpRoutingActivationResponse.class); 10 | 11 | int testerAddress = 0; 12 | int entityAddress = 0; 13 | int responseCode = 0; 14 | long oemData = -1; 15 | 16 | private DoipTcpRoutingActivationResponse() {} 17 | 18 | public DoipTcpRoutingActivationResponse(int testerAddress, int entityAddress, int responseCode, long oemData) { 19 | this.testerAddress = testerAddress; 20 | this.entityAddress = entityAddress; 21 | this.responseCode = responseCode; 22 | this.oemData = oemData; 23 | this.log(Level.INFO); 24 | } 25 | 26 | public String getMessageName() { 27 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_ROUTING_RES); 28 | } 29 | 30 | public static String getMessageNameOfClass() { 31 | return getPayloadTypeAsString(DoipMessage.TYPE_TCP_ROUTING_RES); 32 | } 33 | 34 | public String getResponseCodeAsString(int code) { 35 | switch (code) { 36 | case 0x00: 37 | return "0x00 (routing activation denied due to unknown source address"; 38 | case 0x01: 39 | return "0x01 (routing activation denied because all concurrently supported TCP_DATA sockets are registered and active)"; 40 | case 0x02: 41 | return "0x02 (routing activation denied because an SA different from the table connection entry was received on the already activated TCP_DATA socket)"; 42 | case 0x03: 43 | return "0x03 (routing activation denied because the SA is already registered and active on a different TCP_DATA socket)"; 44 | case 0x04: 45 | return "0x04 (routing activation denied due to missing authentication)"; 46 | case 0x05: 47 | return "0x05 (routing activation denied due to rejected confirmation)"; 48 | case 0x06: 49 | return "0x06 (routing activation denied due to unsupported routing activation type)"; 50 | case 0x10: 51 | return "0x10 (routing successful activated)"; 52 | case 0x11: 53 | return "0x11 (routing will be activated; confirmation required)"; 54 | default: 55 | return String.format("0x02X (???)", code); 56 | 57 | } 58 | } 59 | 60 | 61 | 62 | public void log(Level level) { 63 | logger.log(level, "----------------------------------------"); 64 | logger.log(level, "DoIP routing activation response:"); 65 | logger.log(level, " Tester address = " + this.testerAddress); 66 | logger.log(level, " Entity address = " + this.entityAddress); 67 | logger.log(level, " Response code = " + this.getResponseCodeAsString(this.responseCode)); 68 | if (this.oemData == -1) { 69 | logger.log(level, " OEM data = n/a"); 70 | } else { 71 | logger.log(level, " OEM data = " + this.oemData); 72 | } 73 | logger.log(level, "----------------------------------------"); 74 | } 75 | 76 | @Override 77 | public void parsePayload(byte[] payload) { 78 | int high = payload[0] & 0xFF; 79 | int low = payload[1] & 0xFF; 80 | this.testerAddress = (high << 8) | low; 81 | high = payload[2] & 0xFF; 82 | low = payload[3] & 0xFF; 83 | this.entityAddress = (high << 8) | low; 84 | this.responseCode = payload[4] & 0xFF; 85 | // TODO: parse oemData 86 | } 87 | 88 | public static DoipTcpRoutingActivationResponse createInstance(byte[] payload) { 89 | DoipTcpRoutingActivationResponse doipTcpRoutingActivationResponse = new DoipTcpRoutingActivationResponse(); 90 | doipTcpRoutingActivationResponse.parsePayload(payload); 91 | doipTcpRoutingActivationResponse.log(Level.INFO); 92 | return doipTcpRoutingActivationResponse; 93 | } 94 | 95 | @Override 96 | public byte[] getMessage() { 97 | byte[] msg = null; 98 | if (oemData == -1) { 99 | msg = new byte[8 + 9]; 100 | msg[7] = 9; 101 | } else { 102 | msg = new byte[8 + 13]; 103 | msg[7] = 13; 104 | msg[17] = (byte) (this.oemData >> 24); 105 | msg[18] = (byte) (this.oemData >> 16); 106 | msg[19] = (byte) (this.oemData >> 8); 107 | msg[20] = (byte) (this.oemData); 108 | } 109 | msg[0] = 0x03; 110 | msg[1] = (byte) 0xFC; 111 | msg[2] = 0x00; 112 | msg[3] = 0x06; 113 | msg[4] = 0x00; 114 | msg[5] = 0x00; 115 | msg[6] = 0x00; 116 | // msg[7] already set above 117 | 118 | msg[8] = (byte) (this.testerAddress >> 8); 119 | msg[9] = (byte) (this.testerAddress); 120 | msg[10] = (byte) (this.entityAddress >> 8); 121 | msg[11] = (byte) (this.entityAddress); 122 | 123 | msg[12] = (byte) (this.responseCode); 124 | 125 | msg[13] = 0x00; 126 | msg[14] = 0x00; 127 | msg[15] = 0x00; 128 | msg[16] = 0x00; 129 | 130 | return msg; 131 | } 132 | 133 | public int getTesterAddress() { 134 | return testerAddress; 135 | } 136 | 137 | public void setTesterAddress(int testerAddress) { 138 | this.testerAddress = testerAddress; 139 | } 140 | 141 | public int getEntityAddress() { 142 | return entityAddress; 143 | } 144 | 145 | public void setEntityAddress(int entityAddress) { 146 | this.entityAddress = entityAddress; 147 | } 148 | 149 | public int getResponseCode() { 150 | return responseCode; 151 | } 152 | 153 | public void setResponseCode(int responseCode) { 154 | this.responseCode = responseCode; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpDiagnosticPowerModeRequest.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipUdpDiagnosticPowerModeRequest extends DoipUdpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipUdpDiagnosticPowerModeRequest.class); 10 | 11 | public DoipUdpDiagnosticPowerModeRequest() { 12 | this.log(Level.INFO); 13 | } 14 | 15 | public void log(Level level) { 16 | logger.log(level, "----------------------------------------"); 17 | logger.log(level, "DoIP diagnostic power mode request."); 18 | logger.log(level, "----------------------------------------"); 19 | } 20 | 21 | public String getMessageName() { 22 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_DIAG_POWER_MODE_REQ); 23 | } 24 | 25 | public static String getMessageNameOfClass() { 26 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_DIAG_POWER_MODE_REQ); 27 | } 28 | 29 | @Override 30 | public byte[] getMessage() { 31 | byte[] message = new byte[] {0x03, (byte)0xFC, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00}; 32 | return message; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpDiagnosticPowerModeResponse.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | import org.apache.logging.log4j.Level; 6 | 7 | public class DoipUdpDiagnosticPowerModeResponse extends DoipUdpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipUdpDiagnosticPowerModeResponse.class); 10 | 11 | int diagnsoticPowerMode = 0; 12 | 13 | public DoipUdpDiagnosticPowerModeResponse(int diagnosticPowerMode) { 14 | this.diagnsoticPowerMode = diagnosticPowerMode; 15 | } 16 | 17 | public String getMessageName() { 18 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_DIAG_POWER_MODE_RES); 19 | } 20 | 21 | public static String getMessageNameOfClass() { 22 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_DIAG_POWER_MODE_RES); 23 | } 24 | 25 | public void log(Level level) { 26 | logger.log(level, "----------------------------------------"); 27 | logger.log(level, "DoIP diagnostic power mode response:"); 28 | logger.log(level, " Diagnostic power mode = " + this.diagnsoticPowerMode); 29 | logger.log(level, "----------------------------------------"); 30 | } 31 | 32 | @Override 33 | public byte[] getMessage() { 34 | byte[] message = new byte[] { 0x03, (byte) 0xFC, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00 }; 35 | message[8] = (byte) this.diagnsoticPowerMode; 36 | return message; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpEntityStatusRequest.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipUdpEntityStatusRequest extends DoipUdpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipUdpEntityStatusRequest.class); 10 | 11 | public DoipUdpEntityStatusRequest() { 12 | this.log(Level.INFO); 13 | } 14 | 15 | public void log(Level level) { 16 | logger.log(level, "----------------------------------------"); 17 | logger.log(level, "DoIP entity status request."); 18 | logger.log(level, "----------------------------------------"); 19 | } 20 | 21 | public String getMessageName() { 22 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_ENTITY_STATUS_REQ); 23 | } 24 | 25 | public static String getMessageNameOfClass() { 26 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_ENTITY_STATUS_REQ); 27 | } 28 | 29 | @Override 30 | public byte[] getMessage() { 31 | byte[] msg = new byte[] {0x03, (byte) 0xFC, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00}; 32 | return msg; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpEntityStatusResponse.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipUdpEntityStatusResponse extends DoipUdpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipUdpEntityStatusResponse.class); 10 | 11 | private int nodeType = 0; 12 | 13 | private int maxNumberOfSockets = 0; 14 | 15 | private int currentNumberOfSockets = 0; 16 | 17 | private long maxDataSize = 0; 18 | 19 | public DoipUdpEntityStatusResponse(int nodeType, int maxNumberOfSockets, int currentNumberOfSockets, 20 | long maxDataSize) { 21 | this.nodeType = nodeType; 22 | this.maxNumberOfSockets = maxNumberOfSockets; 23 | this.currentNumberOfSockets = currentNumberOfSockets; 24 | this.maxDataSize = maxDataSize; 25 | this.log(Level.INFO); 26 | } 27 | 28 | public String getMessageName() { 29 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_ENTITY_STATUS_RES); 30 | } 31 | 32 | public static String getMessageNameOfClass() { 33 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_ENTITY_STATUS_RES); 34 | } 35 | 36 | public void log(Level level) { 37 | logger.log(level, "----------------------------------------"); 38 | logger.log(level, "DoIP entity status response:"); 39 | logger.log(level, " Node type = " + this.nodeType); 40 | logger.log(level, " Maximum number of sockets = " + this.maxNumberOfSockets); 41 | logger.log(level, " Current number of sockets = " + this.currentNumberOfSockets); 42 | logger.log(level, " Maximum data size = " + this.maxDataSize); 43 | logger.log(level, "----------------------------------------"); 44 | } 45 | 46 | @Override 47 | public byte[] getMessage() { 48 | byte[] msg = new byte[15]; 49 | msg[0] = 0x03; 50 | msg[1] = (byte) 0xFC; 51 | msg[2] = 0x40; 52 | msg[3] = 0x02; 53 | msg[4] = 0x00; 54 | msg[5] = 0x00; 55 | msg[6] = 0x00; 56 | msg[7] = 0x07; 57 | 58 | msg[8] = (byte)this.nodeType; 59 | msg[9] = (byte)this.maxNumberOfSockets; 60 | msg[10] = (byte)this.currentNumberOfSockets; 61 | 62 | msg[11] = (byte)(this.maxDataSize >> 24); 63 | msg[12] = (byte)(this.maxDataSize >> 16); 64 | msg[13] = (byte)(this.maxDataSize >> 8); 65 | msg[14] = (byte)(this.maxDataSize); 66 | return msg; 67 | } 68 | 69 | public int getNodeType() { 70 | return nodeType; 71 | } 72 | 73 | public void setNodeType(int nodeType) { 74 | this.nodeType = nodeType; 75 | } 76 | 77 | public int getMaxNumberOfSockets() { 78 | return maxNumberOfSockets; 79 | } 80 | 81 | public void setMaxNumberOfSockets(int maxNumberOfSockets) { 82 | this.maxNumberOfSockets = maxNumberOfSockets; 83 | } 84 | 85 | public int getCurrentNumberOfSockets() { 86 | return currentNumberOfSockets; 87 | } 88 | 89 | public void setCurrentNumberOfSockets(int currentNumberOfSockets) { 90 | this.currentNumberOfSockets = currentNumberOfSockets; 91 | } 92 | 93 | public long getMaxDataSize() { 94 | return maxDataSize; 95 | } 96 | 97 | public void setMaxDataSize(long maxDataSize) { 98 | this.maxDataSize = maxDataSize; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpHeaderNegAck.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipUdpHeaderNegAck extends DoipUdpMessage implements DoipHeaderNegAck { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipUdpHeaderNegAck.class); 10 | private int code = -1; 11 | 12 | public DoipUdpHeaderNegAck(int code) { 13 | this.code = code; 14 | this.log(Level.INFO); 15 | } 16 | 17 | public int getCode() { 18 | return code; 19 | } 20 | 21 | public void setCode(int code) { 22 | this.code = code; 23 | } 24 | 25 | public String getMessageName() { 26 | return getPayloadTypeAsString(DoipMessage.TYPE_HEADER_NACK); 27 | } 28 | 29 | public static String getMessageNameOfClass() { 30 | return getPayloadTypeAsString(DoipMessage.TYPE_HEADER_NACK); 31 | } 32 | 33 | public void log(Level level) { 34 | logger.log(level, "----------------------------------------"); 35 | logger.log(level, "DoIP header negative acknowledgement (UDP):"); 36 | logger.log(level, " Code = " + String.format("0x%02X (", code) + getCodeAsString() + ")"); 37 | logger.log(level, "----------------------------------------"); 38 | } 39 | 40 | public String getCodeAsString() { 41 | switch (code) { 42 | case 0x00: 43 | return "incorrect pattern format"; 44 | case 0x01: 45 | return "unknown payload type"; 46 | case 0x02: 47 | return "message too large"; 48 | case 0x03: 49 | return "out of memory"; 50 | case 0x04: 51 | return "invalid payload length"; 52 | default: 53 | return "reserved by this document"; 54 | } 55 | } 56 | 57 | @Override 58 | public byte[] getMessage() { 59 | byte[] message = new byte[] { 0x03, (byte) 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; 60 | message[8] = (byte) (code & 0xFF); 61 | return message; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpMessage.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import doip.library.exception.HeaderTooShort; 7 | import doip.library.exception.IncorrectPatternFormat; 8 | import doip.library.exception.InvalidPayloadLength; 9 | import doip.library.exception.InvalidPayloadType; 10 | 11 | public abstract class DoipUdpMessage extends DoipMessage { 12 | 13 | private static Logger logger = LogManager.getLogger(DoipUdpMessage.class); 14 | 15 | public static DoipUdpMessage parseUDP(byte[] data) 16 | throws HeaderTooShort, IncorrectPatternFormat, InvalidPayloadLength, InvalidPayloadType { 17 | if (logger.isTraceEnabled()) { 18 | logger.trace(">>> DoipMessage parseUDP(byte[] data)"); 19 | } 20 | 21 | if (logger.isDebugEnabled()) { 22 | logger.debug("Parse UDP message ..."); 23 | logger.debug("\tMessage Length = " + data.length); 24 | } 25 | // Check header length 26 | if (data.length < 8) { 27 | logger.trace("<<< DoipMessage parseUDP(byte[] data) return with HeaderTooShort"); 28 | throw new HeaderTooShort("DoIP UDP message too short for interpretation"); 29 | } 30 | 31 | int protocolVersion = data[0] & 0xFF; 32 | int inverseProtocolVersion = data[1] & 0xFF; 33 | int xorProtocolVersion = protocolVersion ^ 0xFF; 34 | 35 | if (xorProtocolVersion != inverseProtocolVersion) { 36 | logger.trace("<<< DoipMessage parseUDP(byte[] data) return with IncorrectPatternFormat"); 37 | String text = "The inverse protocol version (second byte of a" 38 | + " DoIP message) doesn't match the protocol version" 39 | + " (first byte of a DoIP message) XOR 0xFF;" 40 | + " protocol version = " 41 | + String.format("0x%02X", protocolVersion) 42 | + ", inverse protocol version = " 43 | + String.format("0x%02X", inverseProtocolVersion) 44 | + "."; 45 | throw new IncorrectPatternFormat(text); 46 | } 47 | 48 | int high = data[2] & 0xFF; 49 | int low = data[3] & 0xFF; 50 | int payloadType = (high << 8) | low; 51 | String text = getPayloadTypeAsString(payloadType); 52 | if (text == null) 53 | text = "???"; 54 | logger.debug("\tPayload Type = " + String.format("0x%04X", payloadType) + ": " + text); 55 | 56 | long highhigh = data[4] & 0xFF; 57 | long highlow = data[5] & 0xFF; 58 | long lowhigh = data[6] & 0xFF; 59 | long lowlow = data[7] & 0xFF; 60 | long payloadLength = (highhigh << 24) | (highlow << 16) | (lowhigh << 8) | lowlow; 61 | 62 | logger.debug("\tPayload Length in Header = " + payloadLength); 63 | 64 | switch (payloadType) { 65 | //--------------------------------------------------------- 66 | case 0x0000: // Header NACK; TCP or UDP 67 | //--------------------------------------------------------- 68 | checkPayloadLength(1, payloadLength, data.length); 69 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 70 | return new DoipUdpHeaderNegAck(data[8] & 0xFF); 71 | 72 | //--------------------------------------------------------- 73 | case 0x0001: // VIR; UDP 74 | //--------------------------------------------------------- 75 | checkPayloadLength(0, payloadLength, data.length); 76 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 77 | return new DoipUdpVehicleIdentRequest(); 78 | 79 | //--------------------------------------------------------- 80 | case 0x0002: // VIR with EID; UDP 81 | //--------------------------------------------------------- 82 | checkPayloadLength(6, payloadLength, data.length); 83 | 84 | // EID 85 | byte[] eid = new byte[6]; 86 | System.arraycopy(data, 8, eid, 0, 6); 87 | 88 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 89 | return new DoipUdpVehicleIdentRequestWithEid(eid); 90 | 91 | //--------------------------------------------------------- 92 | case 0x0003: // VIR with VIN; UDP 93 | //--------------------------------------------------------- 94 | checkPayloadLength(17, payloadLength, data.length); 95 | 96 | // VIN 97 | byte[] vin = new byte[17]; 98 | System.arraycopy(data, 8, vin, 0, 17); 99 | 100 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 101 | return new DoipUdpVehicleIdentRequestWithVin(vin); 102 | 103 | //--------------------------------------------------------- 104 | case 0x0004: // VAM; UDP 105 | //--------------------------------------------------------- 106 | /* The last byte could be optional, therefore we 107 | use specific method to check the payload length */ 108 | checkPayloadLength_0x0004_VAM(payloadLength, data.length); 109 | 110 | // VIN 111 | vin = new byte[17]; 112 | System.arraycopy(data, 8, vin, 0, 17); 113 | 114 | // Logical Address 115 | high = data[25] & 0xFF; 116 | low = data[26] & 0xFF; 117 | int logicalAddress = (high << 8) + low; 118 | 119 | // EID 120 | eid = new byte[6]; 121 | System.arraycopy(data, 27, eid, 0, 6); 122 | 123 | // GID 124 | byte[] gid = new byte[6]; 125 | System.arraycopy(data, 33, gid, 0, 6); 126 | 127 | // Further Action Required 128 | int furtherActionRequired = data[39] & 0xFF; 129 | 130 | // Sync Status 131 | int syncStatus; 132 | if (payloadLength == 33) { 133 | syncStatus = (data[40] & 0xFF); 134 | } else { 135 | syncStatus = -1; 136 | } 137 | 138 | 139 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 140 | return new DoipUdpVehicleAnnouncementMessage(vin, logicalAddress, eid, gid, furtherActionRequired, 141 | syncStatus); 142 | 143 | //--------------------------------------------------------- 144 | case 0x4001: // Entity status request; UDP 145 | //--------------------------------------------------------- 146 | checkPayloadLength(0, payloadLength, data.length); 147 | 148 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 149 | return new DoipUdpEntityStatusRequest(); 150 | 151 | //--------------------------------------------------------- 152 | case 0x4002: // Entity status response; UDP 153 | //--------------------------------------------------------- 154 | checkPayloadLength(7, payloadLength, data.length); 155 | 156 | // Node Type 157 | byte nodeType = data[8]; 158 | 159 | // Max Number Of Sockets 160 | int maxNumberOfSockets = data[9] & 0xFF; 161 | 162 | // Current Number Of Sockets 163 | int currentNumberOfSockets = data[10] & 0xFF; 164 | 165 | // Max Data Size 166 | int maxDataSize = (data[11] << 24) | (data[12] << 16) | (data[13] << 8) | data[14]; 167 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 168 | return new DoipUdpEntityStatusResponse(nodeType, maxNumberOfSockets, currentNumberOfSockets, maxDataSize); 169 | 170 | //--------------------------------------------------------- 171 | case 0x4003: // Diag power mode request; UDP 172 | //--------------------------------------------------------- 173 | checkPayloadLength(0, payloadLength, data.length); 174 | 175 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 176 | return new DoipUdpDiagnosticPowerModeRequest(); 177 | 178 | //--------------------------------------------------------- 179 | case 0x4004: // Diag power mode response; UDP 180 | //--------------------------------------------------------- 181 | checkPayloadLength(1, payloadLength, data.length); 182 | 183 | byte diagPowerMode = data[8]; 184 | 185 | logger.trace("<<< DoipMessage parseUDP(byte[] data)"); 186 | return new DoipUdpDiagnosticPowerModeResponse(diagPowerMode); 187 | default: 188 | logger.trace("<<< DoipMessage parseUDP(byte[] data) return with InvalidPayloadType"); 189 | text = "The payload type " + String.format("0x%02X", payloadType) + " doesn't have a valid value"; 190 | throw new InvalidPayloadType(text); 191 | } 192 | } 193 | 194 | /** 195 | * Checks the length of the payload. 196 | * 197 | * @param expectedLength The expected length of the payload 198 | * @param payloadLength The lenght of the payload given in the DoIP header 199 | * @param dataLength The length of the DoIP message 200 | * @throws InvalidPayloadLength If payload length is invalid an exception will 201 | * be thrown. 202 | */ 203 | private static void checkPayloadLength(long expectedLength, long payloadLength, long dataLength) 204 | throws InvalidPayloadLength { 205 | try { 206 | logger.trace(">>> void checkPayloadLength(long expectedLength, long payloadLength, long dataLength)"); 207 | 208 | if (payloadLength != expectedLength) { 209 | String text = "The payload length given in the DoIP header is invalid. " + 210 | "It should be " + expectedLength + ", but it is " + payloadLength + "."; 211 | logger.warn(text); 212 | throw new InvalidPayloadLength(text); 213 | } 214 | logger.debug("\tExpected Payload Length = " + expectedLength); 215 | if (payloadLength != (dataLength - 8)) { 216 | String text = "The payload length given in the DoIP header does not match " 217 | + "to the actual length of the payload. The payload given in the DoIP header is " + payloadLength + ", " 218 | + "but the actual length of the payload is " + (dataLength - 8) + "."; 219 | logger.warn(text); 220 | throw new InvalidPayloadLength(text); 221 | } 222 | logger.debug("Payload length of DoIP message is correct."); 223 | } finally { 224 | logger.trace("<<< void checkPayloadLength(long expectedLength, long payloadLength, long dataLength)"); 225 | } 226 | } 227 | 228 | private static void checkPayloadLength_0x0004_VAM(long payloadLength, long dataLength) throws InvalidPayloadLength { 229 | try { 230 | logger.trace(">>> void checkPayloadLength_0x0004_VAM(long payloadLength, long dataLength)"); 231 | if ((payloadLength != 32) && (payloadLength != 33)) { 232 | String text = "The payload length given in the DoIP header of the vehicle announcement message (0x0004) is invalid. " 233 | + "It should be 32 or 33, but it is " + payloadLength + "."; 234 | logger.warn(text); 235 | throw new InvalidPayloadLength(text); 236 | } 237 | 238 | if (payloadLength != (dataLength - 8)) { 239 | String text = "The payload length given in the DoIP header of the vehicle announcement message (0x0004) does not match " 240 | + "to the actual length of the payload. The payload given in the DoIP header is " + payloadLength + ", " 241 | + "but the actual length of the payload is " + (dataLength - 8) + "."; 242 | logger.warn(text); 243 | throw new InvalidPayloadLength(text); 244 | } 245 | logger.debug("Payload length of the vehicle announcement message (0x0004) is correct."); 246 | } finally { 247 | logger.trace("<<< void checkPayloadLength_0x0004_VAM(long payloadLength, long dataLength)"); 248 | } 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpVehicleAnnouncementMessage.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import doip.library.exception.IllegalNullArgument; 8 | import doip.library.util.Conversion; 9 | 10 | public class DoipUdpVehicleAnnouncementMessage extends DoipUdpMessage { 11 | 12 | private static Logger logger = LogManager.getLogger(DoipUdpVehicleAnnouncementMessage.class); 13 | 14 | private byte[] vin = null; 15 | private int logicalAddress = 0; 16 | private byte[] eid = new byte[6]; 17 | private byte[] gid = new byte[6]; 18 | private int furtherActionRequired = 0; 19 | private int syncStatus = 0; 20 | 21 | public DoipUdpVehicleAnnouncementMessage(byte[] vin, int logicalAddress, byte[] eid, byte[] gid, 22 | int furtherActionRequired, int syncStatus) { 23 | String method = "public DoipUdpVehicleAnnouncementMessage(byte[] vin, int logicalAddress, byte[] eid, byte[] gid, int furtherActionRequired, int syncStatus)"; 24 | if (vin == null) throw logger.throwing(new IllegalNullArgument("vin", method)); 25 | if (eid == null) throw logger.throwing(new IllegalNullArgument("eid", method)); 26 | if (gid == null) throw logger.throwing(new IllegalNullArgument("gid", method)); 27 | 28 | this.vin = vin; 29 | this.logicalAddress = logicalAddress; 30 | this.eid = eid; 31 | this.gid = gid; 32 | this.furtherActionRequired = furtherActionRequired; 33 | this.syncStatus = syncStatus; 34 | log(Level.INFO); 35 | } 36 | 37 | public String getMessageName() { 38 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_VAM); 39 | } 40 | 41 | public static String getMessageNameOfClass() { 42 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_VAM); 43 | } 44 | 45 | public void log(Level level) { 46 | logger.log(level, "----------------------------------------"); 47 | logger.log(level, "DoIP vehicle announcement message:"); 48 | logger.log(level, " VIN = " + Conversion.byteArrayToHexString(vin)); 49 | logger.log(level, " Logical address = " + logicalAddress); 50 | logger.log(level, " EID = " + Conversion.byteArrayToHexString(eid)); 51 | logger.log(level, " GID = " + Conversion.byteArrayToHexString(gid)); 52 | logger.log(level, " Further action required = " + furtherActionRequired); 53 | if (syncStatus == -1) { 54 | logger.log(level, " Sync status not available"); 55 | } else { 56 | logger.log(level, " Sync status = " + syncStatus); 57 | } 58 | logger.log(level, "----------------------------------------"); 59 | } 60 | 61 | @Override 62 | public byte[] getMessage() { 63 | byte[] msg = new byte[41]; 64 | if (syncStatus == -1) msg = new byte[40]; 65 | msg[0] = 0x03; 66 | msg[1] = (byte) 0xFC; 67 | msg[2] = 0x00; 68 | msg[3] = 0x04; 69 | msg[4] = 0x00; 70 | msg[5] = 0x00; 71 | msg[6] = 0x00; 72 | msg[7] = 33; 73 | 74 | System.arraycopy(vin, 0, msg, 8, 17); 75 | msg[25] = (byte)(this.logicalAddress >> 8); 76 | msg[26] = (byte)(this.logicalAddress); 77 | 78 | System.arraycopy(this.eid, 0, msg, 27, 6); 79 | System.arraycopy(this.gid, 0, msg, 33, 6); 80 | msg[39] = (byte) this.furtherActionRequired; 81 | if (syncStatus != -1) msg[40] = (byte) this.syncStatus; 82 | return msg; 83 | } 84 | 85 | public byte[] getVin() { 86 | return vin; 87 | } 88 | 89 | public void setVin(byte[] vin) { 90 | this.vin = vin; 91 | } 92 | 93 | public int getLogicalAddress() { 94 | return logicalAddress; 95 | } 96 | 97 | public void setLogicalAddress(int logicalAddress) { 98 | this.logicalAddress = logicalAddress; 99 | } 100 | 101 | public byte[] getEid() { 102 | return eid; 103 | } 104 | 105 | public void setEid(byte[] eid) { 106 | this.eid = eid; 107 | } 108 | 109 | public byte[] getGid() { 110 | return gid; 111 | } 112 | 113 | public void setGid(byte[] gid) { 114 | this.gid = gid; 115 | } 116 | 117 | public int getFurtherActionRequired() { 118 | return furtherActionRequired; 119 | } 120 | 121 | public void setFurtherActionRequired(int furtherActionRequired) { 122 | this.furtherActionRequired = furtherActionRequired; 123 | } 124 | 125 | public int getSyncStatus() { 126 | return syncStatus; 127 | } 128 | 129 | public void setSyncStatus(int syncStatus) { 130 | this.syncStatus = syncStatus; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpVehicleIdentRequest.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class DoipUdpVehicleIdentRequest extends DoipUdpMessage { 8 | 9 | private static Logger logger = LogManager.getLogger(DoipUdpVehicleIdentRequest.class); 10 | 11 | public DoipUdpVehicleIdentRequest() { 12 | this.log(Level.INFO); 13 | } 14 | 15 | public void log(Level level) { 16 | logger.log(level, "----------------------------------------"); 17 | logger.log(level, "DoIP vehicle identification request."); 18 | logger.log(level, "----------------------------------------"); 19 | } 20 | 21 | public String getMessageName() { 22 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_VIR); 23 | } 24 | 25 | public static String getMessageNameOfClass() { 26 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_VIR); 27 | } 28 | 29 | @Override 30 | public byte[] getMessage() { 31 | byte[] message = new byte[] { (byte) 0xFF, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }; 32 | return message; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpVehicleIdentRequestWithEid.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.apache.logging.log4j.Level; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | 9 | import doip.library.util.Conversion; 10 | 11 | public class DoipUdpVehicleIdentRequestWithEid extends DoipUdpMessage { 12 | 13 | private static Logger logger = LogManager.getLogger(DoipUdpVehicleIdentRequestWithEid.class); 14 | 15 | private byte[] eid; 16 | 17 | public DoipUdpVehicleIdentRequestWithEid(byte[] eid) { 18 | this.eid = Arrays.copyOf(eid, 6); 19 | this.log(Level.INFO); 20 | } 21 | 22 | public void log(Level level) { 23 | logger.log(level, "----------------------------------------"); 24 | logger.log(level, "DoIP vehicle ident. request with EID"); 25 | logger.log(level, " EID = " + Conversion.byteArrayToHexString(this.eid)); 26 | logger.log(level, "----------------------------------------"); 27 | } 28 | 29 | public String getMessageName() { 30 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_VIR_EID); 31 | } 32 | 33 | public static String getMessageNameOfClass() { 34 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_VIR_EID); 35 | } 36 | 37 | @Override 38 | public byte[] getMessage() { 39 | byte[] msg = new byte[14]; 40 | msg[0] = (byte) 0xFF; 41 | msg[1] = 0x00; 42 | msg[2] = 0x00; 43 | msg[3] = 0x02; 44 | msg[4] = 0x00; 45 | msg[5] = 0x00; 46 | msg[6] = 0x00; 47 | msg[7] = 6; 48 | 49 | System.arraycopy(this.eid, 0, msg, 8, 6); 50 | return msg; 51 | } 52 | 53 | public byte[] getEid() { 54 | return eid; 55 | } 56 | 57 | public void setEid(byte[] eid) { 58 | this.eid = eid; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/DoipUdpVehicleIdentRequestWithVin.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import doip.library.util.Conversion; 8 | 9 | public class DoipUdpVehicleIdentRequestWithVin extends DoipUdpMessage { 10 | 11 | private static Logger logger = LogManager.getLogger(DoipUdpVehicleIdentRequestWithVin.class); 12 | 13 | private byte[] vin = null; 14 | 15 | public DoipUdpVehicleIdentRequestWithVin(byte[] vin) { 16 | this.vin = vin; 17 | this.log(Level.INFO); 18 | } 19 | 20 | public void log(Level level) { 21 | logger.log(level, "----------------------------------------"); 22 | logger.log(level, "DoIP vehicle ident. request with VIN:"); 23 | logger.log(level, " VIN = " + Conversion.byteArrayToHexString(this.vin)); 24 | logger.log(level, "----------------------------------------"); 25 | } 26 | 27 | public String getMessageName() { 28 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_VIR_VIN); 29 | } 30 | 31 | public static String getMessageNameOfClass() { 32 | return getPayloadTypeAsString(DoipMessage.TYPE_UDP_VIR_VIN); 33 | } 34 | 35 | @Override 36 | public byte[] getMessage() { 37 | byte[] msg = new byte[25]; 38 | msg[0] = (byte) 0xFF; 39 | msg[1] = 0x00; 40 | msg[2] = 0x00; 41 | msg[3] = 0x03; 42 | msg[4] = 0x00; 43 | msg[5] = 0x00; 44 | msg[6] = 0x00; 45 | msg[7] = 17; 46 | 47 | System.arraycopy(this.vin, 0, msg, 8, 17); 48 | return msg; 49 | } 50 | 51 | public byte[] getVin() { 52 | return vin; 53 | } 54 | 55 | public void setVin(byte[] vin) { 56 | this.vin = vin; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/UdsMessage.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | public class UdsMessage { 4 | 5 | public static final int PHYSICAL = 0; 6 | public static final int FUNCTIONAL = 1; 7 | 8 | private int sourceAdrress = 0; 9 | private int targetAddress = 0; 10 | private int targetAddressType = PHYSICAL; 11 | private byte[] message; 12 | 13 | @SuppressWarnings("unused") 14 | private UdsMessage() {} 15 | 16 | public UdsMessage(int sourceAddress, int targetAddress, int targetAddressType, byte[] message) { 17 | this.sourceAdrress = sourceAddress; 18 | this.targetAddress = targetAddress; 19 | this.targetAddressType = targetAddressType; 20 | this.message = message; 21 | } 22 | 23 | public UdsMessage(int sourceAddress, int targetAddress, byte[] message) { 24 | this.sourceAdrress = sourceAddress; 25 | this.targetAddress = targetAddress; 26 | this.targetAddressType = PHYSICAL; 27 | this.message = message; 28 | } 29 | 30 | public int getSourceAdrress() { 31 | return sourceAdrress; 32 | } 33 | 34 | public void setSourceAdrress(int sourceAdrress) { 35 | this.sourceAdrress = sourceAdrress; 36 | } 37 | 38 | public int getTargetAddress() { 39 | return targetAddress; 40 | } 41 | 42 | public void setTargetAddress(int targetAddress) { 43 | this.targetAddress = targetAddress; 44 | } 45 | 46 | public int getTargetAddressType() { 47 | return targetAddressType; 48 | } 49 | 50 | public void setTargetAddressType(int targetAddressType) { 51 | this.targetAddressType = targetAddressType; 52 | } 53 | 54 | public byte[] getMessage() { 55 | return message; 56 | } 57 | 58 | public void setMessage(byte[] message) { 59 | this.message = message; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/doip/library/message/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author marco 6 | * 7 | */ 8 | package doip.library.message; -------------------------------------------------------------------------------- /src/main/java/doip/library/net/TcpReceiver.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | import java.util.Iterator; 4 | import java.util.LinkedList; 5 | import java.util.Map; 6 | 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | import org.apache.logging.log4j.Marker; 10 | import org.apache.logging.log4j.MarkerManager; 11 | 12 | /** 13 | * Implements functions to handle events from a TCP socket. 14 | * It is implemented as a typical publisher-subscriber pattern. 15 | */ 16 | public class TcpReceiver { 17 | 18 | private static Logger logger = LogManager.getLogger(TcpReceiver.class); 19 | private static Marker enter = MarkerManager.getMarker("ENTER"); 20 | private static Marker exit = MarkerManager.getMarker("EXIT"); 21 | 22 | private LinkedList listeners = new LinkedList(); 23 | 24 | /** 25 | * This map contains key/value pairs which are used to set the 26 | * ThreadContext for Log4j. The ThreadContext will be set 27 | * at the beginning of the method "run" which will be called by 28 | * the JRE. 29 | */ 30 | private Map context = null; 31 | 32 | /** 33 | * Adds a listener to this class 34 | * @param listener The listener which shall be added 35 | */ 36 | public void addListener(TcpReceiverListener listener) { 37 | logger.trace(enter, ">>> void addListener(TcpReceiverListener listener)"); 38 | this.listeners.add(listener); 39 | logger.trace(exit, "<<< void addListener(TcpReceiverListener listener)"); 40 | } 41 | 42 | /** 43 | * Removes a listener from this class 44 | * @param listener The listener which shall be removed 45 | */ 46 | public void removeListener(TcpReceiverListener listener) { 47 | logger.trace(enter, ">>> void removeListener(TcpReceiverListener listener)"); 48 | this.listeners.remove(listener); 49 | logger.trace(exit, "<<< void removeListener(TcpReceiverListener listener)"); 50 | } 51 | 52 | /** 53 | * Getter for the field "context" 54 | * @return Returns the field "context" 55 | */ 56 | public Map getContext() { 57 | return this.context; 58 | } 59 | 60 | /** 61 | * Sets the field "context" 62 | * 63 | * @param context A map containing key-value pairs representing the context 64 | */ 65 | public void setContext(Map context) { 66 | this.context = context; 67 | } 68 | 69 | /** 70 | * Will be called when data has been received from the TCP socket. 71 | * It informs all listeners that new data has been received. 72 | * @param data The data which had been received 73 | */ 74 | public void onDataReceived(byte[] data) { 75 | logger.trace(enter, ">>> void onDataReceived(byte[] data)"); 76 | for (TcpReceiverListener listener : this.listeners) { 77 | listener.onDataReceived(data); 78 | } 79 | logger.trace(exit, "<<< void onDataReceived(byte[] data)"); 80 | } 81 | 82 | /** 83 | * Will be called when the socket will be closed. It 84 | * informs all listeners that the socket had been closed. 85 | */ 86 | public void onSocketClosed() { 87 | logger.trace(enter, ">>> public void onSocketClosed()"); 88 | Iterator iter = this.listeners.iterator(); 89 | while (iter.hasNext()) { 90 | TcpReceiverListener listener = iter.next(); 91 | listener.onSocketClosed(); 92 | } 93 | logger.trace(exit, "<<< public void onSocketClosed()"); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/TcpReceiverListener.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | /** 4 | * Interface of a listener which listens on events from the class TcpReceiver. 5 | */ 6 | public interface TcpReceiverListener { 7 | 8 | /** 9 | * Will be called when data has been received on the TCP socket 10 | * @param data The data which had been received on the TCP socket 11 | */ 12 | public void onDataReceived(byte[] data); 13 | 14 | /** 15 | * Will be called when the TCP socket had been closed 16 | */ 17 | public void onSocketClosed(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/TcpReceiverThread.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.Socket; 6 | import java.util.Arrays; 7 | import java.util.Map; 8 | 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | import org.apache.logging.log4j.Marker; 12 | import org.apache.logging.log4j.MarkerManager; 13 | import org.apache.logging.log4j.ThreadContext; 14 | 15 | import doip.library.util.Conversion; 16 | import doip.library.util.Helper; 17 | 18 | /** 19 | * This class is a thread which will listen on a socket for incoming data. When 20 | * new data had been received this class will inform its listeners that new data 21 | * had been received. This class does not create a socket. The socket must be 22 | * given by the function "start(Socket socket)". 23 | * 24 | * This class does not contain any DoIP specific implementation. 25 | * 26 | * @author Marco Wehnert 27 | * 28 | */ 29 | public class TcpReceiverThread extends TcpReceiver implements Runnable { 30 | 31 | /** Log4j logger */ 32 | private static Logger logger = LogManager.getLogger(TcpReceiverThread.class); 33 | 34 | /** Log4j marker for function entry */ 35 | private static Marker enter = MarkerManager.getMarker("ENTER"); 36 | 37 | /** Log4j marker for function exit */ 38 | private static Marker exit = MarkerManager.getMarker("EXIT'"); 39 | 40 | /** This is the thread which will be started. */ 41 | private volatile Thread thread = null; 42 | 43 | /** 44 | * Name of the thread. This can be any name. It will be used in Log4j 45 | * when the thread name will be logged in the log file. 46 | */ 47 | private String threadName = null; 48 | 49 | /** The socket on which the thread will listen for incoming data. */ 50 | private volatile Socket socket = null; 51 | 52 | /** Maximum number of bytes which will be logged for messages. */ 53 | private int maxByteArraySizeLogging = 64; 54 | 55 | /** 56 | * Constructor with the thread name. 57 | * 58 | * @param threadName Thread name which will be logged in the log file. 59 | * @param maxByteArraySizeLogging @see maxByteArraySizeLogging 60 | */ 61 | public TcpReceiverThread(String threadName, int maxByteArraySizeLogging) { 62 | this.threadName = threadName; 63 | this.maxByteArraySizeLogging = maxByteArraySizeLogging; 64 | } 65 | 66 | /** 67 | * Returns true if the thread is still running 68 | * 69 | * @return 70 | */ 71 | public synchronized boolean isAlive() { 72 | logger.trace(">>> boolean isAlive()"); 73 | if (this.thread == null) 74 | return false; 75 | boolean isAlive = this.thread.isAlive(); 76 | logger.trace("<<< boolean isAlive()"); 77 | return isAlive; 78 | } 79 | 80 | /** 81 | * Starts the thread. 82 | * 83 | * @param socket Socket on which the thread will listen for new incoming data. 84 | */ 85 | public void start(Socket socket) { 86 | logger.trace(">>> void start()"); 87 | this.socket = socket; 88 | this.thread = new Thread(this, this.threadName); 89 | this.thread.start(); 90 | logger.trace("<<< void start()"); 91 | } 92 | 93 | /** 94 | * Stops the thread. That also includes that the socket will be closed. That is 95 | * the only way to terminate the blocking function call to read data from the 96 | * socket. 97 | */ 98 | public void stop() { 99 | logger.trace(">>> void stop()"); 100 | try { 101 | logger.debug("Close socket"); 102 | if (this.socket != null) { 103 | this.socket.close(); 104 | } 105 | } catch (IOException e) { 106 | logger.error(Helper.getExceptionAsString(e)); 107 | } 108 | // try { 109 | // logger.debug("Wait that thread has finished"); 110 | // // For some reason this join blocks sometimes 111 | // // TODO: analyze it 112 | // //this.thread.join(); 113 | // } catch (InterruptedException e) { 114 | // logger.error(Helper.getExceptionAsString(e)); 115 | // } 116 | this.thread = null; 117 | logger.trace("<<< void stop()"); 118 | } 119 | 120 | /** 121 | * This is the function which is running in the thread. It calls the function 122 | * "read(byte[] data)" at the input stream of the socket. 123 | */ 124 | @Override 125 | public void run() { 126 | Map context = getContext(); 127 | if (context != null) { 128 | for (Map.Entry entry : context.entrySet()) { 129 | ThreadContext.put(entry.getKey(), entry.getValue()); 130 | } 131 | } 132 | 133 | logger.trace(enter, ">>> void run()"); 134 | 135 | try { 136 | byte[] data = new byte[0x10000]; 137 | InputStream inputStream = this.socket.getInputStream(); 138 | for (;;) { 139 | logger.debug("Read data from socket input stream (blocking read) ..."); 140 | int count = 0; 141 | count = inputStream.read(data); 142 | 143 | if (count <= 0) { 144 | if (this.socket.isConnected()) 145 | this.socket.close(); 146 | break; 147 | } else { 148 | byte[] receivedData = Arrays.copyOf(data, count); 149 | 150 | logger.info("TCP-RECV: Remote = " + this.socket.getInetAddress().getHostAddress() + ":" 151 | + this.socket.getPort() + ", Length = " + receivedData.length + ", Data = " + Conversion 152 | .byteArrayToHexStringShortDotted(receivedData, this.maxByteArraySizeLogging)); 153 | 154 | this.onDataReceived(receivedData); 155 | } 156 | } 157 | logger.debug("No more data to receive. Thread will terminate."); 158 | 159 | } catch (IOException e) { 160 | logger.info("An IOException occured while reading data from the socket."); 161 | logger.info("This might be because"); 162 | logger.info(" - the TCP connection has been closed by the remote host,"); 163 | logger.info(" - the connection has been closed by the local host or"); 164 | logger.info(" - a TCP communication error did occur."); 165 | logger.info(Helper.getExceptionAsString(e)); 166 | } 167 | this.onSocketClosed(); 168 | logger.trace(exit, "<<< void run()"); 169 | } 170 | 171 | public Socket getSocket() { 172 | return socket; 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/TcpServer.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | import java.net.Socket; 4 | import java.util.Iterator; 5 | import java.util.LinkedList; 6 | import java.util.Map; 7 | 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | import org.apache.logging.log4j.Marker; 11 | import org.apache.logging.log4j.MarkerManager; 12 | 13 | public class TcpServer { 14 | 15 | private static Logger logger = LogManager.getLogger(TcpServer.class); 16 | private static Marker enter = MarkerManager.getMarker("ENTER"); 17 | private static Marker exit = MarkerManager.getMarker("EXIT"); 18 | 19 | private LinkedList listeners = new LinkedList(); 20 | 21 | /** 22 | * This map contains key/value pairs which are used to set the 23 | * ThreadContext for Log4j. The ThreadContext will be set 24 | * at the beginning of the method "run" which will be called by 25 | * the JRE. 26 | */ 27 | private Map context = null; 28 | 29 | /** 30 | * Getter for the field "context" 31 | * @return Returns the field "context" 32 | */ 33 | public Map getContext() { 34 | return this.context; 35 | } 36 | 37 | /** 38 | * Sets the field "context" 39 | * 40 | * @param context A map containing key-value pairs representing the context 41 | */ 42 | public void setContext(Map context) { 43 | this.context = context; 44 | } 45 | 46 | public void addListener(TcpServerListener listener) { 47 | this.logger.trace(enter, ">>> void addListener(TcpServerListener listener)"); 48 | this.listeners.add(listener); 49 | this.logger.trace(exit, "<<< void addListener(TcpServerListener listener)"); 50 | } 51 | 52 | public void removeListener(TcpServerListener listener) { 53 | this.logger.trace(enter, ">>> void removeListener(TcpServerListener listener)"); 54 | this.listeners.remove(listener); 55 | this.logger.trace(exit, "<<< void removeListener(TcpServerListener listener)"); 56 | } 57 | 58 | public void onConnectionAccepted(Socket connectionSocket) { 59 | this.logger.trace(enter, ">>> void onConnectionAccepted(Socket connectionSocket)"); 60 | Iterator iter = this.listeners.iterator(); 61 | while (iter.hasNext()) { 62 | TcpServerListener listener = iter.next(); 63 | listener.onConnectionAccepted(this, connectionSocket); 64 | } 65 | this.logger.trace(exit, "<<< void onConnectionAccepted(Socket connectionSocket)"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/TcpServerListener.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | import java.net.Socket; 4 | 5 | public interface TcpServerListener { 6 | public void onConnectionAccepted(TcpServer tcpServer, Socket socket); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/TcpServerThread.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | import java.util.Map; 7 | 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | import org.apache.logging.log4j.Marker; 11 | import org.apache.logging.log4j.MarkerManager; 12 | import org.apache.logging.log4j.ThreadContext; 13 | 14 | import doip.library.util.Helper; 15 | 16 | /** 17 | * Implements a TCP Server as a thread which is listening for 18 | * incoming connections. 19 | * 20 | * @author Marco Wehnert 21 | * 22 | */ 23 | public class TcpServerThread extends TcpServer implements Runnable { 24 | 25 | /** Log4j logger */ 26 | private static Logger logger = LogManager.getLogger(TcpServerThread.class); 27 | 28 | /** Log4j marker for function entry */ 29 | private static Marker enter = MarkerManager.getMarker("ENTER"); 30 | 31 | /** Log4j marker for function exit */ 32 | private static Marker exit = MarkerManager.getMarker("EXIT'"); 33 | 34 | private volatile Thread thread = null; 35 | 36 | private String threadName = null; 37 | 38 | private volatile ServerSocket socket = null; 39 | 40 | public TcpServerThread(String threadName) { 41 | this.threadName = threadName; 42 | } 43 | 44 | public void start(ServerSocket socket) { 45 | try { 46 | logger.trace(">>> void start(ServerSocket socket)"); 47 | this.socket = socket; 48 | this.thread = new Thread(this, this.threadName); 49 | this.thread.start(); 50 | } finally { 51 | logger.trace("<<< void start(ServerSocket socket)"); 52 | } 53 | 54 | } 55 | 56 | public void stop() { 57 | try { 58 | logger.trace(">>> void stop()"); 59 | if (this.socket != null) { 60 | try { 61 | this.socket.close(); 62 | 63 | } catch (IOException e) { 64 | logger.error("Unexpected IOException when calling socket.close()"); 65 | logger.error("Will following exception will give you any hint why this happens?"); 66 | logger.error(Helper.getExceptionAsString(e)); 67 | } 68 | } 69 | 70 | if (this.thread != null) { 71 | try { 72 | this.thread.join(); 73 | } catch (InterruptedException e) { 74 | logger.error("Unexpected InterruptedException"); 75 | logger.error("Will following exception will give you any hint why this happens?"); 76 | logger.error(Helper.getExceptionAsString(e)); 77 | } 78 | this.thread = null; 79 | } 80 | 81 | } finally { 82 | logger.trace("<<< void stop()"); 83 | } 84 | } 85 | 86 | public synchronized boolean isAlive() { 87 | logger.trace(">>> boolean isAlive()"); 88 | if (this.thread == null) 89 | return false; 90 | boolean isAlive = this.thread.isAlive(); 91 | logger.trace("<<< boolean isAlive()"); 92 | return isAlive; 93 | } 94 | 95 | /** 96 | * That is the function which is running in a separate thread. 97 | */ 98 | @Override 99 | public void run() { 100 | Map context = getContext(); 101 | if (context != null) { 102 | for (Map.Entry entry : context.entrySet()) { 103 | ThreadContext.put(entry.getKey(), entry.getValue()); 104 | } 105 | } 106 | 107 | logger.trace(enter, ">>> void run()"); 108 | try { 109 | for (;;) { 110 | Socket connectionSocket = this.socket.accept(); 111 | logger.info("TCP-CONN: New connection accepted, Remote = " + socket.getInetAddress().getHostAddress()); 112 | this.onConnectionAccepted(connectionSocket); 113 | } 114 | } catch (IOException e) { 115 | logger.debug(Helper.getExceptionAsString(e)); 116 | } 117 | logger.trace(exit, "<<< void run()"); 118 | } 119 | 120 | public ServerSocket getSocket() { 121 | return socket; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/UdpReceiver.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | import java.net.DatagramPacket; 4 | import java.net.DatagramSocket; 5 | import java.util.Iterator; 6 | import java.util.LinkedList; 7 | import java.util.Map; 8 | 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | 12 | /** 13 | * Implements the publisher for listening on a UDP socket. It informs 14 | * its listeners when a new package had been received. 15 | */ 16 | public abstract class UdpReceiver { 17 | 18 | private Logger logger = LogManager.getLogger(UdpReceiver.class); 19 | 20 | private LinkedList listeners = new LinkedList(); 21 | 22 | private Map context = null; 23 | 24 | public void setContext(Map context) { 25 | this.context = context; 26 | } 27 | 28 | /** 29 | * Adds a listener 30 | * @param listener 31 | */ 32 | public void addListener(UdpReceiverListener listener) { 33 | this.logger.trace(">>> void addListener(UdpReceiverListener listener)"); 34 | this.listeners.add(listener); 35 | this.logger.trace("<<< void addListener(UdpReceiverListener listener)"); 36 | } 37 | 38 | /** 39 | * Removes a listener 40 | * @param listener 41 | */ 42 | public void removeListener(UdpReceiverListener listener) { 43 | this.logger.trace(">>> void removeListener(UdpReceiverListener listener)"); 44 | this.listeners.remove(listener); 45 | this.logger.trace("<<< void removeListener(UdpReceiverListener listener)"); 46 | } 47 | 48 | /** 49 | * Will be called when new data has been received 50 | * @param datagramPacket 51 | */ 52 | protected void onDatagramPacketReceived(DatagramPacket datagramPacket) { 53 | this.logger.trace(">>> void onDatagramPacketReceived(DatagramPacket datagramPacket)"); 54 | Iterator iter = this.listeners.iterator(); 55 | while (iter.hasNext()) { 56 | UdpReceiverListener listener = iter.next(); 57 | listener.onDatagramPacketReceived(this, datagramPacket); 58 | } 59 | this.logger.trace("<<< void onDatagramPacketReceived(DatagramPacket datagramPacket)"); 60 | } 61 | 62 | /** 63 | * Starts receiving data from the socket 64 | * @param socket 65 | */ 66 | public abstract void start(DatagramSocket socket); 67 | 68 | /** 69 | * Stops receiving data from the socket 70 | */ 71 | public abstract void stop(); 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/UdpReceiverListener.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | import java.net.DatagramPacket; 4 | 5 | /** 6 | * Interface of a listener for a UdpReceiver 7 | */ 8 | public interface UdpReceiverListener { 9 | 10 | /** 11 | * Will be called when a new datagram packet has been received. 12 | * @param udpReceiver The receiver who has received the datagram 13 | * @param datagramPacket The datagram which had been received. 14 | */ 15 | public void onDatagramPacketReceived(UdpReceiver udpReceiver, DatagramPacket datagramPacket); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/UdpReceiverThread.java: -------------------------------------------------------------------------------- 1 | package doip.library.net; 2 | 3 | import java.io.IOException; 4 | import java.net.DatagramPacket; 5 | import java.net.DatagramSocket; 6 | import java.util.Arrays; 7 | 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | 11 | import doip.library.util.Conversion; 12 | import doip.library.util.Helper; 13 | 14 | /** 15 | * Implements a thread that listens on a UDP socket for new data which has been received. 16 | */ 17 | public class UdpReceiverThread extends UdpReceiver implements Runnable { 18 | 19 | private Logger logger = LogManager.getLogger(UdpReceiverThread.class); 20 | 21 | private volatile Thread thread = null; 22 | 23 | private String threadName = null; 24 | 25 | private volatile DatagramSocket socket = null; 26 | 27 | /** 28 | * Constructor with parameter threadName 29 | * @param threadName The name of the thread. It will be used for logging. 30 | */ 31 | public UdpReceiverThread(String threadName) { 32 | this.threadName = threadName; 33 | } 34 | 35 | @Override 36 | public void start(DatagramSocket socket) { 37 | if (logger.isTraceEnabled()) { 38 | this.logger.trace(">>> void start(DatagramSocket socket)"); 39 | } 40 | this.socket = socket; 41 | this.thread = new Thread(this, this.threadName); 42 | this.thread.start(); 43 | if (logger.isTraceEnabled()) { 44 | this.logger.trace("<<< void start(DatagramSocket socket)"); 45 | } 46 | } 47 | 48 | @Override 49 | public void stop() { 50 | if (logger.isTraceEnabled()) { 51 | this.logger.trace(">>> void stop()"); 52 | } 53 | 54 | this.socket.close(); 55 | 56 | try { 57 | this.thread.join(); 58 | } catch (InterruptedException e) { 59 | if (logger.isErrorEnabled()) { 60 | logger.error(Helper.getExceptionAsString(e)); 61 | } 62 | } 63 | 64 | this.thread = null; 65 | 66 | if (logger.isTraceEnabled()) { 67 | this.logger.trace("<<< void stop()"); 68 | } 69 | } 70 | 71 | /** 72 | * Returns true when the thread is alive 73 | * @return 74 | */ 75 | public boolean isAlive() { 76 | this.logger.trace(">>> boolean isAlive()"); 77 | if (this.thread == null) 78 | return false; 79 | boolean isAlive = this.thread.isAlive(); 80 | this.logger.trace("<<< boolean isAlive()"); 81 | return isAlive; 82 | } 83 | 84 | @Override 85 | public void run() { 86 | if (logger.isTraceEnabled()) { 87 | this.logger.trace(">>> void run()"); 88 | } 89 | try { 90 | byte[] data = new byte[0x10000]; 91 | DatagramPacket datagramPacket = new DatagramPacket(data, 0x10000); 92 | for (;;) { 93 | this.socket.receive(datagramPacket); 94 | if (logger.isInfoEnabled()) { 95 | byte[] receivedData = Arrays.copyOf(datagramPacket.getData(), datagramPacket.getLength()); 96 | logger.info("UDP-RECV: Remote = " + datagramPacket.getAddress().getHostAddress() + ":" 97 | + datagramPacket.getPort() + ", Data = " + Conversion.byteArrayToHexString(receivedData)); 98 | } 99 | this.onDatagramPacketReceived(datagramPacket); 100 | } 101 | } catch (IOException e) { 102 | if (logger.isInfoEnabled()) { 103 | logger.info(Helper.getExceptionAsString(e)); 104 | } 105 | } 106 | if (logger.isTraceEnabled()) { 107 | this.logger.trace("<<< void run()"); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/doip/library/net/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains basic implementation for network communication over TCP and UDP sockets. 3 | */ 4 | package doip.library.net; -------------------------------------------------------------------------------- /src/main/java/doip/library/properties/EmptyPropertyValue.java: -------------------------------------------------------------------------------- 1 | package doip.library.properties; 2 | 3 | /** 4 | * Exception which will be thrown when a property 5 | * is empty. It will be used by the class PropertyFile. 6 | * @author Marco Wehnert 7 | * 8 | */ 9 | @SuppressWarnings("serial") 10 | public class EmptyPropertyValue extends Exception { 11 | 12 | /** 13 | * Constructor with the key which was empty. 14 | * @param key The k ey which was empty 15 | */ 16 | public EmptyPropertyValue(String key, String filename) { 17 | super("The property \"" +key+"\" in file \"" + filename + "\" is empty"); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/doip/library/properties/MissingProperty.java: -------------------------------------------------------------------------------- 1 | package doip.library.properties; 2 | 3 | /** 4 | * Exception which will be thrown when a key is 5 | * expected to exist in a property file but it was not defined. 6 | * @author Marco Wehnert 7 | * 8 | */ 9 | @SuppressWarnings("serial") 10 | public class MissingProperty extends Exception { 11 | 12 | /** 13 | * Constructor with the key 14 | * @param key The key which was expected to exist in the property file. 15 | */ 16 | public MissingProperty(String key, String filename) { 17 | super("The property \"" +key+"\" in file \"" + filename + "\" is missing"); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/doip/library/properties/MissingSystemProperty.java: -------------------------------------------------------------------------------- 1 | package doip.library.properties; 2 | 3 | public class MissingSystemProperty extends Exception { 4 | 5 | private static final long serialVersionUID = 839224216192327460L; 6 | 7 | public MissingSystemProperty(String key) { 8 | super("The systemproperty \"" +key+"\" is missing"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/doip/library/properties/PropertyFile.java: -------------------------------------------------------------------------------- 1 | package doip.library.properties; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.IOException; 5 | import java.net.InetAddress; 6 | import java.net.UnknownHostException; 7 | import java.util.Properties; 8 | 9 | import doip.library.util.Conversion; 10 | 11 | /** 12 | * This class is a convenient class to read data from a property file. 13 | * 14 | * @author Marco Wehnert 15 | * 16 | */ 17 | public class PropertyFile { 18 | 19 | /** 20 | * Represents all the properties from a property file 21 | */ 22 | private Properties properties = null; 23 | 24 | private String filename = null; 25 | 26 | /** 27 | * Constructor with the name of the property file. 28 | * 29 | * @param filename Name of the property file 30 | * @throws IOException Will be thrown when the property file could not be 31 | * loaded. 32 | */ 33 | public PropertyFile(String filename) throws IOException { 34 | this.loadProperties(filename); 35 | } 36 | 37 | /** 38 | * Loads all the properties from a property file. 39 | * 40 | * @param filename Name of the property file. 41 | * @throws IOException Will be thrown when the file could not be read. 42 | */ 43 | public void loadProperties(String filename) throws IOException { 44 | this.filename = filename; 45 | FileInputStream fileInputStream = new FileInputStream(filename); 46 | this.properties = new Properties(); 47 | this.properties.load(fileInputStream); 48 | } 49 | 50 | /** 51 | * Returns the value of the property given by the key 52 | * 53 | * @param key The name of the property. 54 | * @return Returns the value of the property given by key. If the key does 55 | * not exist it returns null. If the key is defined but the value is 56 | * empty it returns an empty string ( = ""). 57 | */ 58 | public String getProperty(String key) { 59 | return this.properties.getProperty(key); 60 | } 61 | 62 | public String getPropertyAsString(String key, boolean mandatory) 63 | throws MissingProperty, EmptyPropertyValue { 64 | 65 | if (mandatory) { 66 | return this.getMandatoryPropertyAsString(key); 67 | } else { 68 | return this.getOptionalPropertyAsString(key); 69 | } 70 | } 71 | 72 | public boolean getPropertyAsBoolean(String key, boolean mandatory) 73 | throws MissingProperty, EmptyPropertyValue { 74 | if (mandatory) { 75 | return this.getMandatoryPropertyAsBoolean(key); 76 | } else { 77 | return this.getOptionalPropertyAsBoolean(key); 78 | } 79 | } 80 | 81 | public InetAddress getPropertyAsInetAddress(String key, boolean mandatory) 82 | throws UnknownHostException, MissingProperty, EmptyPropertyValue { 83 | if (mandatory) { 84 | return this.getMandatoryPropertyAsInetAddress(key); 85 | } else { 86 | return this.getOptionalPropertyAsInetAddress(key); 87 | } 88 | } 89 | 90 | /* 91 | public int getPropertyAsInt(String key, boolean mandatory) 92 | throws MissingProperty, EmptyPropertyValue { 93 | if (mandatory) { 94 | return this.getMandatoryPropertyAsInt(key); 95 | } else { 96 | return this.getOptionalPropertyAsInt(key); 97 | } 98 | }*/ 99 | 100 | public byte[] getPropertyAsByteArray(String key, boolean mandatory) 101 | throws MissingProperty, EmptyPropertyValue { 102 | if (mandatory) { 103 | return this.getMandatoryPropertyAsByteArray(key); 104 | } else { 105 | return this.getOptionalPropertyAsByteArray(key); 106 | } 107 | } 108 | 109 | public String getMandatoryPropertyAsString(String key) 110 | throws MissingProperty, EmptyPropertyValue { 111 | String value = this.properties.getProperty(key); 112 | if (value == null) 113 | throw new MissingProperty(key, filename); 114 | if (value == "") 115 | throw new EmptyPropertyValue(key, filename); 116 | return value; 117 | } 118 | 119 | public String getOptionalPropertyAsString(String key) 120 | throws EmptyPropertyValue { 121 | String value = this.properties.getProperty(key); 122 | if (value == null) 123 | return null; 124 | if (value == "") 125 | throw new EmptyPropertyValue(key, filename); 126 | return value; 127 | } 128 | 129 | public boolean getOptionalPropertyAsBoolean(String key) 130 | throws EmptyPropertyValue { 131 | String value = this.properties.getProperty(key); 132 | if (value == null) return false; 133 | if (value == "") throw new EmptyPropertyValue(key, filename); 134 | if (value.toLowerCase().equals("true")) return true; 135 | return false; 136 | } 137 | 138 | public InetAddress getMandatoryPropertyAsInetAddress(String key) 139 | throws MissingProperty, EmptyPropertyValue, UnknownHostException { 140 | String value = this.properties.getProperty(key); 141 | if (value == null) 142 | throw new MissingProperty(key, filename); 143 | if (value == "") 144 | throw new EmptyPropertyValue(key, filename); 145 | InetAddress address = InetAddress.getByName(value); 146 | return address; 147 | } 148 | 149 | public InetAddress getOptionalPropertyAsInetAddress(String key) 150 | throws EmptyPropertyValue, UnknownHostException { 151 | String value = this.properties.getProperty(key); 152 | if (value == null) 153 | return null; 154 | if (value == "") 155 | throw new EmptyPropertyValue(key, filename); 156 | InetAddress address = InetAddress.getByName(value); 157 | return address; 158 | } 159 | 160 | public int getMandatoryPropertyAsInt(String key) 161 | throws MissingProperty, EmptyPropertyValue { 162 | String value = this.properties.getProperty(key); 163 | if (value == null) 164 | throw new MissingProperty(key, filename); 165 | if (value == "") 166 | throw new EmptyPropertyValue(key, filename); 167 | if (value.startsWith("0x")) { 168 | return Integer.parseInt(value.substring(2), 16); 169 | } else { 170 | return Integer.parseInt(value); 171 | } 172 | } 173 | 174 | public boolean getMandatoryPropertyAsBoolean(String key) 175 | throws MissingProperty, EmptyPropertyValue { 176 | String value = this.properties.getProperty(key); 177 | if (value == null) throw new MissingProperty(key, filename); 178 | if (value == "") throw new EmptyPropertyValue(key, filename); 179 | if (value.toLowerCase().equals("true")) return true; 180 | return false; 181 | } 182 | 183 | public int getOptionalPropertyAsInt(String key, int defaultValue) throws EmptyPropertyValue { 184 | String value = this.properties.getProperty(key); 185 | if (value == null) 186 | return defaultValue; 187 | if (value == "") 188 | throw new EmptyPropertyValue(key, filename); 189 | if (value.startsWith("0x")) { 190 | return Integer.parseInt(value.substring(2), 16); 191 | } else { 192 | return Integer.parseInt(value); 193 | } 194 | } 195 | 196 | public byte[] getMandatoryPropertyAsByteArray(String key) 197 | throws MissingProperty, EmptyPropertyValue { 198 | String value = this.properties.getProperty(key); 199 | if (value == null) 200 | throw new MissingProperty(key, filename); 201 | if (value == "") 202 | throw new EmptyPropertyValue(key, filename); 203 | value = value.replace("0x", ""); 204 | value = value.replace(" ", ""); 205 | return Conversion.hexStringToByteArray(value); 206 | } 207 | 208 | public byte[] getOptionalPropertyAsByteArray(String key) 209 | throws EmptyPropertyValue { 210 | String value = this.properties.getProperty(key); 211 | if (value == null) 212 | return null; 213 | if (value == "") 214 | throw new EmptyPropertyValue(key, filename); 215 | value = value.replace("0x", ""); 216 | value = value.replace(" ", ""); 217 | return Conversion.hexStringToByteArray(value); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/main/java/doip/library/properties/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author marco 6 | * 7 | */ 8 | package doip.library.properties; -------------------------------------------------------------------------------- /src/main/java/doip/library/timer/NanoTimer.java: -------------------------------------------------------------------------------- 1 | package doip.library.timer; 2 | 3 | public class NanoTimer { 4 | 5 | private long start = 0; 6 | 7 | public NanoTimer() { 8 | this.start = System.nanoTime(); 9 | } 10 | 11 | public void reset() { 12 | start = System.nanoTime(); 13 | } 14 | 15 | public long getElapsedTime() { 16 | long current = System.nanoTime(); 17 | return current - start; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/doip/library/timer/Timer.java: -------------------------------------------------------------------------------- 1 | package doip.library.timer; 2 | 3 | import java.util.LinkedList; 4 | 5 | /** 6 | * Implements the publisher for a timer which fires cyclic events. 7 | */ 8 | public abstract class Timer { 9 | 10 | private LinkedList listeners = new LinkedList(); 11 | 12 | public synchronized void addListener(TimerListener listener) { 13 | this.listeners.add(listener); 14 | } 15 | 16 | public synchronized void removeListener(TimerListener listener) { 17 | this.listeners.remove(listener); 18 | } 19 | 20 | public void onTimerExpired() { 21 | for (TimerListener listener : this.listeners) { 22 | listener.onTimerExpired(this); 23 | } 24 | } 25 | 26 | public abstract void start(long cycleTime, int numberOfCycles); 27 | 28 | public abstract void stop(); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/doip/library/timer/TimerListener.java: -------------------------------------------------------------------------------- 1 | package doip.library.timer; 2 | 3 | /** 4 | * Interface of subscriber for Timer 5 | */ 6 | public interface TimerListener { 7 | 8 | /** 9 | * Will be called when the timer expired 10 | * @param timer The timer which caused this event 11 | */ 12 | public void onTimerExpired(Timer timer); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/doip/library/timer/TimerThread.java: -------------------------------------------------------------------------------- 1 | package doip.library.timer; 2 | 3 | public class TimerThread extends Timer implements Runnable { 4 | 5 | private volatile Thread thread = null; 6 | private volatile long cycleTime = 1; // cycle time = 1 ms 7 | 8 | private volatile int precision = 10000; 9 | 10 | private volatile boolean runFlag = false; 11 | 12 | private volatile boolean resetFlag = false; 13 | 14 | /** 15 | * The number of cycles the time shall perform, for example if 16 | * number of cycles is 3 the timer will call three times 17 | * onTimerExpired(). If number of cycles is 0 then timer is 18 | * running endless. 19 | */ 20 | private volatile int numberOfCycles = 0; 21 | 22 | public void setPrecision(int precision) { 23 | this.precision = precision; 24 | } 25 | 26 | public void start(long cycleTime, int numberOfCycles) { 27 | this.cycleTime = cycleTime; 28 | this.numberOfCycles = numberOfCycles; 29 | thread = new Thread(this); 30 | this.runFlag = true; 31 | thread.start(); 32 | } 33 | 34 | public void stop() { 35 | this.runFlag = false; 36 | this.thread.interrupt(); 37 | try { 38 | this.thread.join(); 39 | } catch (InterruptedException e) { 40 | } 41 | } 42 | 43 | public boolean isAlive() { 44 | return this.thread.isAlive(); 45 | } 46 | 47 | public void reset() { 48 | this.resetFlag = true; 49 | } 50 | 51 | @Override 52 | public void run() { 53 | int count = 0; 54 | long nextTime = System.nanoTime() + (cycleTime * 1000000); 55 | while (runFlag) { 56 | long currentTime = System.nanoTime(); 57 | if (nextTime < currentTime) { 58 | // Timer expired 59 | nextTime += (cycleTime * 1000000); 60 | this.onTimerExpired(); 61 | count ++; 62 | if (numberOfCycles > 0) { 63 | if (count >= numberOfCycles) { 64 | runFlag = false; 65 | } 66 | } 67 | } 68 | if (resetFlag == true) { 69 | nextTime = System.nanoTime() + (cycleTime * 1000000); 70 | count = 0; 71 | resetFlag = false; 72 | } 73 | try { 74 | Thread.sleep(0, precision); 75 | } catch (InterruptedException e) { 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/Conversion.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.nio.charset.StandardCharsets; 5 | import java.util.Arrays; 6 | 7 | import doip.library.exception.UnsupportedEncoding; 8 | 9 | /** 10 | * This class converts data from one type to another type, e.g. a byte array to 11 | * an ascii string. 12 | * 13 | * @author Marco Wehnert 14 | * 15 | */ 16 | public class Conversion { 17 | 18 | /** 19 | * char array which will be used for conversion of data. 20 | */ 21 | private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); 22 | 23 | public static byte[] hexStringToByteArray(String s) { 24 | s = s.replaceAll("0x" , ""); 25 | 26 | // Following section can be optimized by using char[] 27 | s = s.replaceAll(" ", ""); 28 | s = s.replaceAll(",", ""); 29 | s = s.replaceAll("\\.", ""); 30 | s = s.replaceAll(";", ""); 31 | //----------------------------------- 32 | 33 | int len = s.length(); 34 | byte[] data = new byte[len / 2]; 35 | for (int i = 0; i < len; i += 2) { 36 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 37 | + Character.digit(s.charAt(i + 1), 16)); 38 | } 39 | return data; 40 | } 41 | 42 | /** 43 | * Converts a byte array to an hex string. 44 | * @param bytes The byte array which shall be converted 45 | * @return The hex string 46 | */ 47 | public static String byteArrayToHexString(byte[] bytes) { 48 | return byteArrayToHexStringShort(bytes, bytes.length); 49 | } 50 | 51 | /** 52 | * Converts a byte array to a hex string but it will 53 | * take only 'count' bytes. If the byte array is smaller than 54 | * 'count' the byte array will be converted as it is. 55 | * @param bytes The byte array which shall be converted 56 | * @param count The maximum number of bytes which shall be converted 57 | * @return The hex string 58 | */ 59 | public static String byteArrayToHexStringShort(byte[] bytes, int count) { 60 | if (bytes.length == 0) return ""; 61 | if (count == 0) return ""; 62 | if (bytes.length < count) count = bytes.length; 63 | 64 | // Don't call byteArrayToHexString because that will require 65 | // to create a new byte array with reduced size and this can be 66 | // time consuming 67 | char[] hexChars = new char[count * 3 - 1]; 68 | for (int j = 0; j < count; j++) { 69 | int v = bytes[j] & 0xFF; 70 | hexChars[j * 3] = hexArray[v >>> 4]; 71 | hexChars[j * 3 + 1] = hexArray[v & 0x0F]; 72 | if (j < count - 1) { 73 | hexChars[j * 3 + 2] = ' '; 74 | } 75 | } 76 | return new String(hexChars); 77 | } 78 | 79 | public static String byteArrayToHexStringShortDotted(byte[] bytes, int count) { 80 | if (bytes.length <= count) { 81 | return byteArrayToHexString(bytes); 82 | } else { 83 | return byteArrayToHexStringShort(bytes, count) + "..."; 84 | } 85 | } 86 | 87 | public static String byteArrayToAsciiString(byte[] bytes) throws UnsupportedEncoding { 88 | String string; 89 | try { 90 | string = new String(bytes, "US-ASCII"); 91 | } catch (UnsupportedEncodingException e) { 92 | throw new UnsupportedEncoding(e); 93 | } 94 | return string; 95 | } 96 | 97 | public static byte[] asciiStringToByteArray(String text) { 98 | return text.getBytes(StandardCharsets.ISO_8859_1); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/Helper.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.InetSocketAddress; 6 | import java.net.MulticastSocket; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | import org.apache.logging.log4j.Marker; 13 | import org.apache.logging.log4j.MarkerManager; 14 | 15 | public class Helper { 16 | 17 | static Logger logger =LogManager.getLogger(Helper.class); 18 | 19 | static Marker markerUml = MarkerManager.getMarker("UML"); 20 | 21 | /** 22 | * Returns the path of am file. The returned string has always a '/' at the end. 23 | * 24 | * @param file The file from which the path shall be determined 25 | * @return The path of the file 26 | */ 27 | public static String getPathOfFile(String file) { 28 | if (file == null) 29 | return "./"; 30 | if (file.length() == 0) 31 | return "./"; 32 | if (file.contains("/") == false) { 33 | return "./"; 34 | } 35 | return file.substring(0, file.lastIndexOf("/") + 1); 36 | } 37 | 38 | /** 39 | * Concatenates two byte arrays 40 | * 41 | * @param a First byte array 42 | * @param b Second byte array 43 | * @return Concatenated byte array 44 | */ 45 | /* 46 | static public byte[] concat(byte[] a, byte[] b) { 47 | int lenA = a.length; 48 | int lenB = b.length; 49 | byte[] both = new byte[lenA + lenB]; 50 | System.arraycopy(a, 0, both, 0, lenA); 51 | System.arraycopy(b, 0, both, lenA, lenB); 52 | return both; 53 | }*/ 54 | 55 | static public byte[] concat(byte[]... args) { 56 | int totalLength = 0; 57 | for (int i = 0; i < args.length; i++) { 58 | totalLength += args[i].length; 59 | } 60 | byte[] result = new byte[totalLength]; 61 | 62 | int destPosOfNextArray = 0; 63 | 64 | for (int i = 0; i< args.length; i++) { 65 | System.arraycopy(args[i], 0, result, destPosOfNextArray, args[i].length); 66 | destPosOfNextArray += args[i].length; 67 | } 68 | 69 | return result; 70 | } 71 | 72 | /** 73 | * Returns an exception including its stack trace as string. 74 | * @param e The exception 75 | * @return The exception as string 76 | */ 77 | public static String getExceptionAsString(Throwable e) { 78 | StringBuilder s = new StringBuilder(4096); 79 | String message = e.getMessage(); 80 | if (message != null) { 81 | s.append(message); 82 | } 83 | s.append("\n"); 84 | s.append(e.getClass().getName()); 85 | s.append("\n"); 86 | StackTraceElement[] elements = e.getStackTrace(); 87 | for (StackTraceElement element : elements) { 88 | s.append(" "); 89 | s.append(element); 90 | s.append("\n"); 91 | } 92 | return s.toString(); 93 | } 94 | 95 | public static MulticastSocket createUdpSocket(InetAddress localAddress, int localPort, InetAddress multicast) 96 | throws IOException { 97 | MulticastSocket socket = null; 98 | if (localAddress == null) { 99 | if (localPort == 0) { 100 | socket = new MulticastSocket(); 101 | } else { 102 | socket = new MulticastSocket(localPort); 103 | } 104 | } else { 105 | InetSocketAddress socketAddress = new InetSocketAddress(localAddress, localPort); 106 | socket = new MulticastSocket(socketAddress); 107 | } 108 | 109 | if (multicast != null) { 110 | socket.joinGroup(multicast); 111 | } 112 | 113 | return socket; 114 | 115 | } 116 | 117 | public static ServerSocket createTcpServerSocket(InetAddress localAddress, int localPort) throws IOException { 118 | ServerSocket socket = null; 119 | if (localAddress == null) { 120 | socket = new ServerSocket(localPort); 121 | } else { 122 | socket = new ServerSocket(localPort, 256, localAddress); 123 | } 124 | return socket; 125 | } 126 | 127 | public static Socket createTcpClientSocket(InetAddress remoteAddress, int remotePort) throws IOException { 128 | Socket socket = null; 129 | socket = new Socket(remoteAddress, remotePort); 130 | return socket; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/LookupEntry.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import java.util.LinkedList; 4 | 5 | public class LookupEntry { 6 | 7 | private String regex; 8 | 9 | private String result; 10 | 11 | private LinkedList modifiers = new LinkedList(); 12 | 13 | public LookupEntry(String pattern) { 14 | this.setUp(pattern); 15 | } 16 | 17 | public LookupEntry(String regex, String result) { 18 | this.regex = regex; 19 | this.result = result; 20 | } 21 | 22 | /** 23 | * Copy coonstructor which creates a deep copy of the argument source 24 | * @param source 25 | */ 26 | public LookupEntry(LookupEntry source) { 27 | this.regex = source.getRegex(); 28 | this.result = source.getResult(); 29 | for (LookupEntry modifier : source.getModifiers()) { 30 | LookupEntry copy = new LookupEntry(modifier); 31 | this.modifiers.add(copy); 32 | } 33 | } 34 | 35 | /** 36 | * Setup a lookup entry by a given string. The string shall contain two strings 37 | * separated by a colon character (=":"). All white spaces will be removed before 38 | * splitting the string. A string also can be empty (that is the different way from 39 | * standard split function in Java). In the Java split the string "1003:" will only produce 40 | * one element. The method which has been implemented here will produce two elements where 41 | * the second element it an empty string. 42 | * @param text 43 | */ 44 | public void setUp(String text) { 45 | // At first remove all white spaces. White spaces are only there to make the text more readable. 46 | text = text.replaceAll(" ", ""); 47 | text = text.replaceAll("\t", ""); 48 | 49 | // Now we need to apply a small trick. for example "10 83:".split(":") will only 50 | // return an array with one element. What we want is strings containing nothing will 51 | // also produce an element in split function. We replace ":" with " : ", so empty 52 | // elements also will produce an element in the result. 53 | 54 | text = text.replace(":", " : "); 55 | 56 | // Now we create a split 57 | String[] texts = text.split(":"); 58 | 59 | // check the number of elements produced by split, it should be two or more entries. 60 | if (texts.length < 2) { 61 | throw new IllegalArgumentException("The argument is not well formatted. It shall contain two strings which are separated by a colon character (=':'). But the he given string is " + text); 62 | } 63 | 64 | // Now remove white spaces which had been added before to implement correct behavior of our split. 65 | this.regex = texts[0].replaceAll(" ", ""); 66 | this.result = texts[1].replaceAll(" ", ""); 67 | 68 | if (texts.length > 2 ) { 69 | int numberOfModifiers = (texts.length / 2) - 1; 70 | for (int i = 0; i < numberOfModifiers; i++) { 71 | int index = (i + 1) * 2; 72 | String modifierRegex = texts[index].replaceAll(" ", ""); 73 | String modifierResult = texts[index + 1].replaceAll(" ", ""); 74 | modifiers.add(new LookupEntry(modifierRegex, modifierResult)); 75 | } 76 | } 77 | } 78 | 79 | public String getRegex() { 80 | return regex; 81 | } 82 | 83 | public void setRegex(String regex) { 84 | this.regex = regex; 85 | } 86 | 87 | public String getResult() { 88 | return result; 89 | } 90 | 91 | public void setResult(String result) { 92 | this.result = result; 93 | } 94 | 95 | public LinkedList getModifiers() { 96 | return this.modifiers; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/LookupTable.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.IOException; 6 | import java.util.Iterator; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | /** 14 | * This class holds a sequence of expression-result pairs. The expression is a 15 | * Java regular expression. With the function "find(String text)" all 16 | * expressions will be checked if the text matches to one of these expressions. 17 | * The first expression which matches the text will be returned as a result. If 18 | * none of the expression matches then null will be returned. 19 | * 20 | */ 21 | public class LookupTable { 22 | 23 | private static Logger logger = LogManager.getLogger(LookupTable.class); 24 | 25 | LinkedList lookupEntries = new LinkedList(); 26 | 27 | public LookupTable() { 28 | 29 | } 30 | 31 | public LookupTable(LookupTable source) { 32 | List sourceEntries = source.getLookupEntries(); 33 | for (LookupEntry sourceEntry : sourceEntries) { 34 | LookupEntry copy = new LookupEntry(sourceEntry); 35 | lookupEntries.add(copy); 36 | } 37 | } 38 | 39 | /** 40 | * Loads the table from a file. ATTENTION: The purpose of this table is handle 41 | * byte arrays represented by a hex string, e.g. "01 02 AB 40". For better 42 | * readability it shall be allowed to add spaces between each byte. The 43 | * separator between an expression and a result is the colon character ":". When 44 | * data will be read from a file all spaces within one line will be removed. 45 | * 46 | * @param filename 47 | * @throws IOException 48 | */ 49 | public void addLookupEntriesFromFile(String filename) throws IOException { 50 | BufferedReader br = new BufferedReader(new FileReader(filename)); 51 | String line = br.readLine(); 52 | while (line != null) { 53 | if (!line.startsWith("#")) { 54 | if (line.contains(":")) { 55 | lookupEntries.add(new LookupEntry(line)); 56 | } 57 | } 58 | line = br.readLine(); 59 | } 60 | br.close(); 61 | } 62 | 63 | /** 64 | * Will add lookup entries from a list of files. The files 65 | * are all located relative to the given path. The function 66 | * will just concatenate the path and the filename. 67 | * @param path Path where the files are located 68 | * @param files The files which shall be loaded 69 | * @throws IOException 70 | */ 71 | public void addLookupEntriesFromFiles(String path, String[] files) throws IOException { 72 | for (int i = 0; i < files.length; i++) { 73 | String fileWithPath = path + files[i]; 74 | this.addLookupEntriesFromFile(fileWithPath); 75 | } 76 | } 77 | 78 | /** 79 | * Searches in all lookup entries where the given text matches to the regular 80 | * expression in the lookup entry. If one entry matches then it will return the 81 | * result of the lookup entry. If no entry would match it returns null. 82 | * 83 | * @param text Text which needs to match the regular expression 84 | * @return Returns the result if a entry could be found where the text matches 85 | * the regular expression. If no entry will match it returns null. 86 | */ 87 | public String findResultAndApplyModifiers(String text) { 88 | text = text.replaceAll(" ", ""); 89 | for (LookupEntry lookupEntry : lookupEntries) { 90 | if (text.matches(lookupEntry.getRegex())) { 91 | applyModifiers(lookupEntry.getModifiers()); 92 | 93 | String result = lookupEntry.getResult(); 94 | result = resolveReferences(text, result); 95 | return result; 96 | } 97 | } 98 | 99 | return null; 100 | } 101 | 102 | /** 103 | * Similar to function "String findResult(String text)", but here the parameter 104 | * and the return value is a byte array. The argument "bytes" will be first 105 | * converted to a hex string, then call findResult(String text), and if the 106 | * return value is not null it assumes that the return value is a hex string and 107 | * converts back the hex string to a byte array. 108 | * 109 | * @param bytes 110 | * @return 111 | */ 112 | public byte[] findResultAndApplyModifiers(byte[] bytes) { 113 | String text = Conversion.byteArrayToHexString(bytes); 114 | String result = this.findResultAndApplyModifiers(text); 115 | if (result == null) 116 | return null; 117 | return Conversion.hexStringToByteArray(result); 118 | } 119 | 120 | /** 121 | * It will iterate over all modifiers and search in the lookup 122 | * table if there is a entry where the regex in the table will 123 | * match the regex in the modifier. If so, the result in the 124 | * lookup table will be changed to the result in the modifier. 125 | * @param modifiers List of modifiers which shall be applied 126 | * to the lookup table. 127 | */ 128 | public void applyModifiers(List modifiers) { 129 | for (LookupEntry modifier : modifiers) { 130 | for (LookupEntry entry : this.lookupEntries) { 131 | if (entry.getRegex().equals(modifier.getRegex())) { 132 | entry.setResult(modifier.getResult()); 133 | } 134 | } 135 | } 136 | } 137 | 138 | /** 139 | * Resolves references from a hex string to another hex string. These 140 | * references are in the response and are declared within two brackets '[' and ']'. 141 | * The response is referencing bytes from the request. for example the request is 142 | * "01 FF AB 07" and the response is "02 00 [1] AA BB" the interpretation is that 143 | * the response is 5 bytes long and byte 3 shall have the value from byte with index 1 (index begins at 0) 144 | * in the request. 145 | * So the final result is "02 00 FF AA BB" 146 | * @param request The request message as a hex string 147 | * @param response The response message as a hex string which might contain references to 148 | * bytes in the request by using brackets, for example "[1]". 149 | * @return The response hex string with resolved references. 150 | */ 151 | public String resolveReferences(String request, String response) { 152 | logger.trace(">>> public String resolveReferences(String request, String response)"); 153 | logger.debug("request = " + request); 154 | logger.debug("response = " + response); 155 | 156 | int mode = 0; 157 | 158 | char[] responseChars = response.toCharArray(); 159 | int length = responseChars.length; 160 | StringBuilder result = new StringBuilder(length); 161 | StringBuilder reference = null; 162 | 163 | int i = 0; 164 | while (i < responseChars.length) { 165 | logger.debug("Handle character " + i + " = '"+responseChars[i]+"'"); 166 | if (mode == 0) { 167 | if (responseChars[i] == '[') { 168 | logger.debug("Switch to mode 1"); 169 | mode = 1; 170 | 171 | reference = new StringBuilder(); 172 | } else { 173 | result.append(responseChars[i]); 174 | } 175 | } else { 176 | if (responseChars[i] == ']') { 177 | logger.debug("Switch to mode 0"); 178 | mode = 0; 179 | int index = Integer.parseInt(reference.toString()); 180 | logger.debug("Reference number = " + index); 181 | if ((index * 2) < request.length()) { 182 | result.append(request.charAt(index * 2)); 183 | result.append(request.charAt(index * 2 + 1)); 184 | } else { 185 | result.append("00"); 186 | } 187 | } else { 188 | reference.append(responseChars[i]); 189 | } 190 | } 191 | 192 | i++; 193 | } 194 | 195 | logger.debug("Return " + result.toString()); 196 | 197 | logger.trace("<<< public String resolveReferences(String request, String response)"); 198 | return result.toString(); 199 | } 200 | 201 | public void addEntry(LookupEntry entry) { 202 | this.lookupEntries.add(entry); 203 | } 204 | 205 | public LinkedList getLookupEntries() { 206 | return this.lookupEntries; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/PlantUmlx.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import java.util.HashMap; 4 | 5 | import org.apache.logging.log4j.*; 6 | import org.apache.logging.log4j.MarkerManager; 7 | 8 | public class PlantUmlx { 9 | 10 | public static final String LIGHT_BLUE = "#80ACEA"; 11 | 12 | public static Logger logger = LogManager.getLogger(PlantUmlx.class); 13 | 14 | public static final Marker markerUml = MarkerManager.getMarker("UML"); 15 | 16 | public static HashMap colorMap = new HashMap(); 17 | 18 | public static String getInstanceName(Object obj ) { 19 | return obj.getClass().getSimpleName() + "@" + String.format("%08x", obj.hashCode()); 20 | } 21 | 22 | public static synchronized void setColor(String context, String color) { 23 | colorMap.put(context, color); 24 | } 25 | 26 | public static synchronized void startUml(Class clazz) { 27 | logger.debug(markerUml, "@startuml {}", clazz.getSimpleName()); 28 | //logger.debug(markerUml, "skinparam dpi 150"); 29 | //logger.debug(markerUml, "autoactivate on"); 30 | } 31 | 32 | public static synchronized void endUml() { 33 | logger.debug(markerUml, "@enduml"); 34 | } 35 | 36 | public static synchronized void addSeparator(String name) { 37 | logger.debug(markerUml, "== " + name + "=="); 38 | } 39 | 40 | private static String getColor() { 41 | String color = ""; 42 | String context = ThreadContext.get("context"); 43 | if (context != null) { 44 | String tmp = colorMap.get(context); 45 | if (tmp != null) { 46 | color = tmp; 47 | } 48 | } 49 | return color; 50 | } 51 | 52 | public static synchronized void logCall(Object source, Object target, String function) { 53 | String sourceName = getInstanceName(source); 54 | String targetName = getInstanceName(target); 55 | logger.debug(markerUml, sourceName + " -> " + targetName + ": " + function); 56 | logger.debug(markerUml, "activate " + targetName + " " + getColor()); 57 | } 58 | 59 | public static synchronized void logCall(Object target, String function) { 60 | String targetName = getInstanceName(target); 61 | logger.debug(markerUml, "[-> " + targetName + ": " + function); 62 | logger.debug(markerUml, "activate " + targetName + " " + getColor()); 63 | } 64 | 65 | public static synchronized void logReturn(Object source, Object target) { 66 | String sourceName = getInstanceName(source); 67 | String targetName = getInstanceName(target); 68 | logger.debug(markerUml, sourceName + " <-- " + targetName); 69 | logger.debug(markerUml, "deactivate " + targetName); 70 | } 71 | 72 | public static synchronized void logReturn(Object target) { 73 | String targetName = getInstanceName(target); 74 | logger.debug(markerUml, "[<-- " + targetName); 75 | logger.debug(markerUml, "deactivate " + targetName); 76 | } 77 | 78 | public static synchronized void colorNote(Object obj, String note, String color) { 79 | String objName = getInstanceName(obj); 80 | logger.debug(markerUml, "note over " + objName + " " + color + " : " + note); 81 | } 82 | 83 | public static synchronized void note(Object obj, String note) { 84 | String objName = getInstanceName(obj); 85 | logger.debug(markerUml, "note over " + objName + " : " + note); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/StreamBuffer.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | /** 9 | * Implements a byte array buffer where data can be added at the end 10 | * and can be removed from the beginning. All methods are thread-safe 11 | * by declaring them with keyword "synchronized". 12 | */ 13 | public class StreamBuffer { 14 | 15 | private Logger logger = LogManager.getLogger(StreamBuffer.class); 16 | 17 | private volatile byte[] data = new byte[0]; 18 | 19 | public synchronized byte[] getData() { 20 | return data; 21 | } 22 | 23 | public synchronized int getLength() { 24 | return this.data.length; 25 | } 26 | 27 | public synchronized void append(byte[] newData) { 28 | this.logger.debug("Append: " + newData.length + " bytes: "+ Conversion.byteArrayToHexString(newData)); 29 | int newLength = this.data.length + newData.length; 30 | byte[] newBuffer = new byte[newLength]; 31 | System.arraycopy(data, 0, newBuffer, 0, this.data.length); 32 | System.arraycopy(newData, 0, newBuffer, this.data.length, newData.length); 33 | this.data = newBuffer; 34 | } 35 | 36 | public synchronized byte[] remove(int length) { 37 | this.logger.debug("Remove " + length + " bytes"); 38 | if (length >= this.data.length ) { 39 | byte[] ret = this.data; 40 | this.data = new byte[0]; 41 | return ret; 42 | } 43 | 44 | byte[] ret = Arrays.copyOf(this.data, length); 45 | int remainingLength = this.data.length - length; 46 | byte[] remaining = new byte[remainingLength]; 47 | System.arraycopy(this.data, length, remaining, 0, remainingLength); 48 | this.data = remaining; 49 | return ret; 50 | } 51 | 52 | public synchronized void clear() { 53 | this.logger.debug("Remove all bytes"); 54 | this.data = new byte[0]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/StringConstants.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | public class StringConstants { 4 | 5 | /** 6 | * String with 78 characters '-' 7 | */ 8 | public static final String SINGLE_LINE = "------------------------------------------------------------------------------"; 9 | 10 | /** 11 | * String with 78 characters '#' 12 | */ 13 | public static final String HASH_LINE = "##############################################################################"; 14 | 15 | /** 16 | * String with 78 characters '=' 17 | */ 18 | public static final String DOUBLE_LINE = "=============================================================================="; 19 | 20 | /** 21 | * String with 36 characters "-" 22 | */ 23 | public static final String SHORT_SINGLE_LINE = "------------------------------------"; 24 | 25 | /** 26 | * String with 36 characters '#' 27 | */ 28 | public static final String SHORT_HASH_LINE = "####################################"; 29 | 30 | /** 31 | * String with 36 characters '=' 32 | */ 33 | public static final String SHORT_DOUBLE_LINE = "===================================="; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/TextBuilder.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | public class TextBuilder { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/doip/library/util/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Marco Wehnert 3 | * 4 | */ 5 | package doip.library.util; -------------------------------------------------------------------------------- /src/test/java/doip/library/message/TestDoipTcpAliveCheckRequest.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import static com.starcode88.jtest.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import doip.library.util.Conversion; 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | 11 | public class TestDoipTcpAliveCheckRequest { 12 | 13 | private Logger logger = LogManager.getLogger(TestDoipTcpAliveCheckRequest.class); 14 | 15 | @Test 16 | public void test() { 17 | DoipTcpAliveCheckRequest msg = new DoipTcpAliveCheckRequest(); 18 | String hexString = Conversion.byteArrayToHexString(msg.getMessage()); 19 | logger.info("HEX string = " + hexString); 20 | assertEquals("03 FC 00 07 00 00 00 00", hexString); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/doip/library/message/TestDoipTcpAliveCheckResponse.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import static com.starcode88.jtest.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import doip.library.util.Conversion; 8 | 9 | public class TestDoipTcpAliveCheckResponse { 10 | 11 | @Test 12 | public void test() { 13 | DoipTcpAliveCheckResponse msg = new DoipTcpAliveCheckResponse(0x1234); 14 | String hexString = Conversion.byteArrayToHexString(msg.getMessage()); 15 | assertEquals("03 FC 00 08 00 00 00 02 12 34", hexString); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/doip/library/message/TestDoipTcpDiagnosticMessage.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import static com.starcode88.jtest.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import doip.library.util.Conversion; 8 | 9 | public class TestDoipTcpDiagnosticMessage { 10 | 11 | @Test 12 | public void test() { 13 | DoipTcpDiagnosticMessage msg = new DoipTcpDiagnosticMessage(0x1122, 0x3344, new byte[] {0x01, 0x02, 0x03, 0x04 }); 14 | String hexString = Conversion.byteArrayToHexString(msg.getMessage()); 15 | assertEquals("03 FC 80 01 00 00 00 08 11 22 33 44 01 02 03 04", hexString); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/doip/library/message/TestDoipTcpDiagnosticMessageNegAck.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import static com.starcode88.jtest.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import doip.library.util.Conversion; 8 | 9 | public class TestDoipTcpDiagnosticMessageNegAck { 10 | 11 | @Test 12 | public void test() { 13 | DoipTcpDiagnosticMessageNegAck msg = new DoipTcpDiagnosticMessageNegAck(0x1122, 0x3344, 0x02, new byte[0]); 14 | String hexString = Conversion.byteArrayToHexString(msg.getMessage()); 15 | assertEquals("03 FC 80 03 00 00 00 05 11 22 33 44 02", hexString); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/doip/library/message/TestDoipTcpDiagnosticMessagePosAck.java: -------------------------------------------------------------------------------- 1 | package doip.library.message; 2 | 3 | import static com.starcode88.jtest.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import doip.library.util.Conversion; 8 | 9 | public class TestDoipTcpDiagnosticMessagePosAck { 10 | 11 | @Test 12 | public void test() { 13 | DoipTcpDiagnosticMessagePosAck msg = new DoipTcpDiagnosticMessagePosAck(0x1122, 0x3344, 0x00, new byte[] { 0x10, 003 }); 14 | String hexString = Conversion.byteArrayToHexString(msg.getMessage()); 15 | assertEquals("03 FC 80 02 00 00 00 07 11 22 33 44 00 10 03", hexString); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/doip/library/properties/TestPropertyFile.java: -------------------------------------------------------------------------------- 1 | package doip.library.properties; 2 | import static com.starcode88.jtest.Assertions.*; 3 | 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | 7 | import org.junit.jupiter.api.AfterAll; 8 | import org.junit.jupiter.api.AfterEach; 9 | import org.junit.jupiter.api.BeforeAll; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.opentest4j.AssertionFailedError; 13 | 14 | import org.apache.logging.log4j.LogManager; 15 | import org.apache.logging.log4j.Logger; 16 | 17 | public class TestPropertyFile { 18 | 19 | private static Logger logger = LogManager.getLogger(TestPropertyFile.class); 20 | 21 | private PropertyFile props = null; 22 | 23 | @BeforeAll 24 | public static void setUpBeforeClass() throws Exception { 25 | } 26 | 27 | @AfterAll 28 | public static void tearDownAfterClass() throws Exception { 29 | } 30 | 31 | @BeforeEach 32 | public void setUp() throws Exception { 33 | props = new PropertyFile("src/test/resources/test.properties"); 34 | } 35 | 36 | @AfterEach 37 | public void tearDown() throws Exception { 38 | props = null; 39 | } 40 | 41 | @Test 42 | public void testLoadUnexistingFile() throws IOException { 43 | assertThrows( 44 | FileNotFoundException.class, 45 | () -> { 46 | new PropertyFile("BlackHole.properties"); 47 | }); 48 | } 49 | 50 | @Test 51 | public void testMandatoryBooleanTrue() throws MissingProperty, EmptyPropertyValue { 52 | boolean b = props.getPropertyAsBoolean("booleanTrue", true); 53 | assertEquals(b, true); 54 | } 55 | 56 | @Test 57 | public void testOptionalBoolean() throws MissingProperty, EmptyPropertyValue { 58 | boolean b = props.getPropertyAsBoolean("empty", false); 59 | assertEquals(b, false); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/doip/library/test/TestTemplate.java: -------------------------------------------------------------------------------- 1 | package doip.library.test; 2 | 3 | import org.junit.jupiter.api.AfterAll; 4 | import org.junit.jupiter.api.AfterEach; 5 | import org.junit.jupiter.api.BeforeAll; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | 12 | public class TestTemplate { 13 | 14 | private static Logger logger = LogManager.getLogger(TestTemplate.class); 15 | 16 | @BeforeAll 17 | public static void setUpBeforeClass() throws Exception { 18 | logger.info("-----------------------------------------------------------------------------"); 19 | logger.info(">>> public static void setUpBeforeClass()"); 20 | logger.info("<<< public static void setUpBeforeClass()"); 21 | logger.info("-----------------------------------------------------------------------------"); 22 | } 23 | 24 | @AfterAll 25 | public static void tearDownAfterClass() throws Exception { 26 | logger.info("-----------------------------------------------------------------------------"); 27 | logger.info(">>> public static void tearDownAfterClass()"); 28 | logger.info("<<< public static void tearDownAfterClass()"); 29 | logger.info("-----------------------------------------------------------------------------"); 30 | } 31 | 32 | @BeforeEach 33 | public void setUp() throws Exception { 34 | logger.info("-----------------------------------------------------------------------------"); 35 | logger.info(">>> public void setUp()"); 36 | logger.info("<<< public void setUp()"); 37 | logger.info("-----------------------------------------------------------------------------"); 38 | } 39 | 40 | @AfterEach 41 | public void tearDown() throws Exception { 42 | logger.info("-----------------------------------------------------------------------------"); 43 | logger.info(">>> public void tearDown()"); 44 | logger.info("<<< public void tearDown()"); 45 | logger.info("-----------------------------------------------------------------------------"); 46 | } 47 | 48 | @Test 49 | public void test() { 50 | logger.info("#############################################################################"); 51 | logger.info(">>> public void test()"); 52 | logger.info("<<< public void test()"); 53 | logger.info("#############################################################################"); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/doip/library/timer/TestTimer.java: -------------------------------------------------------------------------------- 1 | package doip.library.timer; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import com.starcode88.jtest.Assertions; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | 10 | 11 | public class TestTimer implements TimerListener { 12 | 13 | private static Logger logger = LogManager.getLogger(TestTimer.class); 14 | 15 | private volatile int count = 0; 16 | 17 | @Test 18 | public void test5() { 19 | count = 0; 20 | Timer timer = new TimerThread(); 21 | timer.addListener(this); 22 | timer.start(100, 5); 23 | sleep(800); 24 | timer.stop(); 25 | Assertions.assertEquals(5, count); 26 | } 27 | 28 | @Test 29 | public void test0() { 30 | count = 0; 31 | Timer timer = new TimerThread(); 32 | timer.addListener(this); 33 | timer.start(100, 0); 34 | sleep(810); 35 | timer.stop(); 36 | Assertions.assertTrue(count >= 8); 37 | } 38 | 39 | @Override 40 | public void onTimerExpired(Timer timer) { 41 | logger.info("onTimerExpired"); 42 | count++; 43 | } 44 | 45 | private void sleep(int millis) { 46 | try { 47 | Thread.sleep(millis); 48 | } catch (InterruptedException e) { 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/doip/library/util/TestConversion.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import static com.starcode88.jtest.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import doip.library.util.Conversion; 8 | 9 | public class TestConversion { 10 | 11 | @Test 12 | public void testHexStringoByteArray() { 13 | assertArrayEquals(new byte[0], Conversion.hexStringToByteArray("")); 14 | assertArrayEquals(new byte[] {0x00, (byte)0xA0}, Conversion.hexStringToByteArray("00 A0")); 15 | assertArrayEquals(new byte[] {0x00, (byte)0xA0}, Conversion.hexStringToByteArray("00 A0..;;,,")); 16 | } 17 | 18 | @Test 19 | public void testByteArrayToHexString() { 20 | byte[] bytes = new byte[] {0x22, (byte) 0xF1, (byte) 0x86}; 21 | assertEquals("22 F1 86", Conversion.byteArrayToHexString(bytes)); 22 | bytes = new byte[0]; 23 | assertEquals("", Conversion.byteArrayToHexString(bytes)); 24 | } 25 | 26 | @Test 27 | public void testByteArrayToHexStringShort() { 28 | byte[] bytes = new byte[] {1,2,3,4,5,6,7,8}; 29 | assertEquals("01 02 03 04", Conversion.byteArrayToHexStringShort(bytes, 4)); 30 | assertEquals("01 02 03 04 05 06 07 08", Conversion.byteArrayToHexStringShort(bytes, 10)); 31 | } 32 | 33 | @Test 34 | public void testByteArrayToHexStringShortDotted() { 35 | byte[] bytes = new byte[0]; 36 | assertEquals("", Conversion.byteArrayToHexStringShortDotted(bytes, 0)); 37 | bytes = new byte[] {1,2,3}; 38 | assertEquals("...", Conversion.byteArrayToHexStringShortDotted(bytes, 0)); 39 | bytes = new byte[] {1,2,3,4,5,6,7,8}; 40 | assertEquals("01 02 03 04...", Conversion.byteArrayToHexStringShortDotted(bytes, 4)); 41 | } 42 | 43 | @Test 44 | public void testAsciiStringToByteArray() { 45 | byte[] actuals = Conversion.asciiStringToByteArray("ABC 123"); 46 | byte[] expecteds = new byte[] {0x41, 0x42, 0x43, 0x20, 0x31, 0x32, 0x33}; 47 | assertArrayEquals(expecteds, actuals); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/doip/library/util/TestLookupEntry.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import static com.starcode88.jtest.Assertions.*; 4 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | import org.junit.jupiter.api.AfterAll; 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeAll; 12 | import org.junit.jupiter.api.BeforeEach; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import org.apache.logging.log4j.LogManager; 16 | import org.apache.logging.log4j.Logger; 17 | 18 | public class TestLookupEntry { 19 | 20 | private static Logger logger = LogManager.getLogger(TestLookupEntry.class); 21 | 22 | 23 | /** 24 | * Test the string "1083:". Expected behavior is a lookup entry where result is just an empty string. 25 | */ 26 | @Test 27 | public void test1083Colon() { 28 | LookupEntry entry = new LookupEntry("1083:"); 29 | assertEquals("1083", entry.getRegex()); 30 | assertEquals("", entry.getResult()); 31 | } 32 | 33 | @Test 34 | public void test4Items() { 35 | LookupEntry entry = new LookupEntry("10 03 : 50 03: 31010203 : 71010203"); 36 | assertEquals("1003", entry.getRegex()); 37 | assertEquals("5003", entry.getResult()); 38 | List modifiers = entry.getModifiers(); 39 | assertEquals(1, modifiers.size()); 40 | assertEquals("31010203", modifiers.get(0).getRegex()); 41 | assertEquals("71010203", modifiers.get(0).getResult()); 42 | } 43 | 44 | @Test 45 | public void testCopyConstructor() { 46 | LookupEntry entry = new LookupEntry("1003:5003"); 47 | LookupEntry copy = new LookupEntry(entry); 48 | assertNotEquals(entry, copy); 49 | } 50 | 51 | @Test 52 | public void testCopyConstructorWithModifiers() { 53 | LookupEntry entry = new LookupEntry("1003:5003"); 54 | LookupEntry modifier = new LookupEntry("11:51"); 55 | entry.getModifiers().add(modifier); 56 | LookupEntry copy = new LookupEntry(entry); 57 | LookupEntry modifier2 = new LookupEntry("22F186:62F18601"); 58 | entry.getModifiers().add(modifier2); 59 | assertEquals(1, copy.getModifiers().size()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/doip/library/util/TestLookupTable.java: -------------------------------------------------------------------------------- 1 | package doip.library.util; 2 | 3 | import static com.starcode88.jtest.Assertions.*; 4 | 5 | import java.io.IOException; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | import org.junit.jupiter.api.AfterAll; 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeAll; 12 | import org.junit.jupiter.api.BeforeEach; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import org.apache.logging.log4j.LogManager; 16 | import org.apache.logging.log4j.Logger; 17 | 18 | 19 | public class TestLookupTable { 20 | 21 | private static Logger logger = LogManager.getLogger(TestLookupTable.class); 22 | 23 | private LookupTable lookupTable = null; 24 | 25 | @BeforeAll 26 | public static void setUpBeforeClass() throws Exception { 27 | logger.info("-----------------------------------------------------------------------------"); 28 | logger.info(">>> public static void setUpBeforeClass()"); 29 | logger.info("<<< public static void setUpBeforeClass()"); 30 | logger.info("-----------------------------------------------------------------------------"); 31 | } 32 | 33 | @AfterAll 34 | public static void tearDownAfterClass() throws Exception { 35 | logger.info("-----------------------------------------------------------------------------"); 36 | logger.info(">>> public static void tearDownAfterClass()"); 37 | logger.info("<<< public static void tearDownAfterClass()"); 38 | logger.info("-----------------------------------------------------------------------------"); 39 | } 40 | 41 | @BeforeEach 42 | public void setUp() throws Exception { 43 | logger.info("-----------------------------------------------------------------------------"); 44 | logger.info(">>> public void setUp()"); 45 | 46 | this.lookupTable = new LookupTable(); 47 | 48 | logger.info("<<< public void setUp()"); 49 | logger.info("-----------------------------------------------------------------------------"); 50 | } 51 | 52 | @AfterEach 53 | public void tearDown() throws Exception { 54 | logger.info("-----------------------------------------------------------------------------"); 55 | logger.info(">>> public void tearDown()"); 56 | 57 | this.lookupTable = null; 58 | 59 | logger.info("<<< public void tearDown()"); 60 | logger.info("-----------------------------------------------------------------------------"); 61 | } 62 | 63 | @Test 64 | public void test() throws IOException { 65 | logger.info("#############################################################################"); 66 | logger.info(">>> public void test()"); 67 | 68 | this.lookupTable.addLookupEntriesFromFile("src/test/resources/LookupTableA.txt"); 69 | String result = this.lookupTable.findResultAndApplyModifiers("1003"); 70 | assertEquals("5003003201E8", result); 71 | 72 | logger.info("<<< public void test()"); 73 | logger.info("#############################################################################"); 74 | } 75 | 76 | 77 | @Test 78 | public void testModifier() { 79 | logger.info("#############################################################################"); 80 | logger.info(">>> testModifier()"); 81 | 82 | LookupTable table = new LookupTable(); 83 | LookupEntry entry = new LookupEntry("1003:5003:22F186:62F18603"); 84 | table.addEntry(entry); 85 | entry = new LookupEntry("22F186:62F18601"); 86 | table.addEntry(entry); 87 | 88 | List entries = table.getLookupEntries(); 89 | assertEquals(2, entries.size()); 90 | assertEquals("1003", entries.get(0).getRegex()); 91 | assertEquals("5003", entries.get(0).getResult()); 92 | LinkedList modifiers = entries.get(0).getModifiers(); 93 | assertEquals(1, modifiers.size()); 94 | 95 | assertEquals("22F186", entries.get(1).getRegex()); 96 | assertEquals("62F18601", entries.get(1).getResult()); 97 | 98 | table.findResultAndApplyModifiers("1003"); 99 | assertEquals("22F186", entries.get(1).getRegex()); 100 | assertEquals("62F18603", entries.get(1).getResult()); 101 | 102 | logger.info("<<< testModifier()"); 103 | logger.info("#############################################################################"); 104 | } 105 | 106 | @Test 107 | public void testReference() { 108 | logger.info("#############################################################################"); 109 | logger.info(">>> public void testReference()"); 110 | 111 | LookupTable table = new LookupTable(); 112 | LookupEntry entry = new LookupEntry("36\\w*", "76[01]"); 113 | table.addEntry(entry); 114 | String result = table.findResultAndApplyModifiers("36FF010203"); 115 | assertEquals("76FF", result); 116 | 117 | byte[] request = new byte[] { 0x36, 0x10, 0x01, 0x02, 0x03 }; 118 | byte[] response = table.findResultAndApplyModifiers(request); 119 | assertNotNull(response); 120 | byte[] expected = new byte[] { 0x76, 0x10 }; 121 | assertArrayEquals(expected, response); 122 | 123 | logger.info("<<< public void testReference()"); 124 | logger.info("#############################################################################"); 125 | } 126 | 127 | @Test 128 | public void testCopyConstructor() { 129 | LookupTable table = new LookupTable(); 130 | table.addEntry(new LookupEntry("10 03 : 50 03")); 131 | LookupTable copy = new LookupTable(table); 132 | assertEquals(1, copy.getLookupEntries().size()); 133 | table.addEntry(new LookupEntry("10 03 : 50 03")); 134 | assertEquals(1, copy.getLookupEntries().size()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/test/resources/LookupTableA.txt: -------------------------------------------------------------------------------- 1 | # Test Lookup Table A 2 | # It contains already some lines after this line with a set of white spaces. These shall be ignored. 3 | 4 | 5 | 6 | 10 01 : 50 01 00 32 01 E8 7 | 8 | 10 03 : 50 03 00 32 01 E8 9 | 10 | # Last line of file -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/test/resources/test.properties: -------------------------------------------------------------------------------- 1 | 2 | booleanTrue = true 3 | booleanFalse = false 4 | booleanEmpty = --------------------------------------------------------------------------------