├── .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 extends Object> 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 =
--------------------------------------------------------------------------------